Refactor: Datasets UI #3221 (#8349)

### What problem does this PR solve?

Refactor Datasets UI #3221.
### Type of change

- [X] New Feature (non-breaking change which adds functionality)
This commit is contained in:
BlueYu-0221
2025-06-19 16:40:30 +08:00
committed by GitHub
parent 403efe81a1
commit fa3e90c72e
55 changed files with 2960 additions and 425 deletions

View File

@ -0,0 +1,48 @@
import { FormLabel } from '@/components/ui/form';
import { MultiSelect } from '@/components/ui/multi-select';
import { useTranslation } from 'react-i18next';
const Languages = [
'English',
'Chinese',
'Spanish',
'French',
'German',
'Japanese',
'Korean',
];
const options = Languages.map((x) => ({ label: x, value: x }));
type CrossLanguageItemProps = {
name?: string | Array<string>;
onChange: (arg: string[]) => void;
};
export const CrossLanguageItem = ({
name = ['prompt_config', 'cross_languages'],
onChange = () => {},
}: CrossLanguageItemProps) => {
const { t } = useTranslation();
return (
<div>
<div className="pb-2">
<FormLabel tooltip={t('chat.crossLanguageTip')}>
{t('chat.crossLanguage')}
</FormLabel>
</div>
<MultiSelect
options={options}
onValueChange={(val) => {
onChange(val);
}}
// defaultValue={field.value}
placeholder={t('fileManager.pleaseSelect')}
maxCount={100}
// {...field}
modalPopover
/>
</div>
);
};

View File

@ -43,17 +43,33 @@ export function DelimiterFormField() {
<FormField
control={form.control}
name={'parser_config.delimiter'}
render={({ field }) => (
<FormItem>
<FormLabel tooltip={t('knowledgeDetails.delimiterTip')}>
{t('knowledgeDetails.delimiter')}
</FormLabel>
<FormControl>
<DelimiterInput {...field}></DelimiterInput>
</FormControl>
<FormMessage />
</FormItem>
)}
render={({ field }) => {
if (typeof field.value === 'undefined') {
// default value set
form.setValue('parser_config.delimiter', '\n');
}
return (
<FormItem className=" items-center space-y-0 ">
<div className="flex items-center">
<FormLabel
tooltip={t('knowledgeDetails.delimiterTip')}
className="text-sm text-muted-foreground whitespace-nowrap w-1/4"
>
{t('knowledgeDetails.delimiter')}
</FormLabel>
<div className="w-3/4">
<FormControl>
<DelimiterInput {...field}></DelimiterInput>
</FormControl>
</div>
</div>
<div className="flex pt-1">
<div className="w-1/4"></div>
<FormMessage />
</div>
</FormItem>
);
}}
/>
);
}

View File

