mirror of
https://github.com/langgenius/dify.git
synced 2026-04-28 20:17:29 +08:00
refactor input and replace search input
This commit is contained in:
parent
9362ae045c
commit
939df16655
@ -21,7 +21,7 @@ import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
|||||||
import { CheckModal } from '@/hooks/use-pay'
|
import { CheckModal } from '@/hooks/use-pay'
|
||||||
import TabSliderNew from '@/app/components/base/tab-slider-new'
|
import TabSliderNew from '@/app/components/base/tab-slider-new'
|
||||||
import { useTabSearchParams } from '@/hooks/use-tab-searchparams'
|
import { useTabSearchParams } from '@/hooks/use-tab-searchparams'
|
||||||
import SearchInput from '@/app/components/base/search-input'
|
import Input from '@/app/components/base/input'
|
||||||
import { useStore as useTagStore } from '@/app/components/base/tag-management/store'
|
import { useStore as useTagStore } from '@/app/components/base/tag-management/store'
|
||||||
import TagManagementModal from '@/app/components/base/tag-management'
|
import TagManagementModal from '@/app/components/base/tag-management'
|
||||||
import TagFilter from '@/app/components/base/tag-management/filter'
|
import TagFilter from '@/app/components/base/tag-management/filter'
|
||||||
@ -133,7 +133,14 @@ const Apps = () => {
|
|||||||
/>
|
/>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
<TagFilter type='app' value={tagFilterValue} onChange={handleTagsChange} />
|
<TagFilter type='app' value={tagFilterValue} onChange={handleTagsChange} />
|
||||||
<SearchInput className='w-[200px]' value={keywords} onChange={handleKeywordsChange} />
|
<Input
|
||||||
|
showLeftIcon
|
||||||
|
showClearIcon
|
||||||
|
wrapperClassName='w-[200px]'
|
||||||
|
value={keywords}
|
||||||
|
onChange={e => handleKeywordsChange(e.target.value)}
|
||||||
|
onClear={() => handleKeywordsChange('')}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<nav className='grid content-start grid-cols-1 gap-4 px-12 pt-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 grow shrink-0'>
|
<nav className='grid content-start grid-cols-1 gap-4 px-12 pt-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 grow shrink-0'>
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import DatasetFooter from './DatasetFooter'
|
|||||||
import ApiServer from './ApiServer'
|
import ApiServer from './ApiServer'
|
||||||
import Doc from './Doc'
|
import Doc from './Doc'
|
||||||
import TabSliderNew from '@/app/components/base/tab-slider-new'
|
import TabSliderNew from '@/app/components/base/tab-slider-new'
|
||||||
import SearchInput from '@/app/components/base/search-input'
|
import Input from '@/app/components/base/input'
|
||||||
import TagManagementModal from '@/app/components/base/tag-management'
|
import TagManagementModal from '@/app/components/base/tag-management'
|
||||||
import TagFilter from '@/app/components/base/tag-management/filter'
|
import TagFilter from '@/app/components/base/tag-management/filter'
|
||||||
|
|
||||||
@ -79,7 +79,14 @@ const Container = () => {
|
|||||||
{activeTab === 'dataset' && (
|
{activeTab === 'dataset' && (
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
<TagFilter type='knowledge' value={tagFilterValue} onChange={handleTagsChange} />
|
<TagFilter type='knowledge' value={tagFilterValue} onChange={handleTagsChange} />
|
||||||
<SearchInput className='w-[200px]' value={keywords} onChange={handleKeywordsChange} />
|
<Input
|
||||||
|
showLeftIcon
|
||||||
|
showClearIcon
|
||||||
|
wrapperClassName='w-[200px]'
|
||||||
|
value={keywords}
|
||||||
|
onChange={e => handleKeywordsChange(e.target.value)}
|
||||||
|
onClear={() => handleKeywordsChange('')}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{activeTab === 'api' && data && <ApiServer apiBaseUrl={data.api_base_url || ''} />}
|
{activeTab === 'api' && data && <ApiServer apiBaseUrl={data.api_base_url || ''} />}
|
||||||
|
|||||||
@ -1,43 +1,84 @@
|
|||||||
'use client'
|
import type { CSSProperties } from 'react'
|
||||||
import type { SVGProps } from 'react'
|
import React from 'react'
|
||||||
import React, { useState } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { RiCloseCircleFill, RiErrorWarningLine, RiSearchLine } from '@remixicon/react'
|
||||||
|
import { type VariantProps, cva } from 'class-variance-authority'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
type InputProps = {
|
export const inputVariants = cva(
|
||||||
placeholder?: string
|
'',
|
||||||
value?: string
|
{
|
||||||
defaultValue?: string
|
variants: {
|
||||||
onChange?: (v: string) => void
|
size: {
|
||||||
className?: string
|
regular: 'px-3 radius-md system-sm-regular',
|
||||||
wrapperClassName?: string
|
large: 'px-4 radius-lg system-md-regular',
|
||||||
type?: string
|
},
|
||||||
showPrefix?: React.ReactNode
|
},
|
||||||
prefixIcon?: React.ReactNode
|
defaultVariants: {
|
||||||
}
|
size: 'regular',
|
||||||
|
},
|
||||||
const GlassIcon = ({ className }: SVGProps<SVGElement>) => (
|
},
|
||||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
|
|
||||||
<path d="M12.25 12.25L10.2084 10.2083M11.6667 6.70833C11.6667 9.44675 9.44675 11.6667 6.70833 11.6667C3.96992 11.6667 1.75 9.44675 1.75 6.70833C1.75 3.96992 3.96992 1.75 6.70833 1.75C9.44675 1.75 11.6667 3.96992 11.6667 6.70833Z" stroke="#344054" strokeWidth="1.25" strokeLinecap="round" strokeLinejoin="round" />
|
|
||||||
</svg>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const Input = ({ value, defaultValue, onChange, className = '', wrapperClassName = '', placeholder, type, showPrefix, prefixIcon }: InputProps) => {
|
export type InputProps = {
|
||||||
const [localValue, setLocalValue] = useState(value ?? defaultValue)
|
value: string
|
||||||
|
showLeftIcon?: boolean
|
||||||
|
showClearIcon?: boolean
|
||||||
|
onClear?: () => void
|
||||||
|
disabled?: boolean
|
||||||
|
destructive?: boolean
|
||||||
|
wrapperClassName?: string
|
||||||
|
styleCss?: CSSProperties
|
||||||
|
} & React.InputHTMLAttributes<HTMLInputElement> & VariantProps<typeof inputVariants>
|
||||||
|
|
||||||
|
const Input = ({
|
||||||
|
size,
|
||||||
|
disabled,
|
||||||
|
destructive,
|
||||||
|
showLeftIcon,
|
||||||
|
showClearIcon,
|
||||||
|
onClear,
|
||||||
|
wrapperClassName,
|
||||||
|
className,
|
||||||
|
styleCss,
|
||||||
|
value,
|
||||||
|
placeholder,
|
||||||
|
onChange,
|
||||||
|
...props
|
||||||
|
}: InputProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
return (
|
return (
|
||||||
<div className={`relative inline-flex w-full ${wrapperClassName}`}>
|
<div className={cn('relative w-full', wrapperClassName)}>
|
||||||
{showPrefix && <span className={'whitespace-nowrap absolute left-2 self-center'}>{prefixIcon ?? <GlassIcon className='h-3.5 w-3.5 stroke-current text-gray-700 stroke-2' />}</span>}
|
{showLeftIcon && <RiSearchLine className={cn('absolute left-2 top-1/2 -translate-y-1/2 w-4 h-4 text-components-input-text-placeholder')} />}
|
||||||
<input
|
<input
|
||||||
type={type ?? 'text'}
|
style={styleCss}
|
||||||
className={cn(`inline-flex h-7 w-full py-1 px-2 rounded-lg text-xs leading-normal bg-gray-100 caret-primary-600 hover:bg-gray-100 focus:ring-1 focus:ring-inset focus:ring-gray-200 focus-visible:outline-none focus:bg-white placeholder:text-gray-400 ${showPrefix ? '!pl-7' : ''}`, className)}
|
className={cn(
|
||||||
placeholder={placeholder ?? (showPrefix ? t('common.operation.search') ?? '' : 'please input')}
|
'w-full py-[7px] bg-components-input-bg-normal border border-transparent text-components-input-text-filled hover:bg-components-input-bg-hover hover:border-components-input-border-hover focus:bg-components-input-bg-active focus:border-components-input-border-active focus:shadow-xs placeholder:text-components-input-text-placeholder appearance-none outline-none caret-primary-600',
|
||||||
value={localValue}
|
inputVariants({ size }),
|
||||||
onChange={(e) => {
|
showLeftIcon && 'pl-[26px]',
|
||||||
setLocalValue(e.target.value)
|
showLeftIcon && size === 'large' && 'pl-7',
|
||||||
onChange && onChange(e.target.value)
|
showClearIcon && value && 'pr-[26px]',
|
||||||
}}
|
showClearIcon && value && size === 'large' && 'pr-7',
|
||||||
|
destructive && 'pr-[26px]',
|
||||||
|
destructive && size === 'large' && 'pr-7',
|
||||||
|
disabled && 'bg-components-input-bg-disabled border-transparent text-components-input-text-filled-disabled cursor-not-allowed hover:bg-components-input-bg-disabled hover:border-transparent',
|
||||||
|
destructive && 'bg-components-input-bg-destructive border-components-input-border-destructive text-components-input-text-filled hover:bg-components-input-bg-destructive hover:border-components-input-border-destructive focus:bg-components-input-bg-destructive focus:border-components-input-border-destructive',
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
placeholder={placeholder ?? (showLeftIcon ? t('common.operation.search') ?? '' : 'please input')}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
disabled={disabled}
|
||||||
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
{showClearIcon && value && !disabled && !destructive && (
|
||||||
|
<div className={cn('absolute right-2 top-1/2 -translate-y-1/2 group p-[1px] cursor-pointer')} onClick={onClear}>
|
||||||
|
<RiCloseCircleFill className='w-3.5 h-3.5 text-text-quaternary cursor-pointer group-hover:text-text-tertiary'/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{destructive && (
|
||||||
|
<RiErrorWarningLine className='absolute right-2 top-1/2 -translate-y-1/2 w-4 h-4 text-text-destructive-secondary'/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,66 +0,0 @@
|
|||||||
import type { FC } from 'react'
|
|
||||||
import { useState } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import { RiSearchLine } from '@remixicon/react'
|
|
||||||
import cn from '@/utils/classnames'
|
|
||||||
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
|
|
||||||
|
|
||||||
type SearchInputProps = {
|
|
||||||
placeholder?: string
|
|
||||||
className?: string
|
|
||||||
value: string
|
|
||||||
onChange: (v: string) => void
|
|
||||||
white?: boolean
|
|
||||||
}
|
|
||||||
const SearchInput: FC<SearchInputProps> = ({
|
|
||||||
placeholder,
|
|
||||||
className,
|
|
||||||
value,
|
|
||||||
onChange,
|
|
||||||
white,
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const [focus, setFocus] = useState<boolean>(false)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={cn(
|
|
||||||
'group flex items-center px-2 h-8 rounded-lg bg-gray-200 hover:bg-gray-300 border border-transparent overflow-hidden',
|
|
||||||
focus && '!bg-white hover:bg-white shawdow-xs !border-gray-300',
|
|
||||||
!focus && value && 'hover:!bg-gray-200 hover:!shawdow-xs hover:!border-black/5',
|
|
||||||
white && '!bg-white hover:!bg-white shawdow-xs !border-gray-300 hover:!border-gray-300',
|
|
||||||
className,
|
|
||||||
)}>
|
|
||||||
<div className="pointer-events-none shrink-0 flex items-center mr-1.5 justify-center w-4 h-4">
|
|
||||||
<RiSearchLine className="h-3.5 w-3.5 text-gray-500" aria-hidden="true" />
|
|
||||||
</div>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="query"
|
|
||||||
className={cn(
|
|
||||||
'grow block h-[18px] bg-gray-200 border-0 text-gray-700 text-[13px] placeholder:text-gray-500 appearance-none outline-none group-hover:bg-gray-300 caret-blue-600',
|
|
||||||
focus && '!bg-white hover:bg-white group-hover:bg-white placeholder:!text-gray-400',
|
|
||||||
!focus && value && 'hover:!bg-gray-200 group-hover:!bg-gray-200',
|
|
||||||
white && '!bg-white hover:!bg-white group-hover:!bg-white placeholder:!text-gray-400',
|
|
||||||
)}
|
|
||||||
placeholder={placeholder || t('common.operation.search')!}
|
|
||||||
value={value}
|
|
||||||
onChange={(e) => {
|
|
||||||
onChange(e.target.value)
|
|
||||||
}}
|
|
||||||
onFocus={() => setFocus(true)}
|
|
||||||
onBlur={() => setFocus(false)}
|
|
||||||
autoComplete="off"
|
|
||||||
/>
|
|
||||||
{value && (
|
|
||||||
<div
|
|
||||||
className='shrink-0 flex items-center justify-center w-4 h-4 cursor-pointer group/clear'
|
|
||||||
onClick={() => onChange('')}
|
|
||||||
>
|
|
||||||
<XCircle className='w-3.5 h-3.5 text-gray-400 group-hover/clear:text-gray-600' />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SearchInput
|
|
||||||
@ -10,7 +10,7 @@ import {
|
|||||||
PortalToFollowElemContent,
|
PortalToFollowElemContent,
|
||||||
PortalToFollowElemTrigger,
|
PortalToFollowElemTrigger,
|
||||||
} from '@/app/components/base/portal-to-follow-elem'
|
} from '@/app/components/base/portal-to-follow-elem'
|
||||||
import SearchInput from '@/app/components/base/search-input'
|
import Input from '@/app/components/base/input'
|
||||||
import { Tag01, Tag03 } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
|
import { Tag01, Tag03 } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
|
||||||
import { Check } from '@/app/components/base/icons/src/vender/line/general'
|
import { Check } from '@/app/components/base/icons/src/vender/line/general'
|
||||||
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
|
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
|
||||||
@ -111,7 +111,13 @@ const TagFilter: FC<TagFilterProps> = ({
|
|||||||
<PortalToFollowElemContent className='z-[1002]'>
|
<PortalToFollowElemContent className='z-[1002]'>
|
||||||
<div className='relative w-[240px] bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg'>
|
<div className='relative w-[240px] bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg'>
|
||||||
<div className='p-2 border-b-[0.5px] border-black/5'>
|
<div className='p-2 border-b-[0.5px] border-black/5'>
|
||||||
<SearchInput white value={keywords} onChange={handleKeywordsChange} />
|
<Input
|
||||||
|
showLeftIcon
|
||||||
|
showClearIcon
|
||||||
|
value={keywords}
|
||||||
|
onChange={e => handleKeywordsChange(e.target.value)}
|
||||||
|
onClear={() => handleKeywordsChange('')}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='p-1 max-h-72 overflow-auto'>
|
<div className='p-1 max-h-72 overflow-auto'>
|
||||||
{filteredTagList.map(tag => (
|
{filteredTagList.map(tag => (
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import cn from '@/utils/classnames'
|
|||||||
import type { HtmlContentProps } from '@/app/components/base/popover'
|
import type { HtmlContentProps } from '@/app/components/base/popover'
|
||||||
import CustomPopover from '@/app/components/base/popover'
|
import CustomPopover from '@/app/components/base/popover'
|
||||||
import Divider from '@/app/components/base/divider'
|
import Divider from '@/app/components/base/divider'
|
||||||
import SearchInput from '@/app/components/base/search-input'
|
import Input from '@/app/components/base/input'
|
||||||
import { Tag01, Tag03 } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
|
import { Tag01, Tag03 } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
|
||||||
import type { Tag } from '@/app/components/base/tag-management/constant'
|
import type { Tag } from '@/app/components/base/tag-management/constant'
|
||||||
import Checkbox from '@/app/components/base/checkbox'
|
import Checkbox from '@/app/components/base/checkbox'
|
||||||
@ -129,7 +129,14 @@ const Panel = (props: PanelProps) => {
|
|||||||
return (
|
return (
|
||||||
<div className='relative w-full bg-white rounded-lg border-[0.5px] border-gray-200' onMouseLeave={onMouseLeave}>
|
<div className='relative w-full bg-white rounded-lg border-[0.5px] border-gray-200' onMouseLeave={onMouseLeave}>
|
||||||
<div className='p-2 border-b-[0.5px] border-black/5'>
|
<div className='p-2 border-b-[0.5px] border-black/5'>
|
||||||
<SearchInput placeholder={t('common.tag.selectorPlaceholder') || ''} white value={keywords} onChange={handleKeywordsChange} />
|
<Input
|
||||||
|
showLeftIcon
|
||||||
|
showClearIcon
|
||||||
|
value={keywords}
|
||||||
|
placeholder={t('common.tag.selectorPlaceholder') || ''}
|
||||||
|
onChange={e => handleKeywordsChange(e.target.value)}
|
||||||
|
onClear={() => handleKeywordsChange('')}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{keywords && notExisted && (
|
{keywords && notExisted && (
|
||||||
<div className='p-1'>
|
<div className='p-1'>
|
||||||
|
|||||||
@ -58,7 +58,7 @@ const EmptyDatasetCreationModal = ({
|
|||||||
<div className={s.tip}>{t('datasetCreation.stepOne.modal.tip')}</div>
|
<div className={s.tip}>{t('datasetCreation.stepOne.modal.tip')}</div>
|
||||||
<div className={s.form}>
|
<div className={s.form}>
|
||||||
<div className={s.label}>{t('datasetCreation.stepOne.modal.input')}</div>
|
<div className={s.label}>{t('datasetCreation.stepOne.modal.input')}</div>
|
||||||
<Input className='!h-8' value={inputValue} placeholder={t('datasetCreation.stepOne.modal.placeholder') || ''} onChange={setInputValue} />
|
<Input value={inputValue} placeholder={t('datasetCreation.stepOne.modal.placeholder') || ''} onChange={e => setInputValue(e.target.value)} />
|
||||||
</div>
|
</div>
|
||||||
<div className='flex flex-row-reverse'>
|
<div className='flex flex-row-reverse'>
|
||||||
<Button className='w-24 ml-2' variant='primary' onClick={submit}>{t('datasetCreation.stepOne.modal.confirmButton')}</Button>
|
<Button className='w-24 ml-2' variant='primary' onClick={submit}>{t('datasetCreation.stepOne.modal.confirmButton')}</Button>
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React, { memo, useEffect, useMemo, useState } from 'react'
|
import React, { memo, useEffect, useMemo, useState } from 'react'
|
||||||
|
import { useDebounceFn } from 'ahooks'
|
||||||
import { HashtagIcon } from '@heroicons/react/24/solid'
|
import { HashtagIcon } from '@heroicons/react/24/solid'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
import { debounce, isNil, omitBy } from 'lodash-es'
|
import { isNil, omitBy } from 'lodash-es'
|
||||||
import {
|
import {
|
||||||
RiCloseLine,
|
RiCloseLine,
|
||||||
RiEditLine,
|
RiEditLine,
|
||||||
@ -241,7 +242,8 @@ const Completed: FC<ICompletedProps> = ({
|
|||||||
// the current segment id and whether to show the modal
|
// the current segment id and whether to show the modal
|
||||||
const [currSegment, setCurrSegment] = useState<{ segInfo?: SegmentDetailModel; showModal: boolean }>({ showModal: false })
|
const [currSegment, setCurrSegment] = useState<{ segInfo?: SegmentDetailModel; showModal: boolean }>({ showModal: false })
|
||||||
|
|
||||||
const [searchValue, setSearchValue] = useState<string>() // the search value
|
const [inputValue, setInputValue] = useState<string>('') // the input value
|
||||||
|
const [searchValue, setSearchValue] = useState<string>('') // the search value
|
||||||
const [selectedStatus, setSelectedStatus] = useState<boolean | 'all'>('all') // the selected status, enabled/disabled/undefined
|
const [selectedStatus, setSelectedStatus] = useState<boolean | 'all'>('all') // the selected status, enabled/disabled/undefined
|
||||||
|
|
||||||
const [lastSegmentsRes, setLastSegmentsRes] = useState<SegmentsResponse | undefined>(undefined)
|
const [lastSegmentsRes, setLastSegmentsRes] = useState<SegmentsResponse | undefined>(undefined)
|
||||||
@ -250,6 +252,15 @@ const Completed: FC<ICompletedProps> = ({
|
|||||||
const [total, setTotal] = useState<number | undefined>()
|
const [total, setTotal] = useState<number | undefined>()
|
||||||
const { eventEmitter } = useEventEmitterContextContext()
|
const { eventEmitter } = useEventEmitterContextContext()
|
||||||
|
|
||||||
|
const { run: handleSearch } = useDebounceFn(() => {
|
||||||
|
setSearchValue(inputValue)
|
||||||
|
}, { wait: 500 })
|
||||||
|
|
||||||
|
const handleInputChange = (value: string) => {
|
||||||
|
setInputValue(value)
|
||||||
|
handleSearch()
|
||||||
|
}
|
||||||
|
|
||||||
const onChangeStatus = ({ value }: Item) => {
|
const onChangeStatus = ({ value }: Item) => {
|
||||||
setSelectedStatus(value === 'all' ? 'all' : !!value)
|
setSelectedStatus(value === 'all' ? 'all' : !!value)
|
||||||
}
|
}
|
||||||
@ -391,7 +402,14 @@ const Completed: FC<ICompletedProps> = ({
|
|||||||
defaultValue={'all'}
|
defaultValue={'all'}
|
||||||
className={s.select}
|
className={s.select}
|
||||||
wrapperClassName='h-fit w-[120px] mr-2' />
|
wrapperClassName='h-fit w-[120px] mr-2' />
|
||||||
<Input showPrefix wrapperClassName='!w-52' className='!h-8' onChange={debounce(setSearchValue, 500)} />
|
<Input
|
||||||
|
showLeftIcon
|
||||||
|
showClearIcon
|
||||||
|
wrapperClassName='!w-52'
|
||||||
|
value={inputValue}
|
||||||
|
onChange={e => handleInputChange(e.target.value)}
|
||||||
|
onClear={() => handleInputChange('')}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<InfiniteVirtualList
|
<InfiniteVirtualList
|
||||||
embeddingAvailable={embeddingAvailable}
|
embeddingAvailable={embeddingAvailable}
|
||||||
|
|||||||
@ -78,8 +78,7 @@ export const FieldInfo: FC<IFieldInfoProps> = ({
|
|||||||
placeholder={`${t('datasetDocuments.metadata.placeholder.add')}${label}`}
|
placeholder={`${t('datasetDocuments.metadata.placeholder.add')}${label}`}
|
||||||
/>
|
/>
|
||||||
: <Input
|
: <Input
|
||||||
className={s.input}
|
onChange={e => onUpdate?.(e.target.value)}
|
||||||
onChange={onUpdate}
|
|
||||||
value={value}
|
value={value}
|
||||||
defaultValue={defaultValue}
|
defaultValue={defaultValue}
|
||||||
placeholder={`${t('datasetDocuments.metadata.placeholder.add')}${label}`}
|
placeholder={`${t('datasetDocuments.metadata.placeholder.add')}${label}`}
|
||||||
|
|||||||
@ -4,7 +4,8 @@ import React, { useMemo, useState } from 'react'
|
|||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
import { debounce, groupBy, omit } from 'lodash-es'
|
import { useDebounceFn } from 'ahooks'
|
||||||
|
import { groupBy, omit } from 'lodash-es'
|
||||||
import { PlusIcon } from '@heroicons/react/24/solid'
|
import { PlusIcon } from '@heroicons/react/24/solid'
|
||||||
import List from './list'
|
import List from './list'
|
||||||
import s from './style.module.css'
|
import s from './style.module.css'
|
||||||
@ -76,6 +77,7 @@ export const fetcher = (url: string) => get(url, {}, {})
|
|||||||
|
|
||||||
const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
|
const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const [inputValue, setInputValue] = useState<string>('') // the input value
|
||||||
const [searchValue, setSearchValue] = useState<string>('')
|
const [searchValue, setSearchValue] = useState<string>('')
|
||||||
const [currPage, setCurrPage] = React.useState<number>(0)
|
const [currPage, setCurrPage] = React.useState<number>(0)
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -192,6 +194,15 @@ const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
|
|||||||
|
|
||||||
const documentsList = isDataSourceNotion ? documentsWithProgress?.data : documentsRes?.data
|
const documentsList = isDataSourceNotion ? documentsWithProgress?.data : documentsRes?.data
|
||||||
|
|
||||||
|
const { run: handleSearch } = useDebounceFn(() => {
|
||||||
|
setSearchValue(inputValue)
|
||||||
|
}, { wait: 500 })
|
||||||
|
|
||||||
|
const handleInputChange = (value: string) => {
|
||||||
|
setInputValue(value)
|
||||||
|
handleSearch()
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col h-full overflow-y-auto'>
|
<div className='flex flex-col h-full overflow-y-auto'>
|
||||||
<div className='flex flex-col justify-center gap-1 px-6 pt-4'>
|
<div className='flex flex-col justify-center gap-1 px-6 pt-4'>
|
||||||
@ -201,11 +212,12 @@ const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
|
|||||||
<div className='flex flex-col px-6 py-4 flex-1'>
|
<div className='flex flex-col px-6 py-4 flex-1'>
|
||||||
<div className='flex items-center justify-between flex-wrap'>
|
<div className='flex items-center justify-between flex-wrap'>
|
||||||
<Input
|
<Input
|
||||||
showPrefix
|
showLeftIcon
|
||||||
|
showClearIcon
|
||||||
wrapperClassName='!w-[200px]'
|
wrapperClassName='!w-[200px]'
|
||||||
className='!h-8 !text-[13px]'
|
value={inputValue}
|
||||||
onChange={debounce(setSearchValue, 500)}
|
onChange={e => handleInputChange(e.target.value)}
|
||||||
value={searchValue}
|
onClear={() => handleInputChange('')}
|
||||||
/>
|
/>
|
||||||
<div className='flex gap-2 justify-center items-center !h-8'>
|
<div className='flex gap-2 justify-center items-center !h-8'>
|
||||||
<RetryButton datasetId={datasetId} />
|
<RetryButton datasetId={datasetId} />
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import {
|
|||||||
PortalToFollowElemTrigger,
|
PortalToFollowElemTrigger,
|
||||||
} from '@/app/components/base/portal-to-follow-elem'
|
} from '@/app/components/base/portal-to-follow-elem'
|
||||||
import Avatar from '@/app/components/base/avatar'
|
import Avatar from '@/app/components/base/avatar'
|
||||||
import SearchInput from '@/app/components/base/search-input'
|
import Input from '@/app/components/base/input'
|
||||||
import { Check } from '@/app/components/base/icons/src/vender/line/general'
|
import { Check } from '@/app/components/base/icons/src/vender/line/general'
|
||||||
import { Users01, UsersPlus } from '@/app/components/base/icons/src/vender/solid/users'
|
import { Users01, UsersPlus } from '@/app/components/base/icons/src/vender/solid/users'
|
||||||
import type { DatasetPermission } from '@/models/datasets'
|
import type { DatasetPermission } from '@/models/datasets'
|
||||||
@ -99,7 +99,7 @@ const PermissionSelector = ({ disabled, permission, value, memberList, onChange,
|
|||||||
)}
|
)}
|
||||||
</PortalToFollowElemTrigger>
|
</PortalToFollowElemTrigger>
|
||||||
<PortalToFollowElemContent className='z-[1002]'>
|
<PortalToFollowElemContent className='z-[1002]'>
|
||||||
<div className='relative w-[480px] bg-white rounded-lg border-[0.5px] bg-gray-200 shadow-lg'>
|
<div className='relative w-[480px] rounded-lg border-[0.5px] bg-white shadow-lg'>
|
||||||
<div className='p-1'>
|
<div className='p-1'>
|
||||||
<div className='pl-3 pr-2 py-1 rounded-lg hover:bg-gray-50 cursor-pointer' onClick={() => {
|
<div className='pl-3 pr-2 py-1 rounded-lg hover:bg-gray-50 cursor-pointer' onClick={() => {
|
||||||
onChange('only_me')
|
onChange('only_me')
|
||||||
@ -139,7 +139,13 @@ const PermissionSelector = ({ disabled, permission, value, memberList, onChange,
|
|||||||
{permission === 'partial_members' && (
|
{permission === 'partial_members' && (
|
||||||
<div className='max-h-[360px] border-t-[1px] border-gray-100 p-1 overflow-y-auto'>
|
<div className='max-h-[360px] border-t-[1px] border-gray-100 p-1 overflow-y-auto'>
|
||||||
<div className='sticky left-0 top-0 p-2 pb-1 bg-white'>
|
<div className='sticky left-0 top-0 p-2 pb-1 bg-white'>
|
||||||
<SearchInput white value={keywords} onChange={handleKeywordsChange} />
|
<Input
|
||||||
|
showLeftIcon
|
||||||
|
showClearIcon
|
||||||
|
value={keywords}
|
||||||
|
onChange={e => handleKeywordsChange(e.target.value)}
|
||||||
|
onClear={() => handleKeywordsChange('')}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{showMe && (
|
{showMe && (
|
||||||
<div className='pl-3 pr-[10px] py-1 flex gap-2 items-center rounded-lg'>
|
<div className='pl-3 pr-[10px] py-1 flex gap-2 items-center rounded-lg'>
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import Loading from '@/app/components/base/loading'
|
|||||||
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
import { getRedirection } from '@/utils/app-redirection'
|
import { getRedirection } from '@/utils/app-redirection'
|
||||||
import SearchInput from '@/app/components/base/search-input'
|
import Input from '@/app/components/base/input'
|
||||||
|
|
||||||
type AppsProps = {
|
type AppsProps = {
|
||||||
pageType?: PageType
|
pageType?: PageType
|
||||||
@ -185,7 +185,14 @@ const Apps = ({
|
|||||||
allCategoriesEn={allCategoriesEn}
|
allCategoriesEn={allCategoriesEn}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
<SearchInput value={keywords} onChange={handleKeywordsChange}/>
|
<Input
|
||||||
|
showLeftIcon
|
||||||
|
showClearIcon
|
||||||
|
wrapperClassName='w-[200px]'
|
||||||
|
value={keywords}
|
||||||
|
onChange={e => handleKeywordsChange(e.target.value)}
|
||||||
|
onClear={() => handleKeywordsChange('')}
|
||||||
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import { getLanguage } from '@/i18n/language'
|
|||||||
import Drawer from '@/app/components/base/drawer'
|
import Drawer from '@/app/components/base/drawer'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Loading from '@/app/components/base/loading'
|
import Loading from '@/app/components/base/loading'
|
||||||
import SearchInput from '@/app/components/base/search-input'
|
import Input from '@/app/components/base/input'
|
||||||
import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal'
|
import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal'
|
||||||
import ConfigCredential from '@/app/components/tools/setting/build-in/config-credentials'
|
import ConfigCredential from '@/app/components/tools/setting/build-in/config-credentials'
|
||||||
import {
|
import {
|
||||||
@ -193,7 +193,13 @@ const AddToolModal: FC<Props> = ({
|
|||||||
<div className='relative grow bg-white rounded-r-xl overflow-y-auto'>
|
<div className='relative grow bg-white rounded-r-xl overflow-y-auto'>
|
||||||
<div className='z-10 sticky top-0 left-0 right-0 p-2 flex items-center gap-1 bg-white'>
|
<div className='z-10 sticky top-0 left-0 right-0 p-2 flex items-center gap-1 bg-white'>
|
||||||
<div className='grow'>
|
<div className='grow'>
|
||||||
<SearchInput className='w-full' value={keywords} onChange={handleKeywordsChange} />
|
<Input
|
||||||
|
showLeftIcon
|
||||||
|
showClearIcon
|
||||||
|
value={keywords}
|
||||||
|
onChange={e => handleKeywordsChange(e.target.value)}
|
||||||
|
onClear={() => handleKeywordsChange('')}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='ml-2 mr-1 w-[1px] h-4 bg-gray-200'></div>
|
<div className='ml-2 mr-1 w-[1px] h-4 bg-gray-200'></div>
|
||||||
<div className='p-2 cursor-pointer' onClick={onHide}>
|
<div className='p-2 cursor-pointer' onClick={onHide}>
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import {
|
|||||||
PortalToFollowElemContent,
|
PortalToFollowElemContent,
|
||||||
PortalToFollowElemTrigger,
|
PortalToFollowElemTrigger,
|
||||||
} from '@/app/components/base/portal-to-follow-elem'
|
} from '@/app/components/base/portal-to-follow-elem'
|
||||||
import SearchInput from '@/app/components/base/search-input'
|
import Input from '@/app/components/base/input'
|
||||||
import { Tag01, Tag03 } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
|
import { Tag01, Tag03 } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
|
||||||
import { Check } from '@/app/components/base/icons/src/vender/line/general'
|
import { Check } from '@/app/components/base/icons/src/vender/line/general'
|
||||||
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
|
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
|
||||||
@ -113,7 +113,13 @@ const LabelFilter: FC<LabelFilterProps> = ({
|
|||||||
<PortalToFollowElemContent className='z-[1002]'>
|
<PortalToFollowElemContent className='z-[1002]'>
|
||||||
<div className='relative w-[240px] bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg'>
|
<div className='relative w-[240px] bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg'>
|
||||||
<div className='p-2 border-b-[0.5px] border-black/5'>
|
<div className='p-2 border-b-[0.5px] border-black/5'>
|
||||||
<SearchInput white value={keywords} onChange={handleKeywordsChange} />
|
<Input
|
||||||
|
showLeftIcon
|
||||||
|
showClearIcon
|
||||||
|
value={keywords}
|
||||||
|
onChange={e => handleKeywordsChange(e.target.value)}
|
||||||
|
onClear={() => handleKeywordsChange('')}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='p-1'>
|
<div className='p-1'>
|
||||||
{filteredLabelList.map(label => (
|
{filteredLabelList.map(label => (
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import {
|
|||||||
PortalToFollowElemContent,
|
PortalToFollowElemContent,
|
||||||
PortalToFollowElemTrigger,
|
PortalToFollowElemTrigger,
|
||||||
} from '@/app/components/base/portal-to-follow-elem'
|
} from '@/app/components/base/portal-to-follow-elem'
|
||||||
import SearchInput from '@/app/components/base/search-input'
|
import Input from '@/app/components/base/input'
|
||||||
import { Tag03 } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
|
import { Tag03 } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
|
||||||
import Checkbox from '@/app/components/base/checkbox'
|
import Checkbox from '@/app/components/base/checkbox'
|
||||||
import type { Label } from '@/app/components/tools/labels/constant'
|
import type { Label } from '@/app/components/tools/labels/constant'
|
||||||
@ -94,7 +94,13 @@ const LabelSelector: FC<LabelSelectorProps> = ({
|
|||||||
<PortalToFollowElemContent className='z-[1040]'>
|
<PortalToFollowElemContent className='z-[1040]'>
|
||||||
<div className='relative w-[591px] bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg'>
|
<div className='relative w-[591px] bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg'>
|
||||||
<div className='p-2 border-b-[0.5px] border-black/5'>
|
<div className='p-2 border-b-[0.5px] border-black/5'>
|
||||||
<SearchInput white value={keywords} onChange={handleKeywordsChange} />
|
<Input
|
||||||
|
showLeftIcon
|
||||||
|
showClearIcon
|
||||||
|
value={keywords}
|
||||||
|
onChange={e => handleKeywordsChange(e.target.value)}
|
||||||
|
onClear={() => handleKeywordsChange('')}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='p-1 max-h-[264px] overflow-y-auto'>
|
<div className='p-1 max-h-[264px] overflow-y-auto'>
|
||||||
{filteredLabelList.map(label => (
|
{filteredLabelList.map(label => (
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import cn from '@/utils/classnames'
|
|||||||
import { useTabSearchParams } from '@/hooks/use-tab-searchparams'
|
import { useTabSearchParams } from '@/hooks/use-tab-searchparams'
|
||||||
import TabSliderNew from '@/app/components/base/tab-slider-new'
|
import TabSliderNew from '@/app/components/base/tab-slider-new'
|
||||||
import LabelFilter from '@/app/components/tools/labels/filter'
|
import LabelFilter from '@/app/components/tools/labels/filter'
|
||||||
import SearchInput from '@/app/components/base/search-input'
|
import Input from '@/app/components/base/input'
|
||||||
import { DotsGrid } from '@/app/components/base/icons/src/vender/line/general'
|
import { DotsGrid } from '@/app/components/base/icons/src/vender/line/general'
|
||||||
import { Colors } from '@/app/components/base/icons/src/vender/line/others'
|
import { Colors } from '@/app/components/base/icons/src/vender/line/others'
|
||||||
import { Route } from '@/app/components/base/icons/src/vender/line/mapsAndTravel'
|
import { Route } from '@/app/components/base/icons/src/vender/line/mapsAndTravel'
|
||||||
@ -84,7 +84,14 @@ const ProviderList = () => {
|
|||||||
/>
|
/>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
<LabelFilter value={tagFilterValue} onChange={handleTagsChange} />
|
<LabelFilter value={tagFilterValue} onChange={handleTagsChange} />
|
||||||
<SearchInput className='w-[200px]' value={keywords} onChange={handleKeywordsChange} />
|
<Input
|
||||||
|
showLeftIcon
|
||||||
|
showClearIcon
|
||||||
|
wrapperClassName='w-[200px]'
|
||||||
|
value={keywords}
|
||||||
|
onChange={e => handleKeywordsChange(e.target.value)}
|
||||||
|
onClear={() => handleKeywordsChange('')}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={cn(
|
<div className={cn(
|
||||||
|
|||||||
@ -12,9 +12,6 @@ import type {
|
|||||||
OffsetOptions,
|
OffsetOptions,
|
||||||
Placement,
|
Placement,
|
||||||
} from '@floating-ui/react'
|
} from '@floating-ui/react'
|
||||||
import {
|
|
||||||
RiSearchLine,
|
|
||||||
} from '@remixicon/react'
|
|
||||||
import type { BlockEnum, OnSelectBlock } from '../types'
|
import type { BlockEnum, OnSelectBlock } from '../types'
|
||||||
import Tabs from './tabs'
|
import Tabs from './tabs'
|
||||||
import {
|
import {
|
||||||
@ -22,10 +19,10 @@ import {
|
|||||||
PortalToFollowElemContent,
|
PortalToFollowElemContent,
|
||||||
PortalToFollowElemTrigger,
|
PortalToFollowElemTrigger,
|
||||||
} from '@/app/components/base/portal-to-follow-elem'
|
} from '@/app/components/base/portal-to-follow-elem'
|
||||||
|
import Input from '@/app/components/base/input'
|
||||||
import {
|
import {
|
||||||
Plus02,
|
Plus02,
|
||||||
} from '@/app/components/base/icons/src/vender/line/general'
|
} from '@/app/components/base/icons/src/vender/line/general'
|
||||||
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
|
|
||||||
|
|
||||||
type NodeSelectorProps = {
|
type NodeSelectorProps = {
|
||||||
open?: boolean
|
open?: boolean
|
||||||
@ -111,30 +108,16 @@ const NodeSelector: FC<NodeSelectorProps> = ({
|
|||||||
</PortalToFollowElemTrigger>
|
</PortalToFollowElemTrigger>
|
||||||
<PortalToFollowElemContent className='z-[1000]'>
|
<PortalToFollowElemContent className='z-[1000]'>
|
||||||
<div className={`rounded-lg border-[0.5px] border-gray-200 bg-white shadow-lg ${popupClassName}`}>
|
<div className={`rounded-lg border-[0.5px] border-gray-200 bg-white shadow-lg ${popupClassName}`}>
|
||||||
<div className='px-2 pt-2'>
|
<div className='px-2 pt-2' onClick={e => e.stopPropagation()}>
|
||||||
<div
|
<Input
|
||||||
className='flex items-center px-2 rounded-lg bg-gray-100'
|
showLeftIcon
|
||||||
onClick={e => e.stopPropagation()}
|
showClearIcon
|
||||||
>
|
autoFocus
|
||||||
<RiSearchLine className='shrink-0 ml-[1px] mr-[5px] w-3.5 h-3.5 text-gray-400' />
|
value={searchText}
|
||||||
<input
|
placeholder={t('workflow.tabs.searchBlock') || ''}
|
||||||
value={searchText}
|
onChange={e => setSearchText(e.target.value)}
|
||||||
className='grow px-0.5 py-[7px] text-[13px] text-gray-700 bg-transparent appearance-none outline-none caret-primary-600 placeholder:text-gray-400'
|
onClear={() => setSearchText('')}
|
||||||
placeholder={t('workflow.tabs.searchBlock') || ''}
|
/>
|
||||||
onChange={e => setSearchText(e.target.value)}
|
|
||||||
autoFocus
|
|
||||||
/>
|
|
||||||
{
|
|
||||||
searchText && (
|
|
||||||
<div
|
|
||||||
className='flex items-center justify-center ml-[5px] w-[18px] h-[18px] cursor-pointer'
|
|
||||||
onClick={() => setSearchText('')}
|
|
||||||
>
|
|
||||||
<XCircle className='w-[14px] h-[14px] text-gray-400' />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<Tabs
|
<Tabs
|
||||||
onSelect={handleSelect}
|
onSelect={handleSelect}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ type Props = {
|
|||||||
const FilterCondition: FC<Props> = ({
|
const FilterCondition: FC<Props> = ({
|
||||||
hasSubVariable,
|
hasSubVariable,
|
||||||
}) => {
|
}) => {
|
||||||
|
const [input, setInput] = React.useState('')
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{hasSubVariable && <SubVariablePicker className="mb-2" />}
|
{hasSubVariable && <SubVariablePicker className="mb-2" />}
|
||||||
@ -24,7 +25,7 @@ const FilterCondition: FC<Props> = ({
|
|||||||
]}
|
]}
|
||||||
onSelect={() => { }}
|
onSelect={() => { }}
|
||||||
/>
|
/>
|
||||||
<Input className='grow h-8 text-components-input-text-filled system-sm-regular !text-[13px]' />
|
<Input className='grow' value={input} onChange={e => setInput(e.target.value)} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user