mirror of
https://github.com/koreanbots/core.git
synced 2025-12-17 06:40:24 +00:00
feat: added emoji picker
This commit is contained in:
parent
cc0485e8a9
commit
6d80be3296
22
app.css
22
app.css
@ -89,3 +89,25 @@ html .dark * ::-webkit-scrollbar-track {
|
|||||||
button {
|
button {
|
||||||
outline: none !important;
|
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;
|
||||||
|
}
|
||||||
@ -1,12 +1,67 @@
|
|||||||
|
/* eslint-disable jsx-a11y/no-autofocus */
|
||||||
|
import { useRef, useState } from 'react'
|
||||||
import { Field } from 'formik'
|
import { Field } from 'formik'
|
||||||
|
import { Picker } from 'emoji-mart'
|
||||||
|
|
||||||
const TextArea = ({ name, placeholder }:TextAreaProps):JSX.Element => {
|
import useOutsideClick from '@utils/useOutsideClick'
|
||||||
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 '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 {
|
interface TextAreaProps {
|
||||||
name: string
|
name: string
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
|
theme?: 'auto' | 'dark' | 'light'
|
||||||
|
value: string
|
||||||
|
setValue(value: string): void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TextArea
|
export default TextArea
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { NextPage, NextPageContext } from 'next'
|
import { NextPage, NextPageContext } from 'next'
|
||||||
import { useState } from 'react'
|
import { useRef, useState } from 'react'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import dynamic from 'next/dynamic'
|
import dynamic from 'next/dynamic'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
@ -9,9 +9,10 @@ import { get } from '@utils/Query'
|
|||||||
import { cleanObject, parseCookie, redirectTo } from '@utils/Tools'
|
import { cleanObject, parseCookie, redirectTo } from '@utils/Tools'
|
||||||
import { AddBotSubmit, AddBotSubmitSchema } from '@utils/Yup'
|
import { AddBotSubmit, AddBotSubmitSchema } from '@utils/Yup'
|
||||||
import { categories, library } from '@utils/Constants'
|
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 { getToken } from '@utils/Csrf'
|
||||||
import Fetch from '@utils/Fetch'
|
import Fetch from '@utils/Fetch'
|
||||||
|
import useOutsideClick from '@utils/useOutsideClick'
|
||||||
|
|
||||||
const CheckBox = dynamic(() => import('@components/Form/CheckBox'))
|
const CheckBox = dynamic(() => import('@components/Form/CheckBox'))
|
||||||
const Label = dynamic(() => import('@components/Form/Label'))
|
const Label = dynamic(() => import('@components/Form/Label'))
|
||||||
@ -27,7 +28,7 @@ const Container = dynamic(() => import('@components/Container'))
|
|||||||
const Message = dynamic(() => import('@components/Message'))
|
const Message = dynamic(() => import('@components/Message'))
|
||||||
const SEO = dynamic(() => import('@components/SEO'))
|
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 [ data, setData ] = useState<ResponseProps<SubmittedBot>>(null)
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
function toLogin() {
|
function toLogin() {
|
||||||
@ -149,7 +150,7 @@ const AddBot:NextPage<AddBotProps> = ({ logged, user, csrfToken }) => {
|
|||||||
<Input name='intro' placeholder='국내 봇을 한 곳에서.' />
|
<Input name='intro' placeholder='국내 봇을 한 곳에서.' />
|
||||||
</Label>
|
</Label>
|
||||||
<Label For='intro' label='봇 설명' labelDesc={<>봇을 자세하게 설명해주세요! (최대 1500자)<br/>마크다운을 지원합니다!</>} error={errors.desc && touched.desc ? errors.desc : null} required>
|
<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>
|
||||||
<Label For='preview' label='설명 미리보기' labelDesc='다음 결과는 실제와 다를 수 있습니다'>
|
<Label For='preview' label='설명 미리보기' labelDesc='다음 결과는 실제와 다를 수 있습니다'>
|
||||||
<Segment>
|
<Segment>
|
||||||
@ -178,6 +179,7 @@ interface AddBotProps {
|
|||||||
logged: boolean
|
logged: boolean
|
||||||
user: User
|
user: User
|
||||||
csrfToken: string
|
csrfToken: string
|
||||||
|
theme: Theme
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AddBot
|
export default AddBot
|
||||||
19
utils/useOutsideClick.ts
Normal file
19
utils/useOutsideClick.ts
Normal 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
|
||||||
Loading…
x
Reference in New Issue
Block a user