@ -2,14 +2,15 @@
display: flex;
gap: 8px;
flex-wrap: wrap;
width: 100%;
// width: 100%;
margin-bottom: 8px;
}
.tag {
max-width: 100%;
margin: 0;
padding: 2px 20px 2px 4px;
padding: 2px 20px 0px 4px;
height: 26px;
font-size: 14px;
.textEllipsis();
position: relative;

View File

@ -74,7 +74,7 @@ const EditTag = ({ value = [], onChange }: EditTagsProps) => {
};
return (
<div>
<div className="flex gap-[8px] items-start">
{Array.isArray(tagChild) && tagChild.length > 0 && (
<TweenOneGroup
className={styles.tweenGroup}
@ -96,19 +96,23 @@ const EditTag = ({ value = [], onChange }: EditTagsProps) => {
</TweenOneGroup>
)}
{inputVisible ? (
<Input
ref={inputRef}
type="text"
size="small"
value={inputValue}
onChange={handleInputChange}
onBlur={handleInputConfirm}
onPressEnter={handleInputConfirm}
/>
<div className="w-[180px] mb-[8px]">
<Input
ref={inputRef}
type="text"
size="small"
value={inputValue}
onChange={handleInputChange}
onBlur={handleInputConfirm}
onPressEnter={handleInputConfirm}
/>
</div>
) : (
<Tag onClick={showInput} style={tagPlusStyle}>
<PlusOutlined />
</Tag>
<div className="mb-[8px]">
<Tag onClick={showInput} style={tagPlusStyle}>
<PlusOutlined />
</Tag>
</div>
)}
</div>
);

View File

@ -24,12 +24,21 @@ export function EntityTypesFormField({
control={form.control}
name={name}
render={({ field }) => (
<FormItem>
<FormLabel>{t('entityTypes')}</FormLabel>
<FormControl>
<EditTag {...field}></EditTag>
</FormControl>
<FormMessage />
<FormItem className=" items-center space-y-0 ">
<div className="flex items-center">
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4">
<span className="text-red-600">*</span> {t('entityTypes')}
</FormLabel>
<div className="w-3/4">
<FormControl>
<EditTag {...field}></EditTag>
</FormControl>
</div>
</div>
<div className="flex pt-1">
<div className="w-1/4"></div>
<FormMessage />
</div>
</FormItem>
)}
/>

View File

@ -17,18 +17,37 @@ export function ExcelToHtmlFormField() {
<FormField
control={form.control}
name="parser_config.html4excel"
render={({ field }) => (
<FormItem defaultChecked={false}>
<FormLabel tooltip={t('html4excelTip')}>{t('html4excel')}</FormLabel>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
></Switch>
</FormControl>
<FormMessage />
</FormItem>
)}
render={({ field }) => {
if (typeof field.value === 'undefined') {
// default value set
form.setValue('parser_config.html4excel', false);
}
return (
<FormItem defaultChecked={false} className=" items-center space-y-0 ">
<div className="flex items-center">
<FormLabel
tooltip={t('html4excelTip')}
className="text-sm text-muted-foreground whitespace-nowrap w-1/4"
>
{t('html4excel')}
</FormLabel>
<div className="w-3/4">
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
></Switch>
</FormControl>
</div>
</div>
<div className="flex pt-1">
<div className="w-1/4"></div>
<FormMessage />
</div>
</FormItem>
);
}}
/>
);
}

View File

@ -54,17 +54,37 @@ export function LayoutRecognizeFormField() {
<FormField
control={form.control}
name="parser_config.layout_recognize"
render={({ field }) => (
<FormItem>
<FormLabel tooltip={t('layoutRecognizeTip')}>
{t('layoutRecognize')}
</FormLabel>
<FormControl>
<RAGFlowSelect {...field} options={options}></RAGFlowSelect>
</FormControl>
<FormMessage />
</FormItem>
)}
render={({ field }) => {
if (typeof field.value === 'undefined') {
// default value set
form.setValue(
'parser_config.layout_recognize',
form.formState.defaultValues?.parser_config?.layout_recognize ??
'DeepDOC',
);
}
return (
<FormItem className=" items-center space-y-0 ">
<div className="flex items-center">
<FormLabel
tooltip={t('layoutRecognizeTip')}
className="text-sm text-muted-foreground whitespace-nowrap w-1/4"
>
{t('layoutRecognize')}
</FormLabel>
<div className="w-3/4">
<FormControl>
<RAGFlowSelect {...field} options={options}></RAGFlowSelect>
</FormControl>
</div>
</div>
<div className="flex pt-1">
<div className="w-1/4"></div>
<FormMessage />
</div>
</FormItem>
);
}}
/>
);
}

View File

@ -0,0 +1,25 @@
import * as React from 'react';
import { cn } from '@/lib/utils';
function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
return (
<input
type={type}
data-slot="input"
className={cn(
'border-input file:text-foreground placeholder:text-muted-foreground/70 flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-sm shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
type === 'search' &&
'[&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none [&::-webkit-search-results-button]:appearance-none [&::-webkit-search-results-decoration]:appearance-none',
type === 'file' &&
'text-muted-foreground/70 file:border-input file:text-foreground p-0 pr-3 italic file:me-3 file:h-full file:border-0 file:border-r file:border-solid file:bg-transparent file:px-3 file:text-sm file:font-medium file:not-italic',
className,
)}
{...props}
/>
);
}
export { Input };

View File

@ -11,7 +11,7 @@ export function PageRankFormField() {
tooltip={t('pageRankTip')}
defaultValue={0}
max={100}
min={1}
min={0}
></SliderInputFormField>
);
}

