feat: added emoji picker

This commit is contained in:
Junseo Park 2021-02-23 22:21:42 +09:00
parent cc0485e8a9
commit 6d80be3296
4 changed files with 104 additions and 6 deletions

22
app.css
View File

@ -88,4 +88,26 @@ html .dark * ::-webkit-scrollbar-track {
button {
outline: none !important;
}
.emoji-selector-button {
width: 24px;
height: 24px;
display: inline-block;
background-image: url("https://unpkg.com/emoji-datasource-twitter@5.0.1/img/twitter/sheets-256/64.png");
background-size: 5700% 5700%;
background-position: 53.5714% 62.5%;
filter: grayscale(100%);
}
.emoji-selector-button:hover {
filter: grayscale(0%);
transform: scale(1.1, 1.1);
opacity: 90%;
transition: ease-in 100ms;
cursor: pointer;
}
.emoji-mart-category-list > *, .emoji-mart-emoji > span {
cursor: pointer;
}

View File

@ -1,12 +1,67 @@
/* eslint-disable jsx-a11y/no-autofocus */
import { useRef, useState } from 'react'
import { Field } from 'formik'
import { Picker } from 'emoji-mart'
const TextArea = ({ name, placeholder }:TextAreaProps):JSX.Element => {
return <Field as='textarea' name={name} className='border border-grey-light dark:border-transparent text-black dark:bg-very-black dark:text-white w-full rounded px-4 py-3 relative min-h-3 resize-none outline-none' placeholder={placeholder} />
import useOutsideClick from '@utils/useOutsideClick'
import 'emoji-mart/css/emoji-mart.css'
const TextArea = ({ name, placeholder, theme='auto', setValue, value }:TextAreaProps):JSX.Element => {
const ref = useRef()
const [ emojiPickerHidden, setEmojiPickerHidden ] = useState(true)
useOutsideClick(ref, () => {
setEmojiPickerHidden(true)
})
return <div className='border-none h-96 text-black dark:bg-very-black dark:text-white rounded px-4 py-3 inline-block relative w-full'>
<Field as='textarea' name={name} className='dark:border-transparent text-black dark:bg-very-black dark:text-white w-full relative h-full resize-none outline-none' placeholder={placeholder} />
<div ref={ref}>
<div className='absolute bottom-12 right-10 z-30'>
{
!emojiPickerHidden && <Picker set='twitter' autoFocus enableFrequentEmojiSort theme={theme} showSkinTones={false} onSelect={(e) => {
setEmojiPickerHidden(true)
setValue(value + ((e as { native: string }).native || e.colons))
}} i18n={{
search: '검색',
notfound: '검색 결과가 없습니다.',
categories: {
search: '검색 결과',
recent: '최근 사용',
people: '사람',
nature: '자연',
foods: '음식',
activity: '활동',
places: '장소',
objects: '사물',
symbols: '기호',
flags: '국기',
custom: '커스텀'
}
}} custom={[{
name: '한국 디스코드봇 리스트',
short_names: ['koreanbots', 'kbots', 'dbkr'],
emoticons: [],
keywords: ['koreanbots', '한국 디스코드봇 리스트', '한디리', 'kbots'],
imageUrl: '/logo.png'
}]}/>
}
</div>
<div className='absolute bottom-3 right-8'>
<button className='emoji-selector-button' onClick={() => setEmojiPickerHidden(false)} />
</div>
</div>
</div>
}
interface TextAreaProps {
name: string
placeholder?: string
theme?: 'auto' | 'dark' | 'light'
value: string
setValue(value: string): void
}
export default TextArea

View File

@ -1,5 +1,5 @@
import { NextPage, NextPageContext } from 'next'
import { useState } from 'react'
import { useRef, useState } from 'react'
import { useRouter } from 'next/router'
import dynamic from 'next/dynamic'
import Link from 'next/link'
@ -9,9 +9,10 @@ import { get } from '@utils/Query'
import { cleanObject, parseCookie, redirectTo } from '@utils/Tools'
import { AddBotSubmit, AddBotSubmitSchema } from '@utils/Yup'
import { categories, library } from '@utils/Constants'
import { ResponseProps, SubmittedBot, User } from '@types'
import { ResponseProps, SubmittedBot, Theme, User } from '@types'
import { getToken } from '@utils/Csrf'
import Fetch from '@utils/Fetch'
import useOutsideClick from '@utils/useOutsideClick'
const CheckBox = dynamic(() => import('@components/Form/CheckBox'))
const Label = dynamic(() => import('@components/Form/Label'))
@ -27,7 +28,7 @@ const Container = dynamic(() => import('@components/Container'))
const Message = dynamic(() => import('@components/Message'))
const SEO = dynamic(() => import('@components/SEO'))
const AddBot:NextPage<AddBotProps> = ({ logged, user, csrfToken }) => {
const AddBot:NextPage<AddBotProps> = ({ logged, user, csrfToken, theme }) => {
const [ data, setData ] = useState<ResponseProps<SubmittedBot>>(null)
const router = useRouter()
function toLogin() {
@ -149,7 +150,7 @@ const AddBot:NextPage<AddBotProps> = ({ logged, user, csrfToken }) => {
<Input name='intro' placeholder='국내 봇을 한 곳에서.' />
</Label>
<Label For='intro' label='봇 설명' labelDesc={<> ! ( 1500)<br/> !</>} error={errors.desc && touched.desc ? errors.desc : null} required>
<TextArea name='desc' placeholder='봇에 대해 최대한 자세히 설명해주세요!' />
<TextArea name='desc' placeholder='봇에 대해 최대한 자세히 설명해주세요!' theme={theme === 'dark' ? 'dark' : 'light'} value={values.desc} setValue={(value) => setFieldValue('desc', value)} />
</Label>
<Label For='preview' label='설명 미리보기' labelDesc='다음 결과는 실제와 다를 수 있습니다'>
<Segment>
@ -178,6 +179,7 @@ interface AddBotProps {
logged: boolean
user: User
csrfToken: string
theme: Theme
}
export default AddBot

19
utils/useOutsideClick.ts Normal file
View File

@ -0,0 +1,19 @@
import { RefObject, useEffect } from 'react'
const useOutsideClick = (ref: RefObject<HTMLElement>, callback: () => void) => {
const handleClick = (e) => {
if (ref.current && !ref.current.contains(e.target)) {
callback()
}
}
useEffect(() => {
document.addEventListener('click', handleClick)
return () => {
document.removeEventListener('click', handleClick)
}
})
}
export default useOutsideClick