feat: introduce trigger functionality (#27644)
Signed-off-by: lyzno1 <yuanyouhuilyz@gmail.com> Co-authored-by: Stream <Stream_2@qq.com> Co-authored-by: lyzno1 <92089059+lyzno1@users.noreply.github.com> Co-authored-by: zhsama <torvalds@linux.do> Co-authored-by: Harry <xh001x@hotmail.com> Co-authored-by: lyzno1 <yuanyouhuilyz@gmail.com> Co-authored-by: yessenia <yessenia.contact@gmail.com> Co-authored-by: hjlarry <hjlarry@163.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: WTW0313 <twwu@dify.ai> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@@ -20,7 +20,7 @@ const Badge = ({
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'relative inline-flex h-5 items-center rounded-[5px] border border-divider-deep px-[5px] leading-3 text-text-tertiary',
|
||||
'relative inline-flex h-5 items-center whitespace-nowrap rounded-[5px] border border-divider-deep px-[5px] leading-3 text-text-tertiary',
|
||||
uppercase ? 'system-2xs-medium-uppercase' : 'system-xs-medium',
|
||||
className,
|
||||
)}
|
||||
|
||||
203
web/app/components/base/checkbox-list/index.tsx
Normal file
@@ -0,0 +1,203 @@
|
||||
'use client'
|
||||
import Badge from '@/app/components/base/badge'
|
||||
import Checkbox from '@/app/components/base/checkbox'
|
||||
import SearchInput from '@/app/components/base/search-input'
|
||||
import SearchMenu from '@/assets/search-menu.svg'
|
||||
import cn from '@/utils/classnames'
|
||||
import Image from 'next/image'
|
||||
import type { FC } from 'react'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '../button'
|
||||
|
||||
export type CheckboxListOption = {
|
||||
label: string
|
||||
value: string
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export type CheckboxListProps = {
|
||||
title?: string
|
||||
label?: string
|
||||
description?: string
|
||||
options: CheckboxListOption[]
|
||||
value?: string[]
|
||||
onChange?: (value: string[]) => void
|
||||
disabled?: boolean
|
||||
containerClassName?: string
|
||||
showSelectAll?: boolean
|
||||
showCount?: boolean
|
||||
showSearch?: boolean
|
||||
maxHeight?: string | number
|
||||
}
|
||||
|
||||
const CheckboxList: FC<CheckboxListProps> = ({
|
||||
title = '',
|
||||
label,
|
||||
description,
|
||||
options,
|
||||
value = [],
|
||||
onChange,
|
||||
disabled = false,
|
||||
containerClassName,
|
||||
showSelectAll = true,
|
||||
showCount = true,
|
||||
showSearch = true,
|
||||
maxHeight,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
|
||||
const filteredOptions = useMemo(() => {
|
||||
if (!searchQuery?.trim())
|
||||
return options
|
||||
|
||||
const query = searchQuery.toLowerCase()
|
||||
return options.filter(option =>
|
||||
option.label.toLowerCase().includes(query) || option.value.toLowerCase().includes(query),
|
||||
)
|
||||
}, [options, searchQuery])
|
||||
|
||||
const selectedCount = value.length
|
||||
|
||||
const isAllSelected = useMemo(() => {
|
||||
const selectableOptions = options.filter(option => !option.disabled)
|
||||
return selectableOptions.length > 0 && selectableOptions.every(option => value.includes(option.value))
|
||||
}, [options, value])
|
||||
|
||||
const isIndeterminate = useMemo(() => {
|
||||
const selectableOptions = options.filter(option => !option.disabled)
|
||||
const selectedCount = selectableOptions.filter(option => value.includes(option.value)).length
|
||||
return selectedCount > 0 && selectedCount < selectableOptions.length
|
||||
}, [options, value])
|
||||
|
||||
const handleSelectAll = useCallback(() => {
|
||||
if (disabled)
|
||||
return
|
||||
|
||||
if (isAllSelected) {
|
||||
// Deselect all
|
||||
onChange?.([])
|
||||
}
|
||||
else {
|
||||
// Select all non-disabled options
|
||||
const allValues = options
|
||||
.filter(option => !option.disabled)
|
||||
.map(option => option.value)
|
||||
onChange?.(allValues)
|
||||
}
|
||||
}, [isAllSelected, options, onChange, disabled])
|
||||
|
||||
const handleToggleOption = useCallback((optionValue: string) => {
|
||||
if (disabled)
|
||||
return
|
||||
|
||||
const newValue = value.includes(optionValue)
|
||||
? value.filter(v => v !== optionValue)
|
||||
: [...value, optionValue]
|
||||
onChange?.(newValue)
|
||||
}, [value, onChange, disabled])
|
||||
|
||||
return (
|
||||
<div className={cn('flex w-full flex-col gap-1', containerClassName)}>
|
||||
{label && (
|
||||
<div className='system-sm-medium text-text-secondary'>
|
||||
{label}
|
||||
</div>
|
||||
)}
|
||||
{description && (
|
||||
<div className='body-xs-regular text-text-tertiary'>
|
||||
{description}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className='rounded-lg border border-components-panel-border bg-components-panel-bg'>
|
||||
{(showSelectAll || title || showSearch) && (
|
||||
<div className='relative flex items-center gap-2 border-b border-divider-subtle px-3 py-2'>
|
||||
{!searchQuery && showSelectAll && (
|
||||
<Checkbox
|
||||
checked={isAllSelected}
|
||||
indeterminate={isIndeterminate}
|
||||
onCheck={handleSelectAll}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)}
|
||||
{!searchQuery ? <div className='flex min-w-0 flex-1 items-center gap-1'>
|
||||
{title && (
|
||||
<span className='system-xs-semibold-uppercase truncate leading-5 text-text-secondary'>
|
||||
{title}
|
||||
</span>
|
||||
)}
|
||||
{showCount && selectedCount > 0 && (
|
||||
<Badge uppercase>
|
||||
{t('common.operation.selectCount', { count: selectedCount })}
|
||||
</Badge>
|
||||
)}
|
||||
</div> : <div className='system-sm-medium-uppercase flex-1 leading-6 text-text-secondary'>{
|
||||
filteredOptions.length > 0
|
||||
? t('common.operation.searchCount', { count: filteredOptions.length, content: title })
|
||||
: t('common.operation.noSearchCount', { content: title })}</div>}
|
||||
{showSearch && (
|
||||
<SearchInput
|
||||
value={searchQuery}
|
||||
onChange={setSearchQuery}
|
||||
placeholder={t('common.placeholder.search')}
|
||||
className='w-40'
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className='p-1'
|
||||
style={maxHeight ? { maxHeight, overflowY: 'auto' } : {}}
|
||||
>
|
||||
{!filteredOptions.length ? (
|
||||
<div className='px-3 py-6 text-center text-sm text-text-tertiary'>
|
||||
{searchQuery ? <div className='flex flex-col items-center justify-center gap-2'>
|
||||
<Image alt='search menu' src={SearchMenu} width={32} />
|
||||
<span className='system-sm-regular text-text-secondary'>{t('common.operation.noSearchResults', { content: title })}</span>
|
||||
<Button variant='secondary-accent' size='small' onClick={() => setSearchQuery('')}>{t('common.operation.resetKeywords')}</Button>
|
||||
</div> : t('common.noData')}
|
||||
</div>
|
||||
) : (
|
||||
filteredOptions.map((option) => {
|
||||
const selected = value.includes(option.value)
|
||||
|
||||
return (
|
||||
<div
|
||||
key={option.value}
|
||||
className={cn(
|
||||
'flex cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 transition-colors hover:bg-state-base-hover',
|
||||
option.disabled && 'cursor-not-allowed opacity-50',
|
||||
)}
|
||||
onClick={() => {
|
||||
if (!option.disabled && !disabled)
|
||||
handleToggleOption(option.value)
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
checked={selected}
|
||||
onCheck={() => {
|
||||
if (!option.disabled && !disabled)
|
||||
handleToggleOption(option.value)
|
||||
}}
|
||||
disabled={option.disabled || disabled}
|
||||
/>
|
||||
<div
|
||||
className='system-sm-medium flex-1 truncate text-text-secondary'
|
||||
title={option.label}
|
||||
>
|
||||
{option.label}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CheckboxList
|
||||
@@ -30,7 +30,7 @@ const Checkbox = ({
|
||||
<div
|
||||
id={id}
|
||||
className={cn(
|
||||
'flex h-4 w-4 cursor-pointer items-center justify-center rounded-[4px] shadow-xs shadow-shadow-shadow-3',
|
||||
'flex h-4 w-4 shrink-0 cursor-pointer items-center justify-center rounded-[4px] shadow-xs shadow-shadow-shadow-3',
|
||||
checkClassName,
|
||||
disabled && disabledClassName,
|
||||
className,
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useEffect, useRef, useState } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '../button'
|
||||
import Tooltip from '../tooltip'
|
||||
|
||||
export type IConfirm = {
|
||||
className?: string
|
||||
@@ -37,7 +38,9 @@ function Confirm({
|
||||
}: IConfirm) {
|
||||
const { t } = useTranslation()
|
||||
const dialogRef = useRef<HTMLDivElement>(null)
|
||||
const titleRef = useRef<HTMLDivElement>(null)
|
||||
const [isVisible, setIsVisible] = useState(isShow)
|
||||
const [isTitleTruncated, setIsTitleTruncated] = useState(false)
|
||||
|
||||
const confirmTxt = confirmText || `${t('common.operation.confirm')}`
|
||||
const cancelTxt = cancelText || `${t('common.operation.cancel')}`
|
||||
@@ -80,6 +83,13 @@ function Confirm({
|
||||
}
|
||||
}, [isShow])
|
||||
|
||||
useEffect(() => {
|
||||
if (titleRef.current) {
|
||||
const isOverflowing = titleRef.current.scrollWidth > titleRef.current.clientWidth
|
||||
setIsTitleTruncated(isOverflowing)
|
||||
}
|
||||
}, [title, isVisible])
|
||||
|
||||
if (!isVisible)
|
||||
return null
|
||||
|
||||
@@ -92,8 +102,18 @@ function Confirm({
|
||||
<div ref={dialogRef} className={'relative w-full max-w-[480px] overflow-hidden'}>
|
||||
<div className='shadows-shadow-lg flex max-w-full flex-col items-start rounded-2xl border-[0.5px] border-solid border-components-panel-border bg-components-panel-bg'>
|
||||
<div className='flex flex-col items-start gap-2 self-stretch pb-4 pl-6 pr-6 pt-6'>
|
||||
<div className='title-2xl-semi-bold text-text-primary'>{title}</div>
|
||||
<div className='system-md-regular w-full text-text-tertiary'>{content}</div>
|
||||
<Tooltip
|
||||
popupContent={title}
|
||||
disabled={!isTitleTruncated}
|
||||
portalContentClassName='!z-[10000001]'
|
||||
asChild={false}
|
||||
triggerClassName='w-full'
|
||||
>
|
||||
<div ref={titleRef} className='title-2xl-semi-bold w-full truncate text-text-primary'>
|
||||
{title}
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div className='system-md-regular w-full whitespace-pre-wrap break-words text-text-tertiary'>{content}</div>
|
||||
</div>
|
||||
<div className='flex items-start justify-end gap-2 self-stretch p-6'>
|
||||
{showCancel && <Button onClick={onCancel}>{cancelTxt}</Button>}
|
||||
|
||||
@@ -10,26 +10,25 @@ const Footer: FC<TimePickerFooterProps> = ({
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className='flex items-center justify-end border-t-[0.5px] border-divider-regular p-2'>
|
||||
<div className='flex items-center gap-x-1'>
|
||||
{/* Now */}
|
||||
<button
|
||||
type='button'
|
||||
className='system-xs-medium flex items-center justify-center px-1.5 py-1 text-components-button-secondary-accent-text'
|
||||
onClick={handleSelectCurrentTime}
|
||||
>
|
||||
<span className='px-[3px]'>{t('time.operation.now')}</span>
|
||||
</button>
|
||||
{/* Confirm Button */}
|
||||
<Button
|
||||
variant='primary'
|
||||
size='small'
|
||||
className='w-16 px-1.5 py-1'
|
||||
onClick={handleConfirm.bind(null)}
|
||||
>
|
||||
{t('time.operation.ok')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className='flex items-center justify-between border-t-[0.5px] border-divider-regular p-2'>
|
||||
{/* Now Button */}
|
||||
<Button
|
||||
variant='secondary-accent'
|
||||
size='small'
|
||||
className='mr-1 flex-1'
|
||||
onClick={handleSelectCurrentTime}
|
||||
>
|
||||
{t('time.operation.now')}
|
||||
</Button>
|
||||
{/* Confirm Button */}
|
||||
<Button
|
||||
variant='primary'
|
||||
size='small'
|
||||
className='ml-1 flex-1'
|
||||
onClick={handleConfirm.bind(null)}
|
||||
>
|
||||
{t('time.operation.ok')}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,15 @@ jest.mock('@/app/components/base/portal-to-follow-elem', () => ({
|
||||
|
||||
jest.mock('./options', () => () => <div data-testid="time-options" />)
|
||||
jest.mock('./header', () => () => <div data-testid="time-header" />)
|
||||
jest.mock('@/app/components/base/timezone-label', () => {
|
||||
return function MockTimezoneLabel({ timezone, inline, className }: { timezone: string, inline?: boolean, className?: string }) {
|
||||
return (
|
||||
<span data-testid="timezone-label" data-timezone={timezone} data-inline={inline} className={className}>
|
||||
UTC+8
|
||||
</span>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
describe('TimePicker', () => {
|
||||
const baseProps: Pick<TimePickerProps, 'onChange' | 'onClear' | 'value'> = {
|
||||
@@ -94,4 +103,86 @@ describe('TimePicker', () => {
|
||||
expect(isDayjsObject(emitted)).toBe(true)
|
||||
expect(emitted?.utcOffset()).toBe(dayjs().tz('America/New_York').utcOffset())
|
||||
})
|
||||
|
||||
describe('Timezone Label Integration', () => {
|
||||
test('should not display timezone label by default', () => {
|
||||
render(
|
||||
<TimePicker
|
||||
{...baseProps}
|
||||
value="12:00 AM"
|
||||
timezone="Asia/Shanghai"
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.queryByTestId('timezone-label')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
test('should not display timezone label when showTimezone is false', () => {
|
||||
render(
|
||||
<TimePicker
|
||||
{...baseProps}
|
||||
value="12:00 AM"
|
||||
timezone="Asia/Shanghai"
|
||||
showTimezone={false}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.queryByTestId('timezone-label')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
test('should display timezone label when showTimezone is true', () => {
|
||||
render(
|
||||
<TimePicker
|
||||
{...baseProps}
|
||||
value="12:00 AM"
|
||||
timezone="Asia/Shanghai"
|
||||
showTimezone={true}
|
||||
/>,
|
||||
)
|
||||
|
||||
const timezoneLabel = screen.getByTestId('timezone-label')
|
||||
expect(timezoneLabel).toBeInTheDocument()
|
||||
expect(timezoneLabel).toHaveAttribute('data-timezone', 'Asia/Shanghai')
|
||||
})
|
||||
|
||||
test('should pass inline prop to timezone label', () => {
|
||||
render(
|
||||
<TimePicker
|
||||
{...baseProps}
|
||||
value="12:00 AM"
|
||||
timezone="America/New_York"
|
||||
showTimezone={true}
|
||||
/>,
|
||||
)
|
||||
|
||||
const timezoneLabel = screen.getByTestId('timezone-label')
|
||||
expect(timezoneLabel).toHaveAttribute('data-inline', 'true')
|
||||
})
|
||||
|
||||
test('should not display timezone label when showTimezone is true but timezone is not provided', () => {
|
||||
render(
|
||||
<TimePicker
|
||||
{...baseProps}
|
||||
value="12:00 AM"
|
||||
showTimezone={true}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.queryByTestId('timezone-label')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
test('should apply shrink-0 and text-xs classes to timezone label', () => {
|
||||
render(
|
||||
<TimePicker
|
||||
{...baseProps}
|
||||
value="12:00 AM"
|
||||
timezone="Europe/London"
|
||||
showTimezone={true}
|
||||
/>,
|
||||
)
|
||||
|
||||
const timezoneLabel = screen.getByTestId('timezone-label')
|
||||
expect(timezoneLabel).toHaveClass('shrink-0', 'text-xs')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -19,6 +19,7 @@ import Header from './header'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiCloseCircleFill, RiTimeLine } from '@remixicon/react'
|
||||
import cn from '@/utils/classnames'
|
||||
import TimezoneLabel from '@/app/components/base/timezone-label'
|
||||
|
||||
const to24Hour = (hour12: string, period: Period) => {
|
||||
const normalized = Number.parseInt(hour12, 10) % 12
|
||||
@@ -35,6 +36,10 @@ const TimePicker = ({
|
||||
title,
|
||||
minuteFilter,
|
||||
popupClassName,
|
||||
notClearable = false,
|
||||
triggerFullWidth = false,
|
||||
showTimezone = false,
|
||||
placement = 'bottom-start',
|
||||
}: TimePickerProps) => {
|
||||
const { t } = useTranslation()
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
@@ -189,7 +194,7 @@ const TimePicker = ({
|
||||
|
||||
const inputElem = (
|
||||
<input
|
||||
className='system-xs-regular flex-1 cursor-pointer appearance-none truncate bg-transparent p-1
|
||||
className='system-xs-regular flex-1 cursor-pointer select-none appearance-none truncate bg-transparent p-1
|
||||
text-components-input-text-filled outline-none placeholder:text-components-input-text-placeholder'
|
||||
readOnly
|
||||
value={isOpen ? '' : displayValue}
|
||||
@@ -200,28 +205,34 @@ const TimePicker = ({
|
||||
<PortalToFollowElem
|
||||
open={isOpen}
|
||||
onOpenChange={setIsOpen}
|
||||
placement='bottom-end'
|
||||
placement={placement}
|
||||
>
|
||||
<PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemTrigger className={triggerFullWidth ? '!block w-full' : undefined}>
|
||||
{renderTrigger ? (renderTrigger({
|
||||
inputElem,
|
||||
onClick: handleClickTrigger,
|
||||
isOpen,
|
||||
})) : (
|
||||
<div
|
||||
className='group flex w-[252px] cursor-pointer items-center gap-x-0.5 rounded-lg bg-components-input-bg-normal px-2 py-1 hover:bg-state-base-hover-alt'
|
||||
className={cn(
|
||||
'group flex cursor-pointer items-center gap-x-0.5 rounded-lg bg-components-input-bg-normal px-2 py-1 hover:bg-state-base-hover-alt',
|
||||
triggerFullWidth ? 'w-full min-w-0' : 'w-[252px]',
|
||||
)}
|
||||
onClick={handleClickTrigger}
|
||||
>
|
||||
{inputElem}
|
||||
{showTimezone && timezone && (
|
||||
<TimezoneLabel timezone={timezone} inline className='shrink-0 select-none text-xs' />
|
||||
)}
|
||||
<RiTimeLine className={cn(
|
||||
'h-4 w-4 shrink-0 text-text-quaternary',
|
||||
isOpen ? 'text-text-secondary' : 'group-hover:text-text-secondary',
|
||||
(displayValue || (isOpen && selectedTime)) && 'group-hover:hidden',
|
||||
(displayValue || (isOpen && selectedTime)) && !notClearable && 'group-hover:hidden',
|
||||
)} />
|
||||
<RiCloseCircleFill
|
||||
className={cn(
|
||||
'hidden h-4 w-4 shrink-0 text-text-quaternary',
|
||||
(displayValue || (isOpen && selectedTime)) && 'hover:text-text-secondary group-hover:inline-block',
|
||||
(displayValue || (isOpen && selectedTime)) && !notClearable && 'hover:text-text-secondary group-hover:inline-block',
|
||||
)}
|
||||
role='button'
|
||||
aria-label={t('common.operation.clear')}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Dayjs } from 'dayjs'
|
||||
import type { Placement } from '@floating-ui/react'
|
||||
|
||||
export enum ViewType {
|
||||
date = 'date',
|
||||
@@ -65,6 +66,10 @@ export type TimePickerProps = {
|
||||
title?: string
|
||||
minuteFilter?: (minutes: string[]) => string[]
|
||||
popupClassName?: string
|
||||
notClearable?: boolean
|
||||
triggerFullWidth?: boolean
|
||||
showTimezone?: boolean
|
||||
placement?: Placement
|
||||
}
|
||||
|
||||
export type TimePickerFooterProps = {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import dayjs from './dayjs'
|
||||
import {
|
||||
convertTimezoneToOffsetStr,
|
||||
getDateWithTimezone,
|
||||
isDayjsObject,
|
||||
toDayjs,
|
||||
@@ -65,3 +66,50 @@ describe('dayjs utilities', () => {
|
||||
expect(result?.minute()).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('convertTimezoneToOffsetStr', () => {
|
||||
test('should return default UTC+0 for undefined timezone', () => {
|
||||
expect(convertTimezoneToOffsetStr(undefined)).toBe('UTC+0')
|
||||
})
|
||||
|
||||
test('should return default UTC+0 for invalid timezone', () => {
|
||||
expect(convertTimezoneToOffsetStr('Invalid/Timezone')).toBe('UTC+0')
|
||||
})
|
||||
|
||||
test('should handle whole hour positive offsets without leading zeros', () => {
|
||||
expect(convertTimezoneToOffsetStr('Asia/Shanghai')).toBe('UTC+8')
|
||||
expect(convertTimezoneToOffsetStr('Pacific/Auckland')).toBe('UTC+12')
|
||||
expect(convertTimezoneToOffsetStr('Pacific/Apia')).toBe('UTC+13')
|
||||
})
|
||||
|
||||
test('should handle whole hour negative offsets without leading zeros', () => {
|
||||
expect(convertTimezoneToOffsetStr('Pacific/Niue')).toBe('UTC-11')
|
||||
expect(convertTimezoneToOffsetStr('Pacific/Honolulu')).toBe('UTC-10')
|
||||
expect(convertTimezoneToOffsetStr('America/New_York')).toBe('UTC-5')
|
||||
})
|
||||
|
||||
test('should handle zero offset', () => {
|
||||
expect(convertTimezoneToOffsetStr('Europe/London')).toBe('UTC+0')
|
||||
expect(convertTimezoneToOffsetStr('UTC')).toBe('UTC+0')
|
||||
})
|
||||
|
||||
test('should handle half-hour offsets (30 minutes)', () => {
|
||||
// India Standard Time: UTC+5:30
|
||||
expect(convertTimezoneToOffsetStr('Asia/Kolkata')).toBe('UTC+5:30')
|
||||
// Australian Central Time: UTC+9:30
|
||||
expect(convertTimezoneToOffsetStr('Australia/Adelaide')).toBe('UTC+9:30')
|
||||
expect(convertTimezoneToOffsetStr('Australia/Darwin')).toBe('UTC+9:30')
|
||||
})
|
||||
|
||||
test('should handle 45-minute offsets', () => {
|
||||
// Chatham Time: UTC+12:45
|
||||
expect(convertTimezoneToOffsetStr('Pacific/Chatham')).toBe('UTC+12:45')
|
||||
})
|
||||
|
||||
test('should preserve leading zeros in minute part for non-zero minutes', () => {
|
||||
// Ensure +05:30 is displayed as "UTC+5:30", not "UTC+5:3"
|
||||
const result = convertTimezoneToOffsetStr('Asia/Kolkata')
|
||||
expect(result).toMatch(/UTC[+-]\d+:30/)
|
||||
expect(result).not.toMatch(/UTC[+-]\d+:3[^0]/)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -107,7 +107,18 @@ export const convertTimezoneToOffsetStr = (timezone?: string) => {
|
||||
const tzItem = tz.find(item => item.value === timezone)
|
||||
if (!tzItem)
|
||||
return DEFAULT_OFFSET_STR
|
||||
return `UTC${tzItem.name.charAt(0)}${tzItem.name.charAt(2)}`
|
||||
// Extract offset from name format like "-11:00 Niue Time" or "+05:30 India Time"
|
||||
// Name format is always "{offset}:{minutes} {timezone name}"
|
||||
const offsetMatch = tzItem.name.match(/^([+-]?\d{1,2}):(\d{2})/)
|
||||
if (!offsetMatch)
|
||||
return DEFAULT_OFFSET_STR
|
||||
// Parse hours and minutes separately
|
||||
const hours = Number.parseInt(offsetMatch[1], 10)
|
||||
const minutes = Number.parseInt(offsetMatch[2], 10)
|
||||
const sign = hours >= 0 ? '+' : ''
|
||||
// If minutes are non-zero, include them in the output (e.g., "UTC+5:30")
|
||||
// Otherwise, only show hours (e.g., "UTC+8")
|
||||
return minutes !== 0 ? `UTC${sign}${hours}:${offsetMatch[2]}` : `UTC${sign}${hours}`
|
||||
}
|
||||
|
||||
export const isDayjsObject = (value: unknown): value is Dayjs => dayjs.isDayjs(value)
|
||||
|
||||
@@ -29,7 +29,7 @@ export type DividerProps = {
|
||||
|
||||
const Divider: FC<DividerProps> = ({ type, bgStyle, className = '', style }) => {
|
||||
return (
|
||||
<div className={classNames(dividerVariants({ type, bgStyle }), className)} style={style}></div>
|
||||
<div className={classNames(dividerVariants({ type, bgStyle }), 'shrink-0', className)} style={style}></div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ export type IDrawerProps = {
|
||||
description?: string
|
||||
dialogClassName?: string
|
||||
dialogBackdropClassName?: string
|
||||
containerClassName?: string
|
||||
panelClassName?: string
|
||||
children: React.ReactNode
|
||||
footer?: React.ReactNode
|
||||
@@ -22,6 +23,7 @@ export type IDrawerProps = {
|
||||
onCancel?: () => void
|
||||
onOk?: () => void
|
||||
unmount?: boolean
|
||||
noOverlay?: boolean
|
||||
}
|
||||
|
||||
export default function Drawer({
|
||||
@@ -29,6 +31,7 @@ export default function Drawer({
|
||||
description = '',
|
||||
dialogClassName = '',
|
||||
dialogBackdropClassName = '',
|
||||
containerClassName = '',
|
||||
panelClassName = '',
|
||||
children,
|
||||
footer,
|
||||
@@ -41,6 +44,7 @@ export default function Drawer({
|
||||
onCancel,
|
||||
onOk,
|
||||
unmount = false,
|
||||
noOverlay = false,
|
||||
}: IDrawerProps) {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
@@ -53,15 +57,15 @@ export default function Drawer({
|
||||
}}
|
||||
className={cn('fixed inset-0 z-[30] overflow-y-auto', dialogClassName)}
|
||||
>
|
||||
<div className={cn('flex h-screen w-screen justify-end', positionCenter && '!justify-center')}>
|
||||
<div className={cn('flex h-screen w-screen justify-end', positionCenter && '!justify-center', containerClassName)}>
|
||||
{/* mask */}
|
||||
<DialogBackdrop
|
||||
{!noOverlay && <DialogBackdrop
|
||||
className={cn('fixed inset-0 z-[40]', mask && 'bg-black/30', dialogBackdropClassName)}
|
||||
onClick={() => {
|
||||
if (!clickOutsideNotOpen)
|
||||
onClose()
|
||||
}}
|
||||
/>
|
||||
/>}
|
||||
<div className={cn('relative z-[50] flex w-full max-w-sm flex-col justify-between overflow-hidden bg-components-panel-bg p-6 text-left align-middle shadow-xl', panelClassName)}>
|
||||
<>
|
||||
<div className='flex justify-between'>
|
||||
|
||||
30
web/app/components/base/encrypted-bottom/index.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import cn from '@/utils/classnames'
|
||||
import { RiLock2Fill } from '@remixicon/react'
|
||||
import Link from 'next/link'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
frontTextKey?: string
|
||||
backTextKey?: string
|
||||
}
|
||||
|
||||
export const EncryptedBottom = (props: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const { frontTextKey, backTextKey, className } = props
|
||||
|
||||
return (
|
||||
<div className={cn('system-xs-regular flex items-center justify-center rounded-b-2xl border-t-[0.5px] border-divider-subtle bg-background-soft px-2 py-3 text-text-tertiary', className)}>
|
||||
<RiLock2Fill className='mx-1 h-3 w-3 text-text-quaternary' />
|
||||
{t(frontTextKey || 'common.provider.encrypted.front')}
|
||||
<Link
|
||||
className='mx-1 text-text-accent'
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
href='https://pycryptodome.readthedocs.io/en/latest/src/cipher/oaep.html'
|
||||
>
|
||||
PKCS1_OAEP
|
||||
</Link>
|
||||
{t(backTextKey || 'common.provider.encrypted.back')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
273
web/app/components/base/error-boundary/index.tsx
Normal file
@@ -0,0 +1,273 @@
|
||||
'use client'
|
||||
import type { ErrorInfo, ReactNode } from 'react'
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { RiAlertLine, RiBugLine } from '@remixicon/react'
|
||||
import Button from '@/app/components/base/button'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type ErrorBoundaryState = {
|
||||
hasError: boolean
|
||||
error: Error | null
|
||||
errorInfo: ErrorInfo | null
|
||||
errorCount: number
|
||||
}
|
||||
|
||||
type ErrorBoundaryProps = {
|
||||
children: ReactNode
|
||||
fallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode)
|
||||
onError?: (error: Error, errorInfo: ErrorInfo) => void
|
||||
onReset?: () => void
|
||||
showDetails?: boolean
|
||||
className?: string
|
||||
resetKeys?: Array<string | number>
|
||||
resetOnPropsChange?: boolean
|
||||
isolate?: boolean
|
||||
enableRecovery?: boolean
|
||||
customTitle?: string
|
||||
customMessage?: string
|
||||
}
|
||||
|
||||
// Internal class component for error catching
|
||||
class ErrorBoundaryInner extends React.Component<
|
||||
ErrorBoundaryProps & {
|
||||
resetErrorBoundary: () => void
|
||||
onResetKeysChange: (prevResetKeys?: Array<string | number>) => void
|
||||
},
|
||||
ErrorBoundaryState
|
||||
> {
|
||||
constructor(props: any) {
|
||||
super(props)
|
||||
this.state = {
|
||||
hasError: false,
|
||||
error: null,
|
||||
errorInfo: null,
|
||||
errorCount: 0,
|
||||
}
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {
|
||||
return {
|
||||
hasError: true,
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.error('ErrorBoundary caught an error:', error)
|
||||
console.error('Error Info:', errorInfo)
|
||||
}
|
||||
|
||||
this.setState(prevState => ({
|
||||
errorInfo,
|
||||
errorCount: prevState.errorCount + 1,
|
||||
}))
|
||||
|
||||
if (this.props.onError)
|
||||
this.props.onError(error, errorInfo)
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: any) {
|
||||
const { resetKeys, resetOnPropsChange } = this.props
|
||||
const { hasError } = this.state
|
||||
|
||||
if (hasError && prevProps.resetKeys !== resetKeys) {
|
||||
if (resetKeys?.some((key, idx) => key !== prevProps.resetKeys?.[idx]))
|
||||
this.props.resetErrorBoundary()
|
||||
}
|
||||
|
||||
if (hasError && resetOnPropsChange && prevProps.children !== this.props.children)
|
||||
this.props.resetErrorBoundary()
|
||||
|
||||
if (prevProps.resetKeys !== resetKeys)
|
||||
this.props.onResetKeysChange(prevProps.resetKeys)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { hasError, error, errorInfo, errorCount } = this.state
|
||||
const {
|
||||
fallback,
|
||||
children,
|
||||
showDetails = false,
|
||||
className,
|
||||
isolate = true,
|
||||
enableRecovery = true,
|
||||
customTitle,
|
||||
customMessage,
|
||||
resetErrorBoundary,
|
||||
} = this.props
|
||||
|
||||
if (hasError && error) {
|
||||
if (fallback) {
|
||||
if (typeof fallback === 'function')
|
||||
return fallback(error, resetErrorBoundary)
|
||||
|
||||
return fallback
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'border-state-critical-border bg-state-critical-hover-alt flex flex-col items-center justify-center rounded-lg border p-8',
|
||||
isolate && 'min-h-[200px]',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className='mb-4 flex items-center gap-2'>
|
||||
<RiAlertLine className='text-state-critical-solid h-8 w-8' />
|
||||
<h2 className='text-xl font-semibold text-text-primary'>
|
||||
{customTitle || 'Something went wrong'}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<p className='mb-6 text-center text-text-secondary'>
|
||||
{customMessage || 'An unexpected error occurred while rendering this component.'}
|
||||
</p>
|
||||
|
||||
{showDetails && errorInfo && (
|
||||
<details className='mb-6 w-full max-w-2xl'>
|
||||
<summary className='mb-2 cursor-pointer text-sm font-medium text-text-tertiary hover:text-text-secondary'>
|
||||
<span className='inline-flex items-center gap-1'>
|
||||
<RiBugLine className='h-4 w-4' />
|
||||
Error Details (Development Only)
|
||||
</span>
|
||||
</summary>
|
||||
<div className='rounded-lg bg-gray-100 p-4'>
|
||||
<div className='mb-2'>
|
||||
<span className='font-mono text-xs font-semibold text-gray-600'>Error:</span>
|
||||
<pre className='mt-1 overflow-auto whitespace-pre-wrap font-mono text-xs text-gray-800'>
|
||||
{error.toString()}
|
||||
</pre>
|
||||
</div>
|
||||
{errorInfo && (
|
||||
<div>
|
||||
<span className='font-mono text-xs font-semibold text-gray-600'>Component Stack:</span>
|
||||
<pre className='mt-1 max-h-40 overflow-auto whitespace-pre-wrap font-mono text-xs text-gray-700'>
|
||||
{errorInfo.componentStack}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
{errorCount > 1 && (
|
||||
<div className='mt-2 text-xs text-gray-600'>
|
||||
This error has occurred {errorCount} times
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</details>
|
||||
)}
|
||||
|
||||
{enableRecovery && (
|
||||
<div className='flex gap-3'>
|
||||
<Button
|
||||
variant='primary'
|
||||
size='small'
|
||||
onClick={resetErrorBoundary}
|
||||
>
|
||||
Try Again
|
||||
</Button>
|
||||
<Button
|
||||
variant='secondary'
|
||||
size='small'
|
||||
onClick={() => window.location.reload()}
|
||||
>
|
||||
Reload Page
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return children
|
||||
}
|
||||
}
|
||||
|
||||
// Main functional component wrapper
|
||||
const ErrorBoundary: React.FC<ErrorBoundaryProps> = (props) => {
|
||||
const [errorBoundaryKey, setErrorBoundaryKey] = useState(0)
|
||||
const resetKeysRef = useRef(props.resetKeys)
|
||||
const prevResetKeysRef = useRef<Array<string | number> | undefined>(undefined)
|
||||
|
||||
const resetErrorBoundary = useCallback(() => {
|
||||
setErrorBoundaryKey(prev => prev + 1)
|
||||
props.onReset?.()
|
||||
}, [props])
|
||||
|
||||
const onResetKeysChange = useCallback((prevResetKeys?: Array<string | number>) => {
|
||||
prevResetKeysRef.current = prevResetKeys
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (prevResetKeysRef.current !== props.resetKeys)
|
||||
resetKeysRef.current = props.resetKeys
|
||||
}, [props.resetKeys])
|
||||
|
||||
return (
|
||||
<ErrorBoundaryInner
|
||||
{...props}
|
||||
key={errorBoundaryKey}
|
||||
resetErrorBoundary={resetErrorBoundary}
|
||||
onResetKeysChange={onResetKeysChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
// Hook for imperative error handling
|
||||
export function useErrorHandler() {
|
||||
const [error, setError] = useState<Error | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (error)
|
||||
throw error
|
||||
}, [error])
|
||||
|
||||
return setError
|
||||
}
|
||||
|
||||
// Hook for catching async errors
|
||||
export function useAsyncError() {
|
||||
const [, setError] = useState()
|
||||
|
||||
return useCallback(
|
||||
(error: Error) => {
|
||||
setError(() => {
|
||||
throw error
|
||||
})
|
||||
},
|
||||
[setError],
|
||||
)
|
||||
}
|
||||
|
||||
// HOC for wrapping components with error boundary
|
||||
export function withErrorBoundary<P extends object>(
|
||||
Component: React.ComponentType<P>,
|
||||
errorBoundaryProps?: Omit<ErrorBoundaryProps, 'children'>,
|
||||
): React.ComponentType<P> {
|
||||
const WrappedComponent = (props: P) => (
|
||||
<ErrorBoundary {...errorBoundaryProps}>
|
||||
<Component {...props} />
|
||||
</ErrorBoundary>
|
||||
)
|
||||
|
||||
WrappedComponent.displayName = `withErrorBoundary(${Component.displayName || Component.name || 'Component'})`
|
||||
|
||||
return WrappedComponent
|
||||
}
|
||||
|
||||
// Simple error fallback component
|
||||
export const ErrorFallback: React.FC<{
|
||||
error: Error
|
||||
resetErrorBoundary: () => void
|
||||
}> = ({ error, resetErrorBoundary }) => {
|
||||
return (
|
||||
<div className='flex min-h-[200px] flex-col items-center justify-center rounded-lg border border-red-200 bg-red-50 p-8'>
|
||||
<h2 className='mb-2 text-lg font-semibold text-red-800'>Oops! Something went wrong</h2>
|
||||
<p className='mb-4 text-center text-red-600'>{error.message}</p>
|
||||
<Button onClick={resetErrorBoundary} size='small'>
|
||||
Try again
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ErrorBoundary
|
||||
@@ -26,6 +26,7 @@ import { CustomConfigurationStatusEnum } from '@/app/components/header/account-s
|
||||
import cn from '@/utils/classnames'
|
||||
import { noop } from 'lodash-es'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
||||
|
||||
const systemTypes = ['openai_moderation', 'keywords', 'api']
|
||||
|
||||
@@ -55,7 +56,7 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
|
||||
const { setShowAccountSettingModal } = useModalContext()
|
||||
const handleOpenSettingsModal = () => {
|
||||
setShowAccountSettingModal({
|
||||
payload: 'provider',
|
||||
payload: ACCOUNT_SETTING_TAB.PROVIDER,
|
||||
onCancelCallback: () => {
|
||||
mutate()
|
||||
},
|
||||
|
||||
@@ -1,20 +1,71 @@
|
||||
import CheckboxList from '@/app/components/base/checkbox-list'
|
||||
import type { FieldState, FormSchema, TypeWithI18N } from '@/app/components/base/form/types'
|
||||
import { FormItemValidateStatusEnum, FormTypeEnum } from '@/app/components/base/form/types'
|
||||
import Input from '@/app/components/base/input'
|
||||
import Radio from '@/app/components/base/radio'
|
||||
import RadioE from '@/app/components/base/radio/ui'
|
||||
import PureSelect from '@/app/components/base/select/pure'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
||||
import { useTriggerPluginDynamicOptions } from '@/service/use-triggers'
|
||||
import cn from '@/utils/classnames'
|
||||
import { RiExternalLinkLine } from '@remixicon/react'
|
||||
import type { AnyFieldApi } from '@tanstack/react-form'
|
||||
import { useStore } from '@tanstack/react-form'
|
||||
import {
|
||||
isValidElement,
|
||||
memo,
|
||||
useCallback,
|
||||
useMemo,
|
||||
} from 'react'
|
||||
import { RiExternalLinkLine } from '@remixicon/react'
|
||||
import type { AnyFieldApi } from '@tanstack/react-form'
|
||||
import { useStore } from '@tanstack/react-form'
|
||||
import cn from '@/utils/classnames'
|
||||
import Input from '@/app/components/base/input'
|
||||
import PureSelect from '@/app/components/base/select/pure'
|
||||
import type { FormSchema } from '@/app/components/base/form/types'
|
||||
import { FormTypeEnum } from '@/app/components/base/form/types'
|
||||
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
||||
import Radio from '@/app/components/base/radio'
|
||||
import RadioE from '@/app/components/base/radio/ui'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const getExtraProps = (type: FormTypeEnum) => {
|
||||
switch (type) {
|
||||
case FormTypeEnum.secretInput:
|
||||
return { type: 'password', autoComplete: 'new-password' }
|
||||
case FormTypeEnum.textNumber:
|
||||
return { type: 'number' }
|
||||
default:
|
||||
return { type: 'text' }
|
||||
}
|
||||
}
|
||||
|
||||
const getTranslatedContent = ({ content, render }: {
|
||||
content: React.ReactNode | string | null | undefined | TypeWithI18N<string> | Record<string, string>
|
||||
render: (content: TypeWithI18N<string> | Record<string, string>) => string
|
||||
}): string => {
|
||||
if (isValidElement(content) || typeof content === 'string')
|
||||
return content as string
|
||||
|
||||
if (typeof content === 'object' && content !== null)
|
||||
return render(content as TypeWithI18N<string>)
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
const VALIDATE_STATUS_STYLE_MAP: Record<FormItemValidateStatusEnum, { componentClassName: string, textClassName: string, infoFieldName: string }> = {
|
||||
[FormItemValidateStatusEnum.Error]: {
|
||||
componentClassName: 'border-components-input-border-destructive focus:border-components-input-border-destructive',
|
||||
textClassName: 'text-text-destructive',
|
||||
infoFieldName: 'errors',
|
||||
},
|
||||
[FormItemValidateStatusEnum.Warning]: {
|
||||
componentClassName: 'border-components-input-border-warning focus:border-components-input-border-warning',
|
||||
textClassName: 'text-text-warning',
|
||||
infoFieldName: 'warnings',
|
||||
},
|
||||
[FormItemValidateStatusEnum.Success]: {
|
||||
componentClassName: '',
|
||||
textClassName: '',
|
||||
infoFieldName: '',
|
||||
},
|
||||
[FormItemValidateStatusEnum.Validating]: {
|
||||
componentClassName: '',
|
||||
textClassName: '',
|
||||
infoFieldName: '',
|
||||
},
|
||||
}
|
||||
|
||||
export type BaseFieldProps = {
|
||||
fieldClassName?: string
|
||||
@@ -25,7 +76,9 @@ export type BaseFieldProps = {
|
||||
field: AnyFieldApi
|
||||
disabled?: boolean
|
||||
onChange?: (field: string, value: any) => void
|
||||
fieldState?: FieldState
|
||||
}
|
||||
|
||||
const BaseField = ({
|
||||
fieldClassName,
|
||||
labelClassName,
|
||||
@@ -35,204 +88,259 @@ const BaseField = ({
|
||||
field,
|
||||
disabled: propsDisabled,
|
||||
onChange,
|
||||
fieldState,
|
||||
}: BaseFieldProps) => {
|
||||
const renderI18nObject = useRenderI18nObject()
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
name,
|
||||
label,
|
||||
required,
|
||||
placeholder,
|
||||
options,
|
||||
labelClassName: formLabelClassName,
|
||||
disabled: formSchemaDisabled,
|
||||
type: formItemType,
|
||||
dynamicSelectParams,
|
||||
multiple = false,
|
||||
tooltip,
|
||||
showCopy,
|
||||
description,
|
||||
url,
|
||||
help,
|
||||
} = formSchema
|
||||
const disabled = propsDisabled || formSchemaDisabled
|
||||
|
||||
const memorizedLabel = useMemo(() => {
|
||||
if (isValidElement(label))
|
||||
return label
|
||||
const [translatedLabel, translatedPlaceholder, translatedTooltip, translatedDescription, translatedHelp] = useMemo(() => {
|
||||
const results = [
|
||||
label,
|
||||
placeholder,
|
||||
tooltip,
|
||||
description,
|
||||
help,
|
||||
].map(v => getTranslatedContent({ content: v, render: renderI18nObject }))
|
||||
if (!results[1]) results[1] = t('common.placeholder.input')
|
||||
return results
|
||||
}, [label, placeholder, tooltip, description, help, renderI18nObject])
|
||||
|
||||
if (typeof label === 'string')
|
||||
return label
|
||||
const watchedVariables = useMemo(() => {
|
||||
const variables = new Set<string>()
|
||||
|
||||
if (typeof label === 'object' && label !== null)
|
||||
return renderI18nObject(label as Record<string, string>)
|
||||
}, [label, renderI18nObject])
|
||||
const memorizedPlaceholder = useMemo(() => {
|
||||
if (typeof placeholder === 'string')
|
||||
return placeholder
|
||||
for (const option of options || []) {
|
||||
for (const condition of option.show_on || [])
|
||||
variables.add(condition.variable)
|
||||
}
|
||||
|
||||
if (typeof placeholder === 'object' && placeholder !== null)
|
||||
return renderI18nObject(placeholder as Record<string, string>)
|
||||
}, [placeholder, renderI18nObject])
|
||||
const optionValues = useStore(field.form.store, (s) => {
|
||||
return Array.from(variables)
|
||||
}, [options])
|
||||
|
||||
const watchedValues = useStore(field.form.store, (s) => {
|
||||
const result: Record<string, any> = {}
|
||||
options?.forEach((option) => {
|
||||
if (option.show_on?.length) {
|
||||
option.show_on.forEach((condition) => {
|
||||
result[condition.variable] = s.values[condition.variable]
|
||||
})
|
||||
}
|
||||
})
|
||||
for (const variable of watchedVariables)
|
||||
result[variable] = s.values[variable]
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
const memorizedOptions = useMemo(() => {
|
||||
return options?.filter((option) => {
|
||||
if (!option.show_on || option.show_on.length === 0)
|
||||
if (!option.show_on?.length)
|
||||
return true
|
||||
|
||||
return option.show_on.every((condition) => {
|
||||
const conditionValue = optionValues[condition.variable]
|
||||
return conditionValue === condition.value
|
||||
return watchedValues[condition.variable] === condition.value
|
||||
})
|
||||
}).map((option) => {
|
||||
return {
|
||||
label: typeof option.label === 'string' ? option.label : renderI18nObject(option.label),
|
||||
label: getTranslatedContent({ content: option.label, render: renderI18nObject }),
|
||||
value: option.value,
|
||||
}
|
||||
}) || []
|
||||
}, [options, renderI18nObject, optionValues])
|
||||
}, [options, renderI18nObject, watchedValues])
|
||||
|
||||
const value = useStore(field.form.store, s => s.values[field.name])
|
||||
|
||||
const { data: dynamicOptionsData, isLoading: isDynamicOptionsLoading, error: dynamicOptionsError } = useTriggerPluginDynamicOptions(
|
||||
dynamicSelectParams || {
|
||||
plugin_id: '',
|
||||
provider: '',
|
||||
action: '',
|
||||
parameter: '',
|
||||
credential_id: '',
|
||||
},
|
||||
formItemType === FormTypeEnum.dynamicSelect,
|
||||
)
|
||||
|
||||
const dynamicOptions = useMemo(() => {
|
||||
if (!dynamicOptionsData?.options)
|
||||
return []
|
||||
return dynamicOptionsData.options.map(option => ({
|
||||
label: getTranslatedContent({ content: option.label, render: renderI18nObject }),
|
||||
value: option.value,
|
||||
}))
|
||||
}, [dynamicOptionsData, renderI18nObject])
|
||||
|
||||
const handleChange = useCallback((value: any) => {
|
||||
field.handleChange(value)
|
||||
onChange?.(field.name, value)
|
||||
}, [field, onChange])
|
||||
|
||||
return (
|
||||
<div className={cn(fieldClassName)}>
|
||||
<div className={cn(labelClassName, formLabelClassName)}>
|
||||
{memorizedLabel}
|
||||
{
|
||||
required && !isValidElement(label) && (
|
||||
<span className='ml-1 text-text-destructive-secondary'>*</span>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div className={cn(inputContainerClassName)}>
|
||||
{
|
||||
formSchema.type === FormTypeEnum.textInput && (
|
||||
<Input
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
className={cn(inputClassName)}
|
||||
value={value || ''}
|
||||
onChange={(e) => {
|
||||
handleChange(e.target.value)
|
||||
}}
|
||||
onBlur={field.handleBlur}
|
||||
disabled={disabled}
|
||||
placeholder={memorizedPlaceholder}
|
||||
<>
|
||||
<div className={cn(fieldClassName)}>
|
||||
<div className={cn(labelClassName, formLabelClassName)}>
|
||||
{translatedLabel}
|
||||
{
|
||||
required && !isValidElement(label) && (
|
||||
<span className='ml-1 text-text-destructive-secondary'>*</span>
|
||||
)
|
||||
}
|
||||
{tooltip && (
|
||||
<Tooltip
|
||||
popupContent={<div className='w-[200px]'>{translatedTooltip}</div>}
|
||||
triggerClassName='ml-0.5 w-4 h-4'
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
formSchema.type === FormTypeEnum.secretInput && (
|
||||
<Input
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
type='password'
|
||||
className={cn(inputClassName)}
|
||||
value={value || ''}
|
||||
onChange={e => handleChange(e.target.value)}
|
||||
onBlur={field.handleBlur}
|
||||
disabled={disabled}
|
||||
placeholder={memorizedPlaceholder}
|
||||
autoComplete={'new-password'}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
formSchema.type === FormTypeEnum.textNumber && (
|
||||
<Input
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
type='number'
|
||||
className={cn(inputClassName)}
|
||||
value={value || ''}
|
||||
onChange={e => handleChange(e.target.value)}
|
||||
onBlur={field.handleBlur}
|
||||
disabled={disabled}
|
||||
placeholder={memorizedPlaceholder}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
formSchema.type === FormTypeEnum.select && (
|
||||
<PureSelect
|
||||
value={value}
|
||||
onChange={v => handleChange(v)}
|
||||
disabled={disabled}
|
||||
placeholder={memorizedPlaceholder}
|
||||
options={memorizedOptions}
|
||||
triggerPopupSameWidth
|
||||
popupProps={{
|
||||
className: 'max-h-[320px] overflow-y-auto',
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
formSchema.type === FormTypeEnum.radio && (
|
||||
)}
|
||||
</div>
|
||||
<div className={cn(inputContainerClassName)}>
|
||||
{
|
||||
[FormTypeEnum.textInput, FormTypeEnum.secretInput, FormTypeEnum.textNumber].includes(formItemType) && (
|
||||
<Input
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
className={cn(inputClassName, VALIDATE_STATUS_STYLE_MAP[fieldState?.validateStatus as FormItemValidateStatusEnum]?.componentClassName)}
|
||||
value={value || ''}
|
||||
onChange={(e) => {
|
||||
handleChange(e.target.value)
|
||||
}}
|
||||
onBlur={field.handleBlur}
|
||||
disabled={disabled}
|
||||
placeholder={translatedPlaceholder}
|
||||
{...getExtraProps(formItemType)}
|
||||
showCopyIcon={showCopy}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
formItemType === FormTypeEnum.select && !multiple && (
|
||||
<PureSelect
|
||||
value={value}
|
||||
onChange={v => handleChange(v)}
|
||||
disabled={disabled}
|
||||
placeholder={translatedPlaceholder}
|
||||
options={memorizedOptions}
|
||||
triggerPopupSameWidth
|
||||
popupProps={{
|
||||
className: 'max-h-[320px] overflow-y-auto',
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
formItemType === FormTypeEnum.checkbox /* && multiple */ && (
|
||||
<CheckboxList
|
||||
title={name}
|
||||
value={value}
|
||||
onChange={v => field.handleChange(v)}
|
||||
options={memorizedOptions}
|
||||
maxHeight='200px'
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
formItemType === FormTypeEnum.dynamicSelect && (
|
||||
<PureSelect
|
||||
options={dynamicOptions}
|
||||
value={value}
|
||||
onChange={field.handleChange}
|
||||
disabled={disabled || isDynamicOptionsLoading}
|
||||
placeholder={
|
||||
isDynamicOptionsLoading
|
||||
? t('common.dynamicSelect.loading')
|
||||
: translatedPlaceholder
|
||||
}
|
||||
{...(dynamicOptionsError ? { popupProps: { title: t('common.dynamicSelect.error'), titleClassName: 'text-text-destructive-secondary' } }
|
||||
: (!dynamicOptions.length ? { popupProps: { title: t('common.dynamicSelect.noData') } } : {}))}
|
||||
triggerPopupSameWidth
|
||||
multiple={multiple}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
formItemType === FormTypeEnum.radio && (
|
||||
<div className={cn(
|
||||
memorizedOptions.length < 3 ? 'flex items-center space-x-2' : 'space-y-2',
|
||||
)}>
|
||||
{
|
||||
memorizedOptions.map(option => (
|
||||
<div
|
||||
key={option.value}
|
||||
className={cn(
|
||||
'system-sm-regular hover:bg-components-option-card-option-hover-bg hover:border-components-option-card-option-hover-border flex h-8 flex-[1] grow cursor-pointer items-center justify-center gap-2 rounded-lg border border-components-option-card-option-border bg-components-option-card-option-bg p-2 text-text-secondary',
|
||||
value === option.value && 'border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg text-text-primary shadow-xs',
|
||||
disabled && 'cursor-not-allowed opacity-50',
|
||||
inputClassName,
|
||||
)}
|
||||
onClick={() => !disabled && handleChange(option.value)}
|
||||
>
|
||||
{
|
||||
formSchema.showRadioUI && (
|
||||
<RadioE
|
||||
className='mr-2'
|
||||
isChecked={value === option.value}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{option.label}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
formItemType === FormTypeEnum.boolean && (
|
||||
<Radio.Group
|
||||
className='flex w-fit items-center'
|
||||
value={value}
|
||||
onChange={v => field.handleChange(v)}
|
||||
>
|
||||
<Radio value={true} className='!mr-1'>True</Radio>
|
||||
<Radio value={false}>False</Radio>
|
||||
</Radio.Group>
|
||||
)
|
||||
}
|
||||
{fieldState?.validateStatus && [FormItemValidateStatusEnum.Error, FormItemValidateStatusEnum.Warning].includes(fieldState?.validateStatus) && (
|
||||
<div className={cn(
|
||||
memorizedOptions.length < 3 ? 'flex items-center space-x-2' : 'space-y-2',
|
||||
'system-xs-regular mt-1 px-0 py-[2px]',
|
||||
VALIDATE_STATUS_STYLE_MAP[fieldState?.validateStatus].textClassName,
|
||||
)}>
|
||||
{
|
||||
memorizedOptions.map(option => (
|
||||
<div
|
||||
key={option.value}
|
||||
className={cn(
|
||||
'system-sm-regular hover:bg-components-option-card-option-hover-bg hover:border-components-option-card-option-hover-border flex h-8 flex-[1] grow cursor-pointer items-center justify-center rounded-lg border border-components-option-card-option-border bg-components-option-card-option-bg p-2 text-text-secondary',
|
||||
value === option.value && 'border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg text-text-primary shadow-xs',
|
||||
disabled && 'cursor-not-allowed opacity-50',
|
||||
inputClassName,
|
||||
)}
|
||||
onClick={() => !disabled && handleChange(option.value)}
|
||||
>
|
||||
{
|
||||
formSchema.showRadioUI && (
|
||||
<RadioE
|
||||
className='mr-2'
|
||||
isChecked={value === option.value}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{option.label}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
{fieldState?.[VALIDATE_STATUS_STYLE_MAP[fieldState?.validateStatus].infoFieldName as keyof FieldState]}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
formSchema.type === FormTypeEnum.boolean && (
|
||||
<Radio.Group
|
||||
className='flex w-fit items-center'
|
||||
value={value}
|
||||
onChange={v => field.handleChange(v)}
|
||||
>
|
||||
<Radio value={true} className='!mr-1'>True</Radio>
|
||||
<Radio value={false}>False</Radio>
|
||||
</Radio.Group>
|
||||
)
|
||||
}
|
||||
{
|
||||
formSchema.url && (
|
||||
<a
|
||||
className='system-xs-regular mt-4 flex items-center text-text-accent'
|
||||
href={formSchema?.url}
|
||||
target='_blank'
|
||||
>
|
||||
<span className='break-all'>
|
||||
{renderI18nObject(formSchema?.help as any)}
|
||||
</span>
|
||||
{
|
||||
<RiExternalLinkLine className='ml-1 h-3 w-3' />
|
||||
}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{description && (
|
||||
<div className='system-xs-regular mt-4 text-text-tertiary'>
|
||||
{translatedDescription}
|
||||
</div>
|
||||
)}
|
||||
{
|
||||
url && (
|
||||
<a
|
||||
className='system-xs-regular mt-4 flex items-center text-text-accent'
|
||||
href={url}
|
||||
target='_blank'
|
||||
>
|
||||
<span className='break-all'>
|
||||
{translatedHelp}
|
||||
</span>
|
||||
<RiExternalLinkLine className='ml-1 h-3 w-3 shrink-0' />
|
||||
</a>
|
||||
)
|
||||
}
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
useCallback,
|
||||
useImperativeHandle,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import type {
|
||||
AnyFieldApi,
|
||||
@@ -12,9 +13,12 @@ import {
|
||||
useForm,
|
||||
useStore,
|
||||
} from '@tanstack/react-form'
|
||||
import type {
|
||||
FormRef,
|
||||
FormSchema,
|
||||
import {
|
||||
type FieldState,
|
||||
FormItemValidateStatusEnum,
|
||||
type FormRef,
|
||||
type FormSchema,
|
||||
type SetFieldsParam,
|
||||
} from '@/app/components/base/form/types'
|
||||
import {
|
||||
BaseField,
|
||||
@@ -36,6 +40,8 @@ export type BaseFormProps = {
|
||||
disabled?: boolean
|
||||
formFromProps?: AnyFormApi
|
||||
onChange?: (field: string, value: any) => void
|
||||
onSubmit?: (e: React.FormEvent<HTMLFormElement>) => void
|
||||
preventDefaultSubmit?: boolean
|
||||
} & Pick<BaseFieldProps, 'fieldClassName' | 'labelClassName' | 'inputContainerClassName' | 'inputClassName'>
|
||||
|
||||
const BaseForm = ({
|
||||
@@ -50,6 +56,8 @@ const BaseForm = ({
|
||||
disabled,
|
||||
formFromProps,
|
||||
onChange,
|
||||
onSubmit,
|
||||
preventDefaultSubmit = false,
|
||||
}: BaseFormProps) => {
|
||||
const initialDefaultValues = useMemo(() => {
|
||||
if (defaultValues)
|
||||
@@ -68,6 +76,8 @@ const BaseForm = ({
|
||||
const { getFormValues } = useGetFormValues(form, formSchemas)
|
||||
const { getValidators } = useGetValidators()
|
||||
|
||||
const [fieldStates, setFieldStates] = useState<Record<string, FieldState>>({})
|
||||
|
||||
const showOnValues = useStore(form.store, (s: any) => {
|
||||
const result: Record<string, any> = {}
|
||||
formSchemas.forEach((schema) => {
|
||||
@@ -81,6 +91,34 @@ const BaseForm = ({
|
||||
return result
|
||||
})
|
||||
|
||||
const setFields = useCallback((fields: SetFieldsParam[]) => {
|
||||
const newFieldStates: Record<string, FieldState> = { ...fieldStates }
|
||||
|
||||
for (const field of fields) {
|
||||
const { name, value, errors, warnings, validateStatus, help } = field
|
||||
|
||||
if (value !== undefined)
|
||||
form.setFieldValue(name, value)
|
||||
|
||||
let finalValidateStatus = validateStatus
|
||||
if (!finalValidateStatus) {
|
||||
if (errors && errors.length > 0)
|
||||
finalValidateStatus = FormItemValidateStatusEnum.Error
|
||||
else if (warnings && warnings.length > 0)
|
||||
finalValidateStatus = FormItemValidateStatusEnum.Warning
|
||||
}
|
||||
|
||||
newFieldStates[name] = {
|
||||
validateStatus: finalValidateStatus,
|
||||
help,
|
||||
errors,
|
||||
warnings,
|
||||
}
|
||||
}
|
||||
|
||||
setFieldStates(newFieldStates)
|
||||
}, [form, fieldStates])
|
||||
|
||||
useImperativeHandle(ref, () => {
|
||||
return {
|
||||
getForm() {
|
||||
@@ -89,8 +127,9 @@ const BaseForm = ({
|
||||
getFormValues: (option) => {
|
||||
return getFormValues(option)
|
||||
},
|
||||
setFields,
|
||||
}
|
||||
}, [form, getFormValues])
|
||||
}, [form, getFormValues, setFields])
|
||||
|
||||
const renderField = useCallback((field: AnyFieldApi) => {
|
||||
const formSchema = formSchemas?.find(schema => schema.name === field.name)
|
||||
@@ -100,18 +139,19 @@ const BaseForm = ({
|
||||
<BaseField
|
||||
field={field}
|
||||
formSchema={formSchema}
|
||||
fieldClassName={fieldClassName}
|
||||
labelClassName={labelClassName}
|
||||
fieldClassName={fieldClassName ?? formSchema.fieldClassName}
|
||||
labelClassName={labelClassName ?? formSchema.labelClassName}
|
||||
inputContainerClassName={inputContainerClassName}
|
||||
inputClassName={inputClassName}
|
||||
disabled={disabled}
|
||||
onChange={onChange}
|
||||
fieldState={fieldStates[field.name]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}, [formSchemas, fieldClassName, labelClassName, inputContainerClassName, inputClassName, disabled, onChange])
|
||||
}, [formSchemas, fieldClassName, labelClassName, inputContainerClassName, inputClassName, disabled, onChange, fieldStates])
|
||||
|
||||
const renderFieldWrapper = useCallback((formSchema: FormSchema) => {
|
||||
const validators = getValidators(formSchema)
|
||||
@@ -142,9 +182,18 @@ const BaseForm = ({
|
||||
if (!formSchemas?.length)
|
||||
return null
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
if (preventDefaultSubmit) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}
|
||||
onSubmit?.(e)
|
||||
}
|
||||
|
||||
return (
|
||||
<form
|
||||
className={cn(formClassName)}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{formSchemas.map(renderFieldWrapper)}
|
||||
</form>
|
||||
|
||||
@@ -11,7 +11,9 @@ type SelectFieldProps = {
|
||||
options: Option[]
|
||||
onChange?: (value: string) => void
|
||||
className?: string
|
||||
} & Omit<PureSelectProps, 'options' | 'value' | 'onChange'>
|
||||
} & Omit<PureSelectProps, 'options' | 'value' | 'onChange' | 'multiple'> & {
|
||||
multiple?: false
|
||||
}
|
||||
|
||||
const SelectField = ({
|
||||
label,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ChangeEvent } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { RiEditLine } from '@remixicon/react'
|
||||
import cn from '@/utils/classnames'
|
||||
import SegmentedControl from '@/app/components/base/segmented-control'
|
||||
@@ -33,9 +33,9 @@ const VariableOrConstantInputField = ({
|
||||
},
|
||||
]
|
||||
|
||||
const handleVariableOrConstantChange = (value: string) => {
|
||||
const handleVariableOrConstantChange = useCallback((value: string) => {
|
||||
setVariableType(value)
|
||||
}
|
||||
}, [setVariableType])
|
||||
|
||||
const handleVariableValueChange = () => {
|
||||
console.log('Variable value changed')
|
||||
|
||||
@@ -12,7 +12,7 @@ export const useGetFormValues = (form: AnyFormApi, formSchemas: FormSchema[]) =>
|
||||
|
||||
const getFormValues = useCallback((
|
||||
{
|
||||
needCheckValidatedValues,
|
||||
needCheckValidatedValues = true,
|
||||
needTransformWhenSecretFieldIsPristine,
|
||||
}: GetValuesOptions,
|
||||
) => {
|
||||
@@ -20,7 +20,7 @@ export const useGetFormValues = (form: AnyFormApi, formSchemas: FormSchema[]) =>
|
||||
if (!needCheckValidatedValues) {
|
||||
return {
|
||||
values,
|
||||
isCheckValidated: false,
|
||||
isCheckValidated: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -102,14 +102,14 @@ const FormPlayground = () => {
|
||||
options={{
|
||||
...demoFormOpts,
|
||||
validators: {
|
||||
onSubmit: ({ value }) => {
|
||||
const result = UserSchema.safeParse(value as typeof demoFormOpts.defaultValues)
|
||||
onSubmit: ({ value: formValue }) => {
|
||||
const result = UserSchema.safeParse(formValue as typeof demoFormOpts.defaultValues)
|
||||
if (!result.success)
|
||||
return result.error.issues[0].message
|
||||
return undefined
|
||||
},
|
||||
},
|
||||
onSubmit: ({ value }) => {
|
||||
onSubmit: () => {
|
||||
setStatus('Successfully saved profile.')
|
||||
},
|
||||
}}
|
||||
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
AnyFormApi,
|
||||
FieldValidators,
|
||||
} from '@tanstack/react-form'
|
||||
import type { Locale } from '@/i18n-config'
|
||||
|
||||
export type TypeWithI18N<T = string> = {
|
||||
en_US: T
|
||||
@@ -36,7 +37,7 @@ export enum FormTypeEnum {
|
||||
}
|
||||
|
||||
export type FormOption = {
|
||||
label: TypeWithI18N | string
|
||||
label: string | TypeWithI18N | Record<Locale, string>
|
||||
value: string
|
||||
show_on?: FormShowOnObject[]
|
||||
icon?: string
|
||||
@@ -44,23 +45,41 @@ export type FormOption = {
|
||||
|
||||
export type AnyValidators = FieldValidators<any, any, any, any, any, any, any, any, any, any, any, any>
|
||||
|
||||
export enum FormItemValidateStatusEnum {
|
||||
Success = 'success',
|
||||
Warning = 'warning',
|
||||
Error = 'error',
|
||||
Validating = 'validating',
|
||||
}
|
||||
|
||||
export type FormSchema = {
|
||||
type: FormTypeEnum
|
||||
name: string
|
||||
label: string | ReactNode | TypeWithI18N
|
||||
label: string | ReactNode | TypeWithI18N | Record<Locale, string>
|
||||
required: boolean
|
||||
multiple?: boolean
|
||||
default?: any
|
||||
tooltip?: string | TypeWithI18N
|
||||
description?: string | TypeWithI18N | Record<Locale, string>
|
||||
tooltip?: string | TypeWithI18N | Record<Locale, string>
|
||||
show_on?: FormShowOnObject[]
|
||||
url?: string
|
||||
scope?: string
|
||||
help?: string | TypeWithI18N
|
||||
placeholder?: string | TypeWithI18N
|
||||
help?: string | TypeWithI18N | Record<Locale, string>
|
||||
placeholder?: string | TypeWithI18N | Record<Locale, string>
|
||||
options?: FormOption[]
|
||||
labelClassName?: string
|
||||
fieldClassName?: string
|
||||
validators?: AnyValidators
|
||||
showRadioUI?: boolean
|
||||
disabled?: boolean
|
||||
showCopy?: boolean
|
||||
dynamicSelectParams?: {
|
||||
plugin_id: string
|
||||
provider: string
|
||||
action: string
|
||||
parameter: string
|
||||
credential_id: string
|
||||
}
|
||||
}
|
||||
|
||||
export type FormValues = Record<string, any>
|
||||
@@ -69,11 +88,25 @@ export type GetValuesOptions = {
|
||||
needTransformWhenSecretFieldIsPristine?: boolean
|
||||
needCheckValidatedValues?: boolean
|
||||
}
|
||||
|
||||
export type FieldState = {
|
||||
validateStatus?: FormItemValidateStatusEnum
|
||||
help?: string | ReactNode
|
||||
errors?: string[]
|
||||
warnings?: string[]
|
||||
}
|
||||
|
||||
export type SetFieldsParam = {
|
||||
name: string
|
||||
value?: any
|
||||
} & FieldState
|
||||
|
||||
export type FormRefObject = {
|
||||
getForm: () => AnyFormApi
|
||||
getFormValues: (obj: GetValuesOptions) => {
|
||||
values: Record<string, any>
|
||||
isCheckValidated: boolean
|
||||
}
|
||||
setFields: (fields: SetFieldsParam[]) => void
|
||||
}
|
||||
export type FormRef = ForwardedRef<FormRefObject>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.43295 1.50009L11.1961 9.7501C11.3342 9.98925 11.2523 10.295 11.0131 10.4331C10.9371 10.477 10.8509 10.5001 10.7631 10.5001H1.23682C0.960676 10.5001 0.736816 10.2762 0.736816 10.0001C0.736816 9.9123 0.759921 9.8261 0.803806 9.7501L5.56695 1.50009C5.705 1.26094 6.0108 1.179 6.24995 1.31707C6.32595 1.36096 6.3891 1.42408 6.43295 1.50009ZM5.49995 8.0001V9.0001H6.49995V8.0001H5.49995ZM5.49995 4.50008V7.0001H6.49995V4.50008H5.49995Z" fill="#F79009"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 564 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.43341 6.41661L6.30441 3.2876L7.12936 2.46265L11.6666 6.99994L7.12936 11.5372L6.30441 10.7122L9.43341 7.58327H2.33331V6.41661H9.43341Z" fill="#155AEF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 267 B |
@@ -0,0 +1,8 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.1499 6.35213L7.25146 6.38208L14.2248 9.03898L14.3172 9.08195C14.7224 9.30788 14.778 9.87906 14.424 10.179L14.342 10.2389L11.8172 11.817L10.2391 14.3417C9.96271 14.7839 9.32424 14.751 9.08219 14.317L9.03923 14.2245L6.38232 7.25122C6.18829 6.74188 6.64437 6.24196 7.1499 6.35213ZM9.81201 12.5084L10.7671 10.981L10.8114 10.9185C10.8589 10.8589 10.9163 10.8075 10.9813 10.7668L12.5086 9.81177L8.15251 8.15226L9.81201 12.5084Z" fill="#676F83"/>
|
||||
<path d="M5.2124 10.3977L3.56266 12.0474L2.61995 11.1047L4.26969 9.455L5.2124 10.3977Z" fill="#676F83"/>
|
||||
<path d="M3.66683 7.99992H1.3335V6.66659H3.66683V7.99992Z" fill="#676F83"/>
|
||||
<path d="M5.2124 4.2688L4.26969 5.21151L2.61995 3.56177L3.56266 2.61906L5.2124 4.2688Z" fill="#676F83"/>
|
||||
<path d="M12.0477 3.56177L10.3979 5.21151L9.45524 4.2688L11.105 2.61906L12.0477 3.56177Z" fill="#676F83"/>
|
||||
<path d="M8.00016 3.66659H6.66683V1.33325H8.00016V3.66659Z" fill="#676F83"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.00001 12.7761L12.1381 8.63804L11.1953 7.69524L8.00001 10.8905L4.80475 7.69524L3.86194 8.63804L8.00001 12.7761ZM8.00001 9.00951L12.1381 4.87146L11.1953 3.92865L8.00001 7.12391L4.80475 3.92865L3.86194 4.87146L8.00001 9.00951Z" fill="#676F83"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 357 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.02888 6.23572C5.08558 6.23572 4.56458 7.33027 5.15943 8.06239L7.13069 10.4885C7.57898 11.0403 8.42124 11.0403 8.86962 10.4885L10.8408 8.06239C11.4357 7.33027 10.9147 6.23572 9.97134 6.23572H6.02888Z" fill="#101828" fill-opacity="0.3"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 351 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 3.22388L3.86194 7.36193L4.80475 8.30473L8 5.10949L11.1953 8.30473L12.1381 7.36193L8 3.22388ZM8 6.99046L3.86194 11.1285L4.80475 12.0713L8 8.87606L11.1953 12.0713L12.1381 11.1285L8 6.99046Z" fill="#676F83"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 321 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.92578 11.0094C5.92578 10.0174 5.12163 9.21256 4.12956 9.21256C3.13752 9.2126 2.33333 10.0174 2.33333 11.0094C2.33349 12.0014 3.13762 12.8056 4.12956 12.8057C5.12153 12.8057 5.92562 12.0014 5.92578 11.0094ZM13.6667 11.0094C13.6667 10.0174 12.8625 9.2126 11.8704 9.21256C10.8784 9.21256 10.0742 10.0174 10.0742 11.0094C10.0744 12.0014 10.8785 12.8057 11.8704 12.8057C12.8624 12.8056 13.6665 12.0014 13.6667 11.0094ZM9.79622 4.32389C9.79619 3.33186 8.99205 2.52767 8 2.52767C7.00796 2.52767 6.20382 3.33186 6.20378 4.32389C6.20378 5.31596 7.00793 6.12012 8 6.12012C8.99207 6.12012 9.79622 5.31596 9.79622 4.32389ZM11.1296 4.32389C11.1296 5.82351 10.0748 7.07628 8.66667 7.38184V7.9196L9.74284 8.71387C10.3012 8.19607 11.0489 7.87923 11.8704 7.87923C13.5989 7.87927 15 9.28101 15 11.0094C14.9998 12.7377 13.5988 14.139 11.8704 14.139C10.1421 14.139 8.74104 12.7378 8.74089 11.0094C8.74089 10.5837 8.82585 10.1776 8.97982 9.80762L8 9.08366L7.01953 9.80762C7.17356 10.1777 7.25911 10.5836 7.25911 11.0094C7.25896 12.7378 5.85791 14.139 4.12956 14.139C2.40124 14.139 1.00016 12.7377 1 11.0094C1 9.28101 2.40114 7.87927 4.12956 7.87923C4.95094 7.87923 5.69819 8.19627 6.25651 8.71387L7.33333 7.9196V7.38184C5.92523 7.07628 4.87044 5.82351 4.87044 4.32389C4.87048 2.59548 6.27158 1.19434 8 1.19434C9.72843 1.19434 11.1295 2.59548 11.1296 4.32389Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.58325 1.75L7.58314 5.98908L11.2549 3.86982L11.8382 4.88018L8.16705 6.99942L11.8382 9.11983L11.2549 10.1302L7.58314 8.01033L7.58325 12.25H6.41659L6.41647 8.01033L2.74495 10.1302L2.16162 9.11983L5.83254 7L2.16162 4.88018L2.74495 3.86982L6.41647 5.98908L6.41659 1.75H7.58325Z" fill="#354052"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 406 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.24984 0.583252V1.74992H8.74984V0.583252H9.9165V1.74992H12.2498C12.572 1.74992 12.8332 2.01109 12.8332 2.33325V11.6666C12.8332 11.9888 12.572 12.2499 12.2498 12.2499H1.74984C1.42767 12.2499 1.1665 11.9888 1.1665 11.6666V2.33325C1.1665 2.01109 1.42767 1.74992 1.74984 1.74992H4.08317V0.583252H5.24984ZM11.6665 5.83325H2.33317V11.0833H11.6665V5.83325ZM8.77055 6.49592L9.5955 7.32093L6.70817 10.2083L4.64578 8.14588L5.47073 7.32093L6.70817 8.55835L8.77055 6.49592ZM4.08317 2.91659H2.33317V4.66659H11.6665V2.91659H9.9165V3.49992H8.74984V2.91659H5.24984V3.49992H4.08317V2.91659Z" fill="#354052"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 706 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.3333 9.33337C11.7015 9.33337 11.9999 9.63193 12 10V11.0573L12.8047 11.862L12.8503 11.9128C13.0638 12.1746 13.0487 12.5607 12.8047 12.8047C12.5606 13.0488 12.1746 13.0639 11.9128 12.8503L11.862 12.8047L10.862 11.8047C10.7371 11.6798 10.6667 11.5101 10.6667 11.3334V10C10.6668 9.63193 10.9652 9.33337 11.3333 9.33337Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.3333 7.33337C13.5425 7.33337 15.3333 9.12424 15.3333 11.3334C15.3333 13.5425 13.5425 15.3334 11.3333 15.3334C9.12419 15.3334 7.33333 13.5425 7.33333 11.3334C7.33333 9.12424 9.12419 7.33337 11.3333 7.33337ZM11.3333 8.66671C9.86057 8.66671 8.66667 9.86061 8.66667 11.3334C8.66667 12.8061 9.86057 14 11.3333 14C12.8061 14 14 12.8061 14 11.3334C14 9.86061 12.8061 8.66671 11.3333 8.66671Z" fill="white"/>
|
||||
<path d="M10.6667 1.33337C11.0349 1.33337 11.3333 1.63185 11.3333 2.00004V2.66671H12.6667C13.4031 2.66671 14 3.26367 14 4.00004V5.66671C14 6.0349 13.7015 6.33337 13.3333 6.33337C12.9651 6.33337 12.6667 6.0349 12.6667 5.66671V4.00004H3.33333V12.6667H5.66667C6.03486 12.6667 6.33333 12.9652 6.33333 13.3334C6.33333 13.7016 6.03486 14 5.66667 14H3.33333C2.59697 14 2 13.4031 2 12.6667V4.00004C2 3.26366 2.59696 2.66671 3.33333 2.66671H4.66667V2.00004C4.66667 1.63185 4.96514 1.33337 5.33333 1.33337C5.70152 1.33337 6 1.63185 6 2.00004V2.66671H10V2.00004C10 1.63185 10.2985 1.33337 10.6667 1.33337Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.34698 6.42505C5.10275 5.79268 5.67045 5.17005 6.29816 5.30916L6.42446 5.34758L13.0846 7.92049L13.1999 7.97518C13.7051 8.26089 13.7647 8.9802 13.3118 9.34432L13.207 9.41659L10.8196 10.8202L9.416 13.2076C9.08465 13.7711 8.28069 13.742 7.97459 13.2004L7.9199 13.0852L5.34698 6.42505ZM8.791 11.6392L9.73631 10.0325L9.73696 10.0318L9.7962 9.94458C9.86055 9.86164 9.94031 9.79125 10.0312 9.73755L10.0319 9.7369L11.6387 8.79159L6.99738 6.99797L8.791 11.6392Z" fill="white"/>
|
||||
<path d="M2.79751 8.9257C3.05781 8.66539 3.47985 8.66547 3.74021 8.9257C4.00057 9.18604 4.00056 9.60805 3.74021 9.86841L3.03318 10.5754C2.77283 10.8356 2.35078 10.8357 2.09047 10.5754C1.83032 10.3151 1.83033 9.89305 2.09047 9.63273L2.79751 8.9257Z" fill="white"/>
|
||||
<path d="M1.99998 5.66659C2.36817 5.66659 2.66665 5.96506 2.66665 6.33325C2.66665 6.70144 2.36817 6.99992 1.99998 6.99992H0.99998C0.63179 6.99992 0.333313 6.70144 0.333313 6.33325C0.333313 5.96506 0.63179 5.66659 0.99998 5.66659H1.99998Z" fill="white"/>
|
||||
<path d="M9.63279 2.09106C9.8931 1.83077 10.3151 1.83086 10.5755 2.09106C10.8358 2.35142 10.8359 2.77343 10.5755 3.03377L9.86847 3.7408C9.6081 4.00098 9.18605 4.0011 8.92576 3.7408C8.66559 3.4805 8.66562 3.05841 8.92576 2.7981L9.63279 2.09106Z" fill="white"/>
|
||||
<path d="M2.09113 2.09041C2.33521 1.84649 2.72126 1.83132 2.98305 2.04484L3.03383 2.09041L3.74087 2.79744L3.78644 2.84823C3.9999 3.11002 3.98476 3.49609 3.74087 3.74015C3.49682 3.9842 3.11079 3.9992 2.84894 3.78573L2.79816 3.74015L2.09113 3.03312L2.04555 2.98234C1.83199 2.72049 1.84705 2.33449 2.09113 2.09041Z" fill="white"/>
|
||||
<path d="M6.33331 0.333252C6.7015 0.333252 6.99998 0.631729 6.99998 0.999919V1.99992C6.99998 2.36811 6.7015 2.66659 6.33331 2.66659C5.96512 2.66659 5.66665 2.36811 5.66665 1.99992V0.999919C5.66665 0.631729 5.96512 0.333252 6.33331 0.333252Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.91246 9.42618C5.77036 9.66084 5.70006 9.85191 5.81358 10.1502C6.12696 10.9742 5.68488 11.776 4.85394 11.9937C4.07033 12.199 3.30686 11.684 3.15138 10.8451C3.01362 10.1025 3.58988 9.37451 4.40859 9.25851C4.45305 9.25211 4.49808 9.24938 4.55563 9.24591C4.58692 9.24404 4.62192 9.24191 4.66252 9.23884L5.90792 7.15051C5.12463 6.37166 4.65841 5.46114 4.7616 4.33295C4.83455 3.53543 5.14813 2.84626 5.72135 2.28138C6.81916 1.19968 8.49403 1.02449 9.78663 1.85479C11.0282 2.65232 11.5967 4.20582 11.112 5.53545L9.97403 5.22671C10.1263 4.48748 10.0137 3.82362 9.5151 3.25494C9.1857 2.87947 8.76303 2.68267 8.28236 2.61015C7.31883 2.46458 6.37278 3.08364 6.09207 4.02937C5.77342 5.10275 6.25566 5.97954 7.5735 6.64023C7.0207 7.56944 6.47235 8.50124 5.91246 9.42618ZM9.18916 5.51562C9.5877 6.2187 9.99236 6.93244 10.3934 7.63958C12.4206 7.01244 13.9491 8.13458 14.4974 9.33604C15.1597 10.7873 14.707 12.5062 13.4062 13.4016C12.0711 14.3207 10.3827 14.1636 9.19976 12.983L10.1279 12.2063C11.2962 12.963 12.3181 12.9274 13.0767 12.0314C13.7236 11.2669 13.7096 10.1271 13.0439 9.37871C12.2757 8.51511 11.2467 8.48878 10.0029 9.31784C9.48696 8.40251 8.96196 7.49424 8.46236 6.57234C8.2939 6.2616 8.10783 6.08135 7.72816 6.01558C7.09403 5.90564 6.68463 5.36109 6.66007 4.75099C6.63593 4.14763 6.99136 3.60224 7.54696 3.38974C8.0973 3.17924 8.74316 3.34916 9.11336 3.81707C9.4159 4.19938 9.51203 4.62966 9.35283 5.10116C9.32283 5.19018 9.28689 5.27727 9.2475 5.37261C9.22869 5.418 9.20916 5.46538 9.18916 5.51562ZM7.7013 11.2634H10.1417C10.1757 11.3087 10.2075 11.3536 10.2386 11.3973C10.3034 11.4887 10.3649 11.5755 10.4367 11.6526C10.9536 12.2052 11.8263 12.2326 12.3788 11.7197C12.9514 11.1881 12.9773 10.2951 12.4362 9.74011C11.9068 9.19704 11.0019 9.14518 10.5103 9.72018C10.2117 10.0696 9.9057 10.1107 9.50936 10.1045C8.49423 10.0888 7.47843 10.0994 6.46346 10.0994C6.52934 11.5273 5.98953 12.417 4.9189 12.6283C3.87051 12.8352 2.90496 12.3003 2.56502 11.3243C2.17891 10.2153 2.65641 9.32838 4.0361 8.62444C3.93228 8.24838 3.8274 7.86778 3.72357 7.49071C2.21981 7.81844 1.09162 9.27738 1.20809 10.9187C1.31097 12.3676 2.47975 13.6544 3.90909 13.8849C4.68542 14.0102 5.41485 13.88 6.09157 13.4962C6.96216 13.0022 7.46736 12.2254 7.7013 11.2634Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,5 @@
|
||||
.wrapper {
|
||||
display: inline-flex;
|
||||
background: url(~@/app/components/base/icons/assets/image/llm/baichuan-text-cn.png) center center no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import cn from '@/utils/classnames'
|
||||
import s from './BaichuanTextCn.module.css'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
className,
|
||||
...restProps
|
||||
}: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> & {
|
||||
ref?: React.RefObject<HTMLSpanElement>;
|
||||
},
|
||||
) => <span className={cn(s.wrapper, className)} {...restProps} ref={ref} />
|
||||
|
||||
Icon.displayName = 'BaichuanTextCn'
|
||||
|
||||
export default Icon
|
||||
@@ -0,0 +1,5 @@
|
||||
.wrapper {
|
||||
display: inline-flex;
|
||||
background: url(~@/app/components/base/icons/assets/image/llm/minimax.png) center center no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
20
web/app/components/base/icons/src/image/llm/Minimax.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import cn from '@/utils/classnames'
|
||||
import s from './Minimax.module.css'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
className,
|
||||
...restProps
|
||||
}: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> & {
|
||||
ref?: React.RefObject<HTMLSpanElement>;
|
||||
},
|
||||
) => <span className={cn(s.wrapper, className)} {...restProps} ref={ref} />
|
||||
|
||||
Icon.displayName = 'Minimax'
|
||||
|
||||
export default Icon
|
||||
@@ -0,0 +1,5 @@
|
||||
.wrapper {
|
||||
display: inline-flex;
|
||||
background: url(~@/app/components/base/icons/assets/image/llm/minimax-text.png) center center no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
20
web/app/components/base/icons/src/image/llm/MinimaxText.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import cn from '@/utils/classnames'
|
||||
import s from './MinimaxText.module.css'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
className,
|
||||
...restProps
|
||||
}: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> & {
|
||||
ref?: React.RefObject<HTMLSpanElement>;
|
||||
},
|
||||
) => <span className={cn(s.wrapper, className)} {...restProps} ref={ref} />
|
||||
|
||||
Icon.displayName = 'MinimaxText'
|
||||
|
||||
export default Icon
|
||||
@@ -0,0 +1,5 @@
|
||||
.wrapper {
|
||||
display: inline-flex;
|
||||
background: url(~@/app/components/base/icons/assets/image/llm/tongyi.png) center center no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
20
web/app/components/base/icons/src/image/llm/Tongyi.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import cn from '@/utils/classnames'
|
||||
import s from './Tongyi.module.css'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
className,
|
||||
...restProps
|
||||
}: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> & {
|
||||
ref?: React.RefObject<HTMLSpanElement>;
|
||||
},
|
||||
) => <span className={cn(s.wrapper, className)} {...restProps} ref={ref} />
|
||||
|
||||
Icon.displayName = 'Tongyi'
|
||||
|
||||
export default Icon
|
||||
@@ -0,0 +1,5 @@
|
||||
.wrapper {
|
||||
display: inline-flex;
|
||||
background: url(~@/app/components/base/icons/assets/image/llm/tongyi-text.png) center center no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
20
web/app/components/base/icons/src/image/llm/TongyiText.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import cn from '@/utils/classnames'
|
||||
import s from './TongyiText.module.css'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
className,
|
||||
...restProps
|
||||
}: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> & {
|
||||
ref?: React.RefObject<HTMLSpanElement>;
|
||||
},
|
||||
) => <span className={cn(s.wrapper, className)} {...restProps} ref={ref} />
|
||||
|
||||
Icon.displayName = 'TongyiText'
|
||||
|
||||
export default Icon
|
||||
@@ -0,0 +1,5 @@
|
||||
.wrapper {
|
||||
display: inline-flex;
|
||||
background: url(~@/app/components/base/icons/assets/image/llm/tongyi-text-cn.png) center center no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
20
web/app/components/base/icons/src/image/llm/TongyiTextCn.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import cn from '@/utils/classnames'
|
||||
import s from './TongyiTextCn.module.css'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
className,
|
||||
...restProps
|
||||
}: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> & {
|
||||
ref?: React.RefObject<HTMLSpanElement>;
|
||||
},
|
||||
) => <span className={cn(s.wrapper, className)} {...restProps} ref={ref} />
|
||||
|
||||
Icon.displayName = 'TongyiTextCn'
|
||||
|
||||
export default Icon
|
||||
@@ -0,0 +1,5 @@
|
||||
.wrapper {
|
||||
display: inline-flex;
|
||||
background: url(~@/app/components/base/icons/assets/image/llm/wxyy.png) center center no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
20
web/app/components/base/icons/src/image/llm/Wxyy.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import cn from '@/utils/classnames'
|
||||
import s from './Wxyy.module.css'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
className,
|
||||
...restProps
|
||||
}: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> & {
|
||||
ref?: React.RefObject<HTMLSpanElement>;
|
||||
},
|
||||
) => <span className={cn(s.wrapper, className)} {...restProps} ref={ref} />
|
||||
|
||||
Icon.displayName = 'Wxyy'
|
||||
|
||||
export default Icon
|
||||
@@ -0,0 +1,5 @@
|
||||
.wrapper {
|
||||
display: inline-flex;
|
||||
background: url(~@/app/components/base/icons/assets/image/llm/wxyy-text.png) center center no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
20
web/app/components/base/icons/src/image/llm/WxyyText.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import cn from '@/utils/classnames'
|
||||
import s from './WxyyText.module.css'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
className,
|
||||
...restProps
|
||||
}: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> & {
|
||||
ref?: React.RefObject<HTMLSpanElement>;
|
||||
},
|
||||
) => <span className={cn(s.wrapper, className)} {...restProps} ref={ref} />
|
||||
|
||||
Icon.displayName = 'WxyyText'
|
||||
|
||||
export default Icon
|
||||
@@ -0,0 +1,5 @@
|
||||
.wrapper {
|
||||
display: inline-flex;
|
||||
background: url(~@/app/components/base/icons/assets/image/llm/wxyy-text-cn.png) center center no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
20
web/app/components/base/icons/src/image/llm/WxyyTextCn.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import cn from '@/utils/classnames'
|
||||
import s from './WxyyTextCn.module.css'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
className,
|
||||
...restProps
|
||||
}: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> & {
|
||||
ref?: React.RefObject<HTMLSpanElement>;
|
||||
},
|
||||
) => <span className={cn(s.wrapper, className)} {...restProps} ref={ref} />
|
||||
|
||||
Icon.displayName = 'WxyyTextCn'
|
||||
|
||||
export default Icon
|
||||
9
web/app/components/base/icons/src/image/llm/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export { default as BaichuanTextCn } from './BaichuanTextCn'
|
||||
export { default as MinimaxText } from './MinimaxText'
|
||||
export { default as Minimax } from './Minimax'
|
||||
export { default as TongyiTextCn } from './TongyiTextCn'
|
||||
export { default as TongyiText } from './TongyiText'
|
||||
export { default as Tongyi } from './Tongyi'
|
||||
export { default as WxyyTextCn } from './WxyyTextCn'
|
||||
export { default as WxyyText } from './WxyyText'
|
||||
export { default as Wxyy } from './Wxyy'
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
20
web/app/components/base/icons/src/public/model/Checked.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Checked.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
Icon.displayName = 'Checked'
|
||||
|
||||
export default Icon
|
||||
1
web/app/components/base/icons/src/public/model/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as Checked } from './Checked'
|
||||
20
web/app/components/base/icons/src/public/plugins/Google.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Google.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
Icon.displayName = 'Google'
|
||||
|
||||
export default Icon
|
||||
@@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './WebReader.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
Icon.displayName = 'WebReader'
|
||||
|
||||
export default Icon
|
||||
@@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Wikipedia.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
Icon.displayName = 'Wikipedia'
|
||||
|
||||
export default Icon
|
||||
@@ -0,0 +1,7 @@
|
||||
export { default as Google } from './Google'
|
||||
export { default as PartnerDark } from './PartnerDark'
|
||||
export { default as PartnerLight } from './PartnerLight'
|
||||
export { default as VerifiedDark } from './VerifiedDark'
|
||||
export { default as VerifiedLight } from './VerifiedLight'
|
||||
export { default as WebReader } from './WebReader'
|
||||
export { default as Wikipedia } from './Wikipedia'
|
||||
@@ -18,4 +18,3 @@ const Icon = (
|
||||
Icon.displayName = 'DataSet'
|
||||
|
||||
export default Icon
|
||||
|
||||
|
||||
20
web/app/components/base/icons/src/public/thought/Loading.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Loading.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
Icon.displayName = 'Loading'
|
||||
|
||||
export default Icon
|
||||
20
web/app/components/base/icons/src/public/thought/Search.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Search.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
Icon.displayName = 'Search'
|
||||
|
||||
export default Icon
|
||||
@@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './ThoughtList.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
Icon.displayName = 'ThoughtList'
|
||||
|
||||
export default Icon
|
||||
@@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './WebReader.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
Icon.displayName = 'WebReader'
|
||||
|
||||
export default Icon
|
||||
@@ -1,2 +1,5 @@
|
||||
export { default as DataSet } from './DataSet'
|
||||
|
||||
export { default as Loading } from './Loading'
|
||||
export { default as Search } from './Search'
|
||||
export { default as ThoughtList } from './ThoughtList'
|
||||
export { default as WebReader } from './WebReader'
|
||||
|
||||
@@ -8,10 +8,10 @@ export { default as LangsmithIconBig } from './LangsmithIconBig'
|
||||
export { default as LangsmithIcon } from './LangsmithIcon'
|
||||
export { default as OpikIconBig } from './OpikIconBig'
|
||||
export { default as OpikIcon } from './OpikIcon'
|
||||
export { default as PhoenixIconBig } from './PhoenixIconBig'
|
||||
export { default as PhoenixIcon } from './PhoenixIcon'
|
||||
export { default as TencentIconBig } from './TencentIconBig'
|
||||
export { default as TencentIcon } from './TencentIcon'
|
||||
export { default as PhoenixIconBig } from './PhoenixIconBig'
|
||||
export { default as PhoenixIcon } from './PhoenixIcon'
|
||||
export { default as TracingIcon } from './TracingIcon'
|
||||
export { default as WeaveIconBig } from './WeaveIconBig'
|
||||
export { default as WeaveIcon } from './WeaveIcon'
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const Icon = (
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "12",
|
||||
"height": "12",
|
||||
"viewBox": "0 0 12 12",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M6.43295 1.50009L11.1961 9.7501C11.3342 9.98925 11.2523 10.295 11.0131 10.4331C10.9371 10.477 10.8509 10.5001 10.7631 10.5001H1.23682C0.960676 10.5001 0.736816 10.2762 0.736816 10.0001C0.736816 9.9123 0.759921 9.8261 0.803806 9.7501L5.56695 1.50009C5.705 1.26094 6.0108 1.179 6.24995 1.31707C6.32595 1.36096 6.3891 1.42408 6.43295 1.50009ZM5.49995 8.0001V9.0001H6.49995V8.0001H5.49995ZM5.49995 4.50008V7.0001H6.49995V4.50008H5.49995Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "Warning"
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Warning.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
Icon.displayName = 'Warning'
|
||||
|
||||
export default Icon
|
||||
@@ -1,3 +1,4 @@
|
||||
export { default as AlertTriangle } from './AlertTriangle'
|
||||
export { default as ThumbsDown } from './ThumbsDown'
|
||||
export { default as ThumbsUp } from './ThumbsUp'
|
||||
export { default as Warning } from './Warning'
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "14",
|
||||
"height": "14",
|
||||
"viewBox": "0 0 14 14",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M9.43341 6.41661L6.30441 3.2876L7.12936 2.46265L11.6666 6.99994L7.12936 11.5372L6.30441 10.7122L9.43341 7.58327H2.33331V6.41661H9.43341Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "IconR"
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './IconR.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
Icon.displayName = 'IconR'
|
||||
|
||||
export default Icon
|
||||
@@ -1,3 +1,4 @@
|
||||
export { default as IconR } from './IconR'
|
||||
export { default as ArrowNarrowLeft } from './ArrowNarrowLeft'
|
||||
export { default as ArrowUpRight } from './ArrowUpRight'
|
||||
export { default as ChevronDownDouble } from './ChevronDownDouble'
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "14",
|
||||
"height": "14",
|
||||
"viewBox": "0 0 14 14",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "ai-text"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Vector",
|
||||
"d": "M2.33301 10.5H4.08301M2.33301 7H5.24967M2.33301 3.5H11.6663M9.91634 5.83333L10.7913 7.875L12.833 8.75L10.7913 9.625L9.91634 11.6667L9.04134 9.625L6.99967 8.75L9.04134 7.875L9.91634 5.83333Z",
|
||||
"stroke": "currentColor",
|
||||
"stroke-width": "1.25",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "AiText"
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './AiText.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
Icon.displayName = 'AiText'
|
||||
|
||||
export default Icon
|
||||