View File

@ -58,17 +58,27 @@ export function UseGraphRagFormField() {
control={form.control}
name="parser_config.graphrag.use_graphrag"
render={({ field }) => (
<FormItem defaultChecked={false}>
<FormLabel tooltip={t('useGraphRagTip')}>
{t('useGraphRag')}
</FormLabel>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
></Switch>
</FormControl>
<FormMessage />
<FormItem defaultChecked={false} className=" items-center space-y-0 ">
<div className="flex items-center">
<FormLabel
tooltip={t('useGraphRagTip')}
className="text-sm text-muted-foreground whitespace-nowrap w-1/4"
>
{t('useGraphRag')}
</FormLabel>
<div className="w-3/4">
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
></Switch>
</FormControl>
</div>
</div>
<div className="flex pt-1">
<div className="w-1/4"></div>
<FormMessage />
</div>
</FormItem>
)}
/>
@ -112,25 +122,33 @@ const GraphRagItems = ({
control={form.control}
name="parser_config.graphrag.method"
render={({ field }) => (
<FormItem>
<FormLabel
tooltip={renderWideTooltip(
<div
dangerouslySetInnerHTML={{
__html: t('graphRagMethodTip'),
}}
></div>,
)}
>
{t('graphRagMethod')}
</FormLabel>
<FormControl>
<RAGFlowSelect
{...field}
options={methodOptions}
></RAGFlowSelect>
</FormControl>
<FormMessage />
<FormItem className=" items-center space-y-0 ">
<div className="flex items-center">
<FormLabel
className="text-sm text-muted-foreground whitespace-nowrap w-1/4"
tooltip={renderWideTooltip(
<div
dangerouslySetInnerHTML={{
__html: t('graphRagMethodTip'),
}}
></div>,
)}
>
{t('graphRagMethod')}
</FormLabel>
<div className="w-3/4">
<FormControl>
<RAGFlowSelect
{...field}
options={methodOptions}
></RAGFlowSelect>
</FormControl>
</div>
</div>
<div className="flex pt-1">
<div className="w-1/4"></div>
<FormMessage />
</div>
</FormItem>
)}
/>
@ -139,17 +157,27 @@ const GraphRagItems = ({
control={form.control}
name="parser_config.graphrag.resolution"
render={({ field }) => (
<FormItem>
<FormLabel tooltip={renderWideTooltip('resolutionTip')}>
{t('resolution')}
</FormLabel>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
></Switch>
</FormControl>
<FormMessage />
<FormItem className=" items-center space-y-0 ">
<div className="flex items-center">
<FormLabel
tooltip={renderWideTooltip('resolutionTip')}
className="text-sm text-muted-foreground whitespace-nowrap w-1/4"
>
{t('resolution')}
</FormLabel>
<div className="w-3/4">
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
></Switch>
</FormControl>
</div>
</div>
<div className="flex pt-1">
<div className="w-1/4"></div>
<FormMessage />
</div>
</FormItem>
)}
/>
@ -158,17 +186,27 @@ const GraphRagItems = ({
control={form.control}
name="parser_config.graphrag.community"
render={({ field }) => (
<FormItem>
<FormLabel tooltip={renderWideTooltip('communityTip')}>
{t('community')}
</FormLabel>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
></Switch>
</FormControl>
<FormMessage />
<FormItem className=" items-center space-y-0 ">
<div className="flex items-center">
<FormLabel
tooltip={renderWideTooltip('communityTip')}
className="text-sm text-muted-foreground whitespace-nowrap w-1/4"
>
{t('community')}
</FormLabel>
<div className="w-3/4">
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
></Switch>
</FormControl>
</div>
</div>
<div className="flex pt-1">
<div className="w-1/4"></div>
<FormMessage />
</div>
</FormItem>
)}
/>

View File

@ -0,0 +1,146 @@
import { DocumentParserType } from '@/constants/knowledge';
import { useTranslate } from '@/hooks/common-hooks';
import random from 'lodash/random';
import { Plus } from 'lucide-react';
import { useCallback } from 'react';
import { useFormContext, useWatch } from 'react-hook-form';
import { SliderInputFormField } from '../slider-input-form-field';
import { Button } from '../ui/button';
import {
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '../ui/form';
import { Input } from '../ui/input';
import { Switch } from '../ui/switch';
import { Textarea } from '../ui/textarea';
export const excludedParseMethods = [
DocumentParserType.Table,
DocumentParserType.Resume,
DocumentParserType.One,
DocumentParserType.Picture,
DocumentParserType.KnowledgeGraph,
DocumentParserType.Qa,
DocumentParserType.Tag,
];
export const showRaptorParseConfiguration = (
parserId: DocumentParserType | undefined,
) => {
return !excludedParseMethods.some((x) => x === parserId);
};
export const excludedTagParseMethods = [
DocumentParserType.Table,
DocumentParserType.KnowledgeGraph,
DocumentParserType.Tag,
];
export const showTagItems = (parserId: DocumentParserType) => {
return !excludedTagParseMethods.includes(parserId);
};
const UseRaptorField = 'parser_config.raptor.use_raptor';
const RandomSeedField = 'parser_config.raptor.random_seed';
// The three types "table", "resume" and "one" do not display this configuration.
const RaptorFormFields = () => {
const form = useFormContext();
const { t } = useTranslate('knowledgeConfiguration');
const useRaptor = useWatch({ name: UseRaptorField });
const handleGenerate = useCallback(() => {
form.setValue(RandomSeedField, random(10000));
}, [form]);
return (
<>
<FormField
control={form.control}
name={UseRaptorField}
render={({ field }) => (
<FormItem defaultChecked={false}>
<FormLabel tooltip={t('useRaptorTip')}>{t('useRaptor')}</FormLabel>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
></Switch>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{useRaptor && (
<div className="space-y-3">
<FormField
control={form.control}
name={'parser_config.raptor.prompt'}
render={({ field }) => (
<FormItem>
<FormLabel tooltip={t('promptTip')}>{t('prompt')}</FormLabel>
<FormControl>
<Textarea {...field} rows={8} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<SliderInputFormField
name={'parser_config.raptor.max_token'}
label={t('maxToken')}
tooltip={t('maxTokenTip')}
defaultValue={256}
max={2048}
min={0}
></SliderInputFormField>
<SliderInputFormField
name={'parser_config.raptor.threshold'}
label={t('threshold')}
tooltip={t('thresholdTip')}
defaultValue={0.1}
step={0.01}
max={1}
min={0}
></SliderInputFormField>
<SliderInputFormField
name={'parser_config.raptor.max_cluster'}
label={t('maxCluster')}
tooltip={t('maxClusterTip')}
defaultValue={64}
max={1024}
min={1}
></SliderInputFormField>
<FormField
control={form.control}
name={'parser_config.raptor.random_seed'}
render={({ field }) => (
<FormItem>
<FormLabel>{t('randomSeed')}</FormLabel>
<FormControl defaultValue={0}>
<div className="flex gap-4">
<Input {...field} />
<Button
size={'sm'}
onClick={handleGenerate}
type={'button'}
>
<Plus />
</Button>
</div>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
)}
</>
);
};
export default RaptorFormFields;

View File

@ -62,18 +62,39 @@ const RaptorFormFields = () => {
<FormField
control={form.control}
name={UseRaptorField}
render={({ field }) => (
<FormItem defaultChecked={false}>
<FormLabel tooltip={t('useRaptorTip')}>{t('useRaptor')}</FormLabel>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
></Switch>
</FormControl>
<FormMessage />
</FormItem>
)}
render={({ field }) => {
if (typeof field.value === 'undefined') {
// default value set
form.setValue('parser_config.raptor.use_raptor', false);
}
return (
<FormItem
defaultChecked={false}
className="items-center space-y-0 "
>
<div className="flex items-center">
<FormLabel
tooltip={t('useRaptorTip')}
className="text-sm text-muted-foreground whitespace-nowrap w-1/4"
>
{t('useRaptor')}
</FormLabel>
<div className="w-3/4">
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
></Switch>
</FormControl>
</div>
</div>
<div className="flex pt-1">
<div className="w-1/4"></div>
<FormMessage />
</div>
</FormItem>
);
}}
/>
{useRaptor && (
<div className="space-y-3">
@ -81,12 +102,24 @@ const RaptorFormFields = () => {
control={form.control}
name={'parser_config.raptor.prompt'}
render={({ field }) => (
<FormItem>
<FormLabel tooltip={t('promptTip')}>{t('prompt')}</FormLabel>
<FormControl>
<Textarea {...field} rows={8} />
</FormControl>
<FormMessage />
<FormItem className=" items-center space-y-0 ">
<div className="flex items-start">
<FormLabel
tooltip={t('promptTip')}
className="text-sm text-muted-foreground whitespace-nowrap w-1/4"
>
{t('prompt')}
</FormLabel>
<div className="w-3/4">
<FormControl>
<Textarea {...field} rows={8} />
</FormControl>
</div>
</div>
<div className="flex pt-1">
<div className="w-1/4"></div>
<FormMessage />
</div>
</FormItem>
)}
/>
@ -119,21 +152,30 @@ const RaptorFormFields = () => {
control={form.control}
name={'parser_config.raptor.random_seed'}
render={({ field }) => (
<FormItem>
<FormLabel>{t('randomSeed')}</FormLabel>
<FormControl defaultValue={0}>
<div className="flex gap-4">
<Input {...field} />
<Button
size={'sm'}
onClick={handleGenerate}
type={'button'}
>
<Plus />
</Button>
<FormItem className=" items-center space-y-0 ">
<div className="flex items-center">
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4">
{t('randomSeed')}
</FormLabel>
<div className="w-3/4">
<FormControl defaultValue={0}>
<div className="flex gap-4">
<Input {...field} />
<Button
size={'sm'}
onClick={handleGenerate}
type={'button'}
>
<Plus />
</Button>
</div>
</FormControl>
</div>
</FormControl>
<FormMessage />
</div>
<div className="flex pt-1">
<div className="w-1/4"></div>
<FormMessage />
</div>
</FormItem>
)}
/>

View File

@ -0,0 +1,58 @@
import { Input } from '@/components/originui/input';
import { useTranslate } from '@/hooks/common-hooks';
import { EyeIcon, EyeOffIcon } from 'lucide-react';
import { ChangeEvent, LegacyRef, forwardRef, useId, useState } from 'react';
type PropType = {
name: string;
value: string;
onBlur: () => void;
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
};
function PasswordInput(
props: PropType,
ref: LegacyRef<HTMLInputElement> | undefined,
) {
const id = useId();
const [isVisible, setIsVisible] = useState<boolean>(false);
const toggleVisibility = () => setIsVisible((prevState) => !prevState);
const { t } = useTranslate('setting');
return (
<div className="*:not-first:mt-2 w-full">
{/* <Label htmlFor={id}>Show/hide password input</Label> */}
<div className="relative">
<Input
autoComplete="off"
inputMode="numeric"
id={id}
className="pe-9"
placeholder=""
type={isVisible ? 'text' : 'password'}
value={props.value}
onBlur={props.onBlur}
onChange={(ev) => props.onChange(ev)}
/>
<button
className="text-muted-foreground/80 hover:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 absolute inset-y-0 end-0 flex h-full w-9 items-center justify-center rounded-e-md transition-[color,box-shadow] outline-none focus:z-10 focus-visible:ring-[3px] disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50"
type="button"
onClick={toggleVisibility}
aria-label={isVisible ? 'Hide password' : 'Show password'}
aria-pressed={isVisible}
aria-controls="password"
>
{isVisible ? (
<EyeOffIcon size={16} aria-hidden="true" />
) : (
<EyeIcon size={16} aria-hidden="true" />
)}
</button>
</div>
</div>
);
}
export default forwardRef(PasswordInput);

View File

@ -38,38 +38,50 @@ export function SliderInputFormField({
<FormField
control={form.control}
name={name}
defaultValue={defaultValue}
defaultValue={defaultValue || 0}
render={({ field }) => (
<FormItem>
<FormLabel tooltip={tooltip}>{label}</FormLabel>
<div
className={cn(
'flex items-center gap-14 justify-between',
className,
)}
>
<FormControl>
<SingleFormSlider
{...field}
max={max}
min={min}
step={step}
// defaultValue={
// typeof defaultValue === 'number' ? [defaultValue] : undefined
// }
></SingleFormSlider>
</FormControl>
<FormControl>
<Input
type={'number'}
className="h-7 w-20"
max={max}
min={min}
step={step}
{...field}
// defaultValue={defaultValue}
></Input>
</FormControl>
<FormItem className=" items-center space-y-0 ">
<div className="flex items-center">
<FormLabel
tooltip={tooltip}
className="text-sm text-muted-foreground whitespace-nowrap w-1/4"
>
{label}
</FormLabel>
<div
className={cn(
'flex items-center gap-14 justify-between',
'w-3/4',
className,
)}
>
<FormControl>
<SingleFormSlider
{...field}
max={max}
min={min}
step={step}
// defaultValue={
// typeof defaultValue === 'number' ? [defaultValue] : undefined
// }
></SingleFormSlider>
</FormControl>
<FormControl>
<Input
type={'number'}
className="h-7 w-20"
max={max}
min={min}
step={step}
{...field}
onChange={(ev) => {
const value = ev.target.value;
field.onChange(value === '' ? 0 : Number(value)); // convert to number
}}
// defaultValue={defaultValue}
></Input>
</FormControl>
</div>
</div>
<FormMessage />
</FormItem>

View File

@ -0,0 +1,64 @@
import * as TabsPrimitive from '@radix-ui/react-tabs';
import * as React from 'react';
import { cn } from '@/lib/utils';
function Tabs({
className,
...props
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
return (
<TabsPrimitive.Root
data-slot="tabs"
className={cn('flex flex-col gap-2', className)}
{...props}
/>
);
}
function TabsList({
className,
...props
}: React.ComponentProps<typeof TabsPrimitive.List>) {
return (
<TabsPrimitive.List
data-slot="tabs-list"
className={cn(
'bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]',
className,
)}
{...props}
/>
);
}
function TabsTrigger({
className,
...props
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
return (
<TabsPrimitive.Trigger
data-slot="tabs-trigger"
className={cn(
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className,
)}
{...props}
/>
);
}
function TabsContent({
className,
...props
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
return (
<TabsPrimitive.Content
data-slot="tabs-content"
className={cn('flex-1 outline-none', className)}
{...props}
/>
);
}
export { Tabs, TabsContent, TabsList, TabsTrigger };