| | |
| | | import Checkbox from '@/app/components/base/checkbox' |
| | | import { bindTag, createTag, fetchTagList, unBindTag } from '@/service/tag' |
| | | import { ToastContext } from '@/app/components/base/toast' |
| | | import { noop } from 'lodash-es' |
| | | |
| | | type TagSelectorProps = { |
| | | targetID: string |
| | |
| | | return tagList.filter(tag => tag.type === type && !value.includes(tag.id) && tag.name.includes(keywords)) |
| | | }, [type, tagList, value, keywords]) |
| | | |
| | | const [creating, setCreating] = useState<boolean>(false) |
| | | const [creating, setCreating] = useState<Boolean>(false) |
| | | const createNewTag = async () => { |
| | | if (!keywords) |
| | | return |
| | |
| | | setCreating(false) |
| | | onCreate() |
| | | } |
| | | catch { |
| | | catch (e: any) { |
| | | notify({ type: 'error', message: t('common.tag.failed') }) |
| | | setCreating(false) |
| | | } |
| | |
| | | await bindTag(tagIDs, targetID, type) |
| | | notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) |
| | | } |
| | | catch { |
| | | catch (e: any) { |
| | | notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') }) |
| | | } |
| | | } |
| | |
| | | await unBindTag(tagID, targetID, type) |
| | | notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) |
| | | } |
| | | catch { |
| | | catch (e: any) { |
| | | notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') }) |
| | | } |
| | | } |
| | |
| | | }) |
| | | |
| | | return ( |
| | | <div className='relative w-full rounded-lg border-[0.5px] border-components-panel-border bg-components-input-bg-hover'> |
| | | <div className='border-b-[0.5px] border-divider-regular p-2'> |
| | | <div className='relative w-full bg-white rounded-lg border-[0.5px] border-gray-200'> |
| | | <div className='p-2 border-b-[0.5px] border-black/5'> |
| | | <Input |
| | | showLeftIcon |
| | | showClearIcon |
| | |
| | | </div> |
| | | {keywords && notExisted && ( |
| | | <div className='p-1'> |
| | | <div className='flex cursor-pointer items-center gap-2 rounded-lg py-[6px] pl-3 pr-2 hover:bg-state-base-hover' onClick={createNewTag}> |
| | | <RiAddLine className='h-4 w-4 text-text-tertiary' /> |
| | | <div className='grow truncate text-sm leading-5 text-text-secondary'> |
| | | <div className='flex items-center gap-2 pl-3 py-[6px] pr-2 rounded-lg cursor-pointer hover:bg-gray-100' onClick={createNewTag}> |
| | | <RiAddLine className='h-4 w-4 text-gray-500' /> |
| | | <div className='grow text-sm text-gray-700 leading-5 truncate'> |
| | | {`${t('common.tag.create')} `} |
| | | <span className='font-medium'>{`"${keywords}"`}</span> |
| | | </div> |
| | |
| | | </div> |
| | | )} |
| | | {keywords && notExisted && filteredTagList.length > 0 && ( |
| | | <Divider className='!my-0 !h-[1px]' /> |
| | | <Divider className='!h-[1px] !my-0' /> |
| | | )} |
| | | {(filteredTagList.length > 0 || filteredSelectedTagList.length > 0) && ( |
| | | <div className='max-h-[172px] overflow-y-auto p-1'> |
| | | <div className='p-1 max-h-[172px] overflow-y-auto'> |
| | | {filteredSelectedTagList.map(tag => ( |
| | | <div |
| | | key={tag.id} |
| | | className='flex cursor-pointer items-center gap-2 rounded-lg py-[6px] pl-3 pr-2 hover:bg-state-base-hover' |
| | | className='flex items-center gap-2 pl-3 py-[6px] pr-2 rounded-lg cursor-pointer hover:bg-gray-100' |
| | | onClick={() => selectTag(tag)} |
| | | > |
| | | <Checkbox |
| | | className='shrink-0' |
| | | checked={selectedTagIDs.includes(tag.id)} |
| | | onCheck={noop} |
| | | onCheck={() => { }} |
| | | /> |
| | | <div title={tag.name} className='grow truncate text-sm leading-5 text-text-secondary'>{tag.name}</div> |
| | | <div title={tag.name} className='grow text-sm text-gray-700 leading-5 truncate'>{tag.name}</div> |
| | | </div> |
| | | ))} |
| | | {filteredTagList.map(tag => ( |
| | | <div |
| | | key={tag.id} |
| | | className='flex cursor-pointer items-center gap-2 rounded-lg py-[6px] pl-3 pr-2 hover:bg-state-base-hover' |
| | | className='flex items-center gap-2 pl-3 py-[6px] pr-2 rounded-lg cursor-pointer hover:bg-gray-100' |
| | | onClick={() => selectTag(tag)} |
| | | > |
| | | <Checkbox |
| | | className='shrink-0' |
| | | checked={selectedTagIDs.includes(tag.id)} |
| | | onCheck={noop} |
| | | onCheck={() => { }} |
| | | /> |
| | | <div title={tag.name} className='grow truncate text-sm leading-5 text-text-secondary'>{tag.name}</div> |
| | | <div title={tag.name} className='grow text-sm text-gray-700 leading-5 truncate'>{tag.name}</div> |
| | | </div> |
| | | ))} |
| | | </div> |
| | | )} |
| | | {!keywords && !filteredTagList.length && !filteredSelectedTagList.length && ( |
| | | <div className='p-1'> |
| | | <div className='flex flex-col items-center gap-1 p-3'> |
| | | <Tag03 className='h-6 w-6 text-text-quaternary' /> |
| | | <div className='text-xs leading-[14px] text-text-tertiary'>{t('common.tag.noTag')}</div> |
| | | <div className='p-3 flex flex-col items-center gap-1'> |
| | | <Tag03 className='h-6 w-6 text-gray-300' /> |
| | | <div className='text-gray-500 text-xs leading-[14px]'>{t('common.tag.noTag')}</div> |
| | | </div> |
| | | </div> |
| | | )} |
| | | <Divider className='!my-0 !h-[1px]' /> |
| | | <Divider className='!h-[1px] !my-0' /> |
| | | <div className='p-1'> |
| | | <div className='flex cursor-pointer items-center gap-2 rounded-lg py-[6px] pl-3 pr-2 hover:bg-state-base-hover' onClick={() => setShowTagManagementModal(true)}> |
| | | <Tag03 className='h-4 w-4 text-text-tertiary' /> |
| | | <div className='grow truncate text-sm leading-5 text-text-secondary'> |
| | | <div className='flex items-center gap-2 pl-3 py-[6px] pr-2 rounded-lg cursor-pointer hover:bg-gray-100' onClick={() => setShowTagManagementModal(true)}> |
| | | <Tag03 className='h-4 w-4 text-gray-500' /> |
| | | <div className='grow text-sm text-gray-700 leading-5 truncate'> |
| | | {t('common.tag.manageTags')} |
| | | </div> |
| | | </div> |
| | |
| | | }) => { |
| | | const { t } = useTranslation() |
| | | |
| | | const tagList = useTagStore(s => s.tagList) |
| | | const setTagList = useTagStore(s => s.setTagList) |
| | | |
| | | const getTagList = async () => { |
| | |
| | | |
| | | const triggerContent = useMemo(() => { |
| | | if (selectedTags?.length) |
| | | return selectedTags.filter(selectedTag => tagList.find(tag => tag.id === selectedTag.id)).map(tag => tag.name).join(', ') |
| | | return selectedTags.map(tag => tag.name).join(', ') |
| | | return '' |
| | | }, [selectedTags, tagList]) |
| | | }, [selectedTags]) |
| | | |
| | | const Trigger = () => { |
| | | return ( |
| | | <div className={cn( |
| | | 'group/tip relative flex w-full cursor-pointer items-center gap-1 rounded-md px-2 py-[7px] hover:bg-state-base-hover', |
| | | 'group/tip relative w-full flex items-center gap-1 px-2 py-[7px] rounded-md cursor-pointer hover:bg-gray-100', |
| | | )}> |
| | | <Tag01 className='h-3 w-3 shrink-0 text-components-input-text-placeholder' /> |
| | | <div className='system-sm-regular grow truncate text-start text-components-input-text-placeholder'> |
| | | <Tag01 className='shrink-0 w-3 h-3' /> |
| | | <div className='grow text-xs text-start leading-[18px] font-normal truncate'> |
| | | {!triggerContent ? t('common.tag.addTag') : triggerContent} |
| | | </div> |
| | | </div> |
| | |
| | | btnElement={<Trigger />} |
| | | btnClassName={open => |
| | | cn( |
| | | open ? '!bg-state-base-hover !text-text-secondary' : '!bg-transparent', |
| | | '!w-full !border-0 !p-0 !text-text-tertiary hover:!bg-state-base-hover hover:!text-text-secondary', |
| | | open ? '!bg-gray-100 !text-gray-700' : '!bg-transparent', |
| | | '!w-full !p-0 !border-0 !text-gray-500 hover:!bg-gray-100 hover:!text-gray-700', |
| | | ) |
| | | } |
| | | popupClassName='!w-full !ring-0' |
| | | className={'!z-20 h-fit !w-full'} |
| | | className={'!w-full h-fit !z-20'} |
| | | /> |
| | | )} |
| | | </> |