mirror of https://github.com/langgenius/dify.git
feat: emoji-picker
This commit is contained in:
parent
01384e634c
commit
db1e311519
|
|
@ -4,7 +4,7 @@ import { useEffect } from 'react'
|
|||
import AppCard from './AppCard'
|
||||
import NewAppCard from './NewAppCard'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import EmojiPicker from '@/app/components/base/emoji-picker'
|
||||
|
||||
const Apps = () => {
|
||||
const { apps, mutateApps } = useAppContext()
|
||||
|
||||
|
|
@ -13,12 +13,11 @@ const Apps = () => {
|
|||
}, [])
|
||||
|
||||
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'>
|
||||
{apps.map(app => (<AppCard key={app.id} app={app} />))}
|
||||
<NewAppCard />
|
||||
</nav>
|
||||
<EmojiPicker /></>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import NewAppDialog from './NewAppDialog'
|
|||
const CreateAppCard = () => {
|
||||
const { t } = useTranslation()
|
||||
const [showNewAppDialog, setShowNewAppDialog] = useState(false)
|
||||
|
||||
return (
|
||||
<a className={classNames(style.listItem, style.newItemCard)} onClick={() => setShowNewAppDialog(true)}>
|
||||
<div className={style.listItemTitle}>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import { createApp, fetchAppTemplates } from '@/service/apps'
|
|||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import AppsContext from '@/context/app-context'
|
||||
|
||||
import EmojiPicker from '@/app/components/base/emoji-picker'
|
||||
|
||||
type NewAppDialogProps = {
|
||||
show: boolean
|
||||
onClose?: () => void
|
||||
|
|
@ -31,6 +33,7 @@ const NewAppDialog = ({ show, onClose }: NewAppDialogProps) => {
|
|||
const [newAppMode, setNewAppMode] = useState<AppMode>()
|
||||
const [isWithTemplate, setIsWithTemplate] = useState(false)
|
||||
const [selectedTemplateIndex, setSelectedTemplateIndex] = useState<number>(-1)
|
||||
const [showEmojiPicker, setShowEmojiPicker] = useState(false)
|
||||
const mutateApps = useContextSelector(AppsContext, state => state.mutateApps)
|
||||
|
||||
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>
|
||||
|
||||
<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' />
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ export type AppIconProps = {
|
|||
background?: string
|
||||
className?: string
|
||||
innerIcon?: React.ReactNode
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
const AppIcon: FC<AppIconProps> = ({
|
||||
|
|
@ -17,6 +18,7 @@ const AppIcon: FC<AppIconProps> = ({
|
|||
background,
|
||||
className,
|
||||
innerIcon,
|
||||
onClick,
|
||||
}) => {
|
||||
return (
|
||||
<span
|
||||
|
|
@ -29,6 +31,7 @@ const AppIcon: FC<AppIconProps> = ({
|
|||
style={{
|
||||
background,
|
||||
}}
|
||||
onClick={onClick}
|
||||
>
|
||||
{innerIcon ? innerIcon : <>🤖</>}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {
|
|||
MagnifyingGlassIcon
|
||||
} from '@heroicons/react/24/outline'
|
||||
import React from 'react'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
|
|
@ -95,7 +96,6 @@ const EmojiSelect: FC<IEmojiSelectProps> = ({
|
|||
}) => {
|
||||
|
||||
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">
|
||||
{categories.map((category: any) => {
|
||||
return <div key={category.id} className='flex flex-col'>
|
||||
|
|
@ -122,16 +122,21 @@ const EmojiSelect: FC<IEmojiSelectProps> = ({
|
|||
}
|
||||
|
||||
interface IEmojiPickerProps {
|
||||
isModal?: boolean
|
||||
onSelect?: (emoji: string, background: string) => void
|
||||
}
|
||||
const EmojiPicker: FC<IEmojiPickerProps> = ({
|
||||
isModal = true,
|
||||
onSelect
|
||||
}) => {
|
||||
const [selectedEmoji, setSelectedEmoji] = useState('')
|
||||
const [selectBackground, setSelectBackground] = useState('')
|
||||
|
||||
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="relative w-full">
|
||||
<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)
|
||||
}} />
|
||||
<ColorSelect selectedEmoji={selectedEmoji} onSelect={color => {
|
||||
setSelectBackground(color)
|
||||
onSelect && onSelect(selectedEmoji, color)
|
||||
}} />
|
||||
<Divider className='m-0' />
|
||||
|
|
@ -154,11 +158,10 @@ const EmojiPicker: FC<IEmojiPickerProps> = ({
|
|||
OK
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal> : <>
|
||||
</>
|
||||
}
|
||||
|
||||
return <>
|
||||
<Elem />
|
||||
</>
|
||||
return <Elem />
|
||||
}
|
||||
export default EmojiPicker
|
||||
|
|
|
|||
|
|
@ -23,51 +23,51 @@ export default function Modal({
|
|||
closable = false,
|
||||
}: IModal) {
|
||||
return (
|
||||
<Transition appear show={isShow} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={onClose}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-black bg-opacity-25" />
|
||||
</Transition.Child>
|
||||
<Transition appear show={isShow} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={onClose}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-black bg-opacity-25" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
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}`}>
|
||||
{title && <Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-medium leading-6 text-gray-900"
|
||||
>
|
||||
{title}
|
||||
</Dialog.Title>}
|
||||
{description && <Dialog.Description className='text-gray-500 text-xs font-normal mt-2'>
|
||||
{description}
|
||||
</Dialog.Description>}
|
||||
{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'>
|
||||
<XMarkIcon className='w-4 h-4 text-gray-500' onClick={onClose} />
|
||||
</div>}
|
||||
{children}
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
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}`}>
|
||||
{title && <Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-medium leading-6 text-gray-900"
|
||||
>
|
||||
{title}
|
||||
</Dialog.Title>}
|
||||
{description && <Dialog.Description className='text-gray-500 text-xs font-normal mt-2'>
|
||||
{description}
|
||||
</Dialog.Description>}
|
||||
{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'>
|
||||
<XMarkIcon className='w-4 h-4 text-gray-500' onClick={onClose} />
|
||||
</div>}
|
||||
{children}
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue