feat: emoji-picker

This commit is contained in:
crazywoola 2023-05-18 09:10:36 +08:00
parent 01384e634c
commit db1e311519
6 changed files with 70 additions and 58 deletions

View File

@ -4,7 +4,7 @@ import { useEffect } from 'react'
import AppCard from './AppCard' import AppCard from './AppCard'
import NewAppCard from './NewAppCard' import NewAppCard from './NewAppCard'
import { useAppContext } from '@/context/app-context' import { useAppContext } from '@/context/app-context'
import EmojiPicker from '@/app/components/base/emoji-picker'
const Apps = () => { const Apps = () => {
const { apps, mutateApps } = useAppContext() const { apps, mutateApps } = useAppContext()
@ -13,12 +13,11 @@ const Apps = () => {
}, []) }, [])
return ( return (
<>
<nav className='grid content-start grid-cols-1 gap-4 px-12 pt-8 sm:grid-cols-2 lg:grid-cols-4 grow shrink-0'> <nav className='grid content-start grid-cols-1 gap-4 px-12 pt-8 sm:grid-cols-2 lg:grid-cols-4 grow shrink-0'>
{apps.map(app => (<AppCard key={app.id} app={app} />))} {apps.map(app => (<AppCard key={app.id} app={app} />))}
<NewAppCard /> <NewAppCard />
</nav> </nav>
<EmojiPicker /></>
) )
} }

View File

@ -9,7 +9,6 @@ import NewAppDialog from './NewAppDialog'
const CreateAppCard = () => { const CreateAppCard = () => {
const { t } = useTranslation() const { t } = useTranslation()
const [showNewAppDialog, setShowNewAppDialog] = useState(false) const [showNewAppDialog, setShowNewAppDialog] = useState(false)
return ( return (
<a className={classNames(style.listItem, style.newItemCard)} onClick={() => setShowNewAppDialog(true)}> <a className={classNames(style.listItem, style.newItemCard)} onClick={() => setShowNewAppDialog(true)}>
<div className={style.listItemTitle}> <div className={style.listItemTitle}>

View File

@ -17,6 +17,8 @@ import { createApp, fetchAppTemplates } from '@/service/apps'
import AppIcon from '@/app/components/base/app-icon' import AppIcon from '@/app/components/base/app-icon'
import AppsContext from '@/context/app-context' import AppsContext from '@/context/app-context'
import EmojiPicker from '@/app/components/base/emoji-picker'
type NewAppDialogProps = { type NewAppDialogProps = {
show: boolean show: boolean
onClose?: () => void onClose?: () => void
@ -31,6 +33,7 @@ const NewAppDialog = ({ show, onClose }: NewAppDialogProps) => {
const [newAppMode, setNewAppMode] = useState<AppMode>() const [newAppMode, setNewAppMode] = useState<AppMode>()
const [isWithTemplate, setIsWithTemplate] = useState(false) const [isWithTemplate, setIsWithTemplate] = useState(false)
const [selectedTemplateIndex, setSelectedTemplateIndex] = useState<number>(-1) const [selectedTemplateIndex, setSelectedTemplateIndex] = useState<number>(-1)
const [showEmojiPicker, setShowEmojiPicker] = useState(false)
const mutateApps = useContextSelector(AppsContext, state => state.mutateApps) const mutateApps = useContextSelector(AppsContext, state => state.mutateApps)
const { data: templates, mutate } = useSWR({ url: '/app-templates' }, fetchAppTemplates) const { data: templates, mutate } = useSWR({ url: '/app-templates' }, fetchAppTemplates)
@ -93,10 +96,15 @@ const NewAppDialog = ({ show, onClose }: NewAppDialogProps) => {
</> </>
} }
> >
{showEmojiPicker && <EmojiPicker onSelect={(emoji, background) => {
console.log(emoji, background)
setShowEmojiPicker(false)
}} />}
<h3 className={style.newItemCaption}>{t('app.newApp.captionName')}</h3> <h3 className={style.newItemCaption}>{t('app.newApp.captionName')}</h3>
<div className='flex items-center justify-between gap-3 mb-8'> <div className='flex items-center justify-between gap-3 mb-8'>
<AppIcon size='large' /> <AppIcon size='large' onClick={() => { setShowEmojiPicker(true) }} />
<input ref={nameInputRef} className='h-10 px-3 text-sm font-normal bg-gray-100 rounded-lg grow' /> <input ref={nameInputRef} className='h-10 px-3 text-sm font-normal bg-gray-100 rounded-lg grow' />
</div> </div>

View File

@ -9,6 +9,7 @@ export type AppIconProps = {
background?: string background?: string
className?: string className?: string
innerIcon?: React.ReactNode innerIcon?: React.ReactNode
onClick?: () => void
} }
const AppIcon: FC<AppIconProps> = ({ const AppIcon: FC<AppIconProps> = ({
@ -17,6 +18,7 @@ const AppIcon: FC<AppIconProps> = ({
background, background,
className, className,
innerIcon, innerIcon,
onClick,
}) => { }) => {
return ( return (
<span <span
@ -29,6 +31,7 @@ const AppIcon: FC<AppIconProps> = ({
style={{ style={{
background, background,
}} }}
onClick={onClick}
> >
{innerIcon ? innerIcon : <>🤖</>} {innerIcon ? innerIcon : <>🤖</>}
</span> </span>

View File

@ -12,6 +12,7 @@ import {
MagnifyingGlassIcon MagnifyingGlassIcon
} from '@heroicons/react/24/outline' } from '@heroicons/react/24/outline'
import React from 'react' import React from 'react'
import Modal from '@/app/components/base/modal'
declare global { declare global {
namespace JSX { namespace JSX {
@ -95,7 +96,6 @@ const EmojiSelect: FC<IEmojiSelectProps> = ({
}) => { }) => {
const { categories, emojis } = data as any const { categories, emojis } = data as any
console.log(categories, emojis);
return <div className="w-full max-h-[200px] overflow-x-hidden overflow-y-auto px-3"> return <div className="w-full max-h-[200px] overflow-x-hidden overflow-y-auto px-3">
{categories.map((category: any) => { {categories.map((category: any) => {
return <div key={category.id} className='flex flex-col'> return <div key={category.id} className='flex flex-col'>
@ -122,16 +122,21 @@ const EmojiSelect: FC<IEmojiSelectProps> = ({
} }
interface IEmojiPickerProps { interface IEmojiPickerProps {
isModal?: boolean
onSelect?: (emoji: string, background: string) => void onSelect?: (emoji: string, background: string) => void
} }
const EmojiPicker: FC<IEmojiPickerProps> = ({ const EmojiPicker: FC<IEmojiPickerProps> = ({
isModal = true,
onSelect onSelect
}) => { }) => {
const [selectedEmoji, setSelectedEmoji] = useState('') const [selectedEmoji, setSelectedEmoji] = useState('')
const [selectBackground, setSelectBackground] = useState('')
const Elem = () => { const Elem = () => {
return <div className={cn(s.container, 'bg-white')} > return isModal ? <Modal
onClose={() => { }}
isShow
className={cn(s.container, '!w-[362px] !p-0')}
>
<div className='flex flex-col items-center w-full p-3'> <div className='flex flex-col items-center w-full p-3'>
<div className="relative w-full"> <div className="relative w-full">
<div className="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none"> <div className="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
@ -145,7 +150,6 @@ const EmojiPicker: FC<IEmojiPickerProps> = ({
setSelectedEmoji(itm) setSelectedEmoji(itm)
}} /> }} />
<ColorSelect selectedEmoji={selectedEmoji} onSelect={color => { <ColorSelect selectedEmoji={selectedEmoji} onSelect={color => {
setSelectBackground(color)
onSelect && onSelect(selectedEmoji, color) onSelect && onSelect(selectedEmoji, color)
}} /> }} />
<Divider className='m-0' /> <Divider className='m-0' />
@ -154,11 +158,10 @@ const EmojiPicker: FC<IEmojiPickerProps> = ({
OK OK
</Button> </Button>
</div> </div>
</div> </Modal> : <>
</>
} }
return <> return <Elem />
<Elem />
</>
} }
export default EmojiPicker export default EmojiPicker

View File

@ -23,51 +23,51 @@ export default function Modal({
closable = false, closable = false,
}: IModal) { }: IModal) {
return ( return (
<Transition appear show={isShow} as={Fragment}> <Transition appear show={isShow} as={Fragment}>
<Dialog as="div" className="relative z-10" onClose={onClose}> <Dialog as="div" className="relative z-10" onClose={onClose}>
<Transition.Child <Transition.Child
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-out duration-300"
enterFrom="opacity-0" enterFrom="opacity-0"
enterTo="opacity-100" enterTo="opacity-100"
leave="ease-in duration-200" leave="ease-in duration-200"
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
<div className="fixed inset-0 bg-black bg-opacity-25" /> <div className="fixed inset-0 bg-black bg-opacity-25" />
</Transition.Child> </Transition.Child>
<div className="fixed inset-0 overflow-y-auto"> <div className="fixed inset-0 overflow-y-auto">
<div className="flex min-h-full items-center justify-center p-4 text-center"> <div className="flex min-h-full items-center justify-center p-4 text-center">
<Transition.Child <Transition.Child
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-out duration-300"
enterFrom="opacity-0 scale-95" enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100" enterTo="opacity-100 scale-100"
leave="ease-in duration-200" leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100" leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95" leaveTo="opacity-0 scale-95"
> >
<Dialog.Panel className={`w-full max-w-md transform overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all ${className}`}> <Dialog.Panel className={`w-full max-w-md transform overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all ${className}`}>
{title && <Dialog.Title {title && <Dialog.Title
as="h3" as="h3"
className="text-lg font-medium leading-6 text-gray-900" className="text-lg font-medium leading-6 text-gray-900"
> >
{title} {title}
</Dialog.Title>} </Dialog.Title>}
{description && <Dialog.Description className='text-gray-500 text-xs font-normal mt-2'> {description && <Dialog.Description className='text-gray-500 text-xs font-normal mt-2'>
{description} {description}
</Dialog.Description>} </Dialog.Description>}
{closable {closable
&& <div className='absolute top-6 right-6 w-5 h-5 rounded-2xl flex items-center justify-center hover:cursor-pointer hover:bg-gray-100'> && <div className='absolute top-6 right-6 w-5 h-5 rounded-2xl flex items-center justify-center hover:cursor-pointer hover:bg-gray-100'>
<XMarkIcon className='w-4 h-4 text-gray-500' onClick={onClose} /> <XMarkIcon className='w-4 h-4 text-gray-500' onClick={onClose} />
</div>} </div>}
{children} {children}
</Dialog.Panel> </Dialog.Panel>
</Transition.Child> </Transition.Child>
</div> </div>
</div> </div>
</Dialog> </Dialog>
</Transition> </Transition>
) )
} }