123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- import type {
- FC,
- MouseEventHandler,
- } from 'react'
- import {
- memo,
- useCallback,
- useMemo,
- useState,
- } from 'react'
- import { useTranslation } from 'react-i18next'
- import type {
- OffsetOptions,
- Placement,
- } from '@floating-ui/react'
- import type { BlockEnum, OnSelectBlock } from '../types'
- import Tabs from './tabs'
- import { TabsEnum } from './types'
- import {
- PortalToFollowElem,
- PortalToFollowElemContent,
- PortalToFollowElemTrigger,
- } from '@/app/components/base/portal-to-follow-elem'
- import Input from '@/app/components/base/input'
- import {
- Plus02,
- } from '@/app/components/base/icons/src/vender/line/general'
- type NodeSelectorProps = {
- open?: boolean
- onOpenChange?: (open: boolean) => void
- onSelect: OnSelectBlock
- trigger?: (open: boolean) => React.ReactNode
- placement?: Placement
- offset?: OffsetOptions
- triggerStyle?: React.CSSProperties
- triggerClassName?: (open: boolean) => string
- triggerInnerClassName?: string
- popupClassName?: string
- asChild?: boolean
- availableBlocksTypes?: BlockEnum[]
- disabled?: boolean
- noBlocks?: boolean
- }
- const NodeSelector: FC<NodeSelectorProps> = ({
- open: openFromProps,
- onOpenChange,
- onSelect,
- trigger,
- placement = 'right',
- offset = 6,
- triggerClassName,
- triggerInnerClassName,
- triggerStyle,
- popupClassName,
- asChild,
- availableBlocksTypes,
- disabled,
- noBlocks = false,
- }) => {
- const { t } = useTranslation()
- const [searchText, setSearchText] = useState('')
- const [localOpen, setLocalOpen] = useState(false)
- const open = openFromProps === undefined ? localOpen : openFromProps
- const handleOpenChange = useCallback((newOpen: boolean) => {
- setLocalOpen(newOpen)
- if (!newOpen)
- setSearchText('')
- if (onOpenChange)
- onOpenChange(newOpen)
- }, [onOpenChange])
- const handleTrigger = useCallback<MouseEventHandler<HTMLDivElement>>((e) => {
- if (disabled)
- return
- e.stopPropagation()
- handleOpenChange(!open)
- }, [handleOpenChange, open, disabled])
- const handleSelect = useCallback<OnSelectBlock>((type, toolDefaultValue) => {
- handleOpenChange(false)
- onSelect(type, toolDefaultValue)
- }, [handleOpenChange, onSelect])
- const [activeTab, setActiveTab] = useState(noBlocks ? TabsEnum.Tools : TabsEnum.Blocks)
- const handleActiveTabChange = useCallback((newActiveTab: TabsEnum) => {
- setActiveTab(newActiveTab)
- }, [])
- const searchPlaceholder = useMemo(() => {
- if (activeTab === TabsEnum.Blocks)
- return t('workflow.tabs.searchBlock')
- if (activeTab === TabsEnum.Tools)
- return t('workflow.tabs.searchTool')
- return ''
- }, [activeTab, t])
- return (
- <PortalToFollowElem
- placement={placement}
- offset={offset}
- open={open}
- onOpenChange={handleOpenChange}
- >
- <PortalToFollowElemTrigger
- asChild={asChild}
- onClick={handleTrigger}
- className={triggerInnerClassName}
- >
- {
- trigger
- ? trigger(open)
- : (
- <div
- className={`
- flex items-center justify-center
- w-4 h-4 rounded-full bg-primary-600 cursor-pointer z-10
- ${triggerClassName?.(open)}
- `}
- style={triggerStyle}
- >
- <Plus02 className='w-2.5 h-2.5 text-white' />
- </div>
- )
- }
- </PortalToFollowElemTrigger>
- <PortalToFollowElemContent className='z-[1000]'>
- <div className={`rounded-lg border-[0.5px] border-gray-200 bg-white shadow-lg ${popupClassName}`}>
- <div className='px-2 pt-2' onClick={e => e.stopPropagation()}>
- <Input
- showLeftIcon
- showClearIcon
- autoFocus
- value={searchText}
- placeholder={searchPlaceholder}
- onChange={e => setSearchText(e.target.value)}
- onClear={() => setSearchText('')}
- />
- </div>
- <Tabs
- activeTab={activeTab}
- onActiveTabChange={handleActiveTabChange}
- onSelect={handleSelect}
- searchText={searchText}
- availableBlocksTypes={availableBlocksTypes}
- noBlocks={noBlocks}
- />
- </div>
- </PortalToFollowElemContent>
- </PortalToFollowElem>
- )
- }
- export default memo(NodeSelector)
|