mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-05-03 00:37:48 +08:00
Feat: Modify the style of the classification operator and fix some console errors. (#13314)
### What problem does this PR solve? Feat: Modify the style of the classification operator and fix some console errors. ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -16,7 +16,7 @@ interface EditTagsProps {
|
||||
}
|
||||
|
||||
const EditTag = React.forwardRef<HTMLDivElement, EditTagsProps>(
|
||||
({ value = [], onChange, disabled }: EditTagsProps) => {
|
||||
function EditTag({ value = [], onChange, disabled }, ref) {
|
||||
const [inputVisible, setInputVisible] = useState(false);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
@ -82,7 +82,7 @@ const EditTag = React.forwardRef<HTMLDivElement, EditTagsProps>(
|
||||
const tagChild = value?.map(forMap);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div ref={ref}>
|
||||
{inputVisible && (
|
||||
<Input
|
||||
ref={inputRef}
|
||||
|
||||
@ -2,12 +2,14 @@ import { isNumber, trim } from 'lodash';
|
||||
import { MinusIcon, PlusIcon } from 'lucide-react';
|
||||
import React, {
|
||||
FocusEventHandler,
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { InputProps } from '../ui/input';
|
||||
|
||||
interface NumberInputProps {
|
||||
className?: string;
|
||||
@ -18,113 +20,119 @@ interface NumberInputProps {
|
||||
max?: number;
|
||||
}
|
||||
|
||||
const NumberInput: React.FC<NumberInputProps> = ({
|
||||
className,
|
||||
value: initialValue,
|
||||
onChange,
|
||||
height,
|
||||
min = 0,
|
||||
max = Infinity,
|
||||
}) => {
|
||||
const [value, setValue] = useState<number | ''>(() => {
|
||||
return initialValue ?? 0;
|
||||
});
|
||||
const NumberInput = forwardRef<HTMLInputElement, InputProps & NumberInputProps>(
|
||||
function NumberInput(
|
||||
{
|
||||
className,
|
||||
value: initialValue,
|
||||
onChange,
|
||||
height,
|
||||
min = 0,
|
||||
max = Infinity,
|
||||
},
|
||||
ref,
|
||||
) {
|
||||
const [value, setValue] = useState<number | ''>(() => {
|
||||
return initialValue ?? 0;
|
||||
});
|
||||
|
||||
const valueRef = useRef<number>();
|
||||
const valueRef = useRef<number>();
|
||||
|
||||
useEffect(() => {
|
||||
if (initialValue !== undefined) {
|
||||
setValue(initialValue);
|
||||
}
|
||||
}, [initialValue]);
|
||||
|
||||
const handleDecrement = () => {
|
||||
if (isNumber(value) && value > min) {
|
||||
setValue(value - 1);
|
||||
onChange?.(value - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handleIncrement = () => {
|
||||
if (!isNumber(value)) {
|
||||
return;
|
||||
}
|
||||
if (value > max - 1) {
|
||||
return;
|
||||
}
|
||||
setValue(value + 1);
|
||||
onChange?.(value + 1);
|
||||
};
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const currentValue = e.target.value;
|
||||
const newValue = Number(currentValue);
|
||||
|
||||
if (trim(currentValue) === '') {
|
||||
if (isNumber(value)) {
|
||||
valueRef.current = value;
|
||||
useEffect(() => {
|
||||
if (initialValue !== undefined) {
|
||||
setValue(initialValue);
|
||||
}
|
||||
setValue('');
|
||||
return;
|
||||
}
|
||||
}, [initialValue]);
|
||||
|
||||
if (!isNaN(newValue)) {
|
||||
if (newValue > max || newValue < min) {
|
||||
const handleDecrement = () => {
|
||||
if (isNumber(value) && value > min) {
|
||||
setValue(value - 1);
|
||||
onChange?.(value - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handleIncrement = () => {
|
||||
if (!isNumber(value)) {
|
||||
return;
|
||||
}
|
||||
setValue(newValue);
|
||||
onChange?.(newValue);
|
||||
}
|
||||
};
|
||||
if (value > max - 1) {
|
||||
return;
|
||||
}
|
||||
setValue(value + 1);
|
||||
onChange?.(value + 1);
|
||||
};
|
||||
|
||||
const handleBlur: FocusEventHandler<HTMLInputElement> = useCallback(() => {
|
||||
if (isNumber(value)) {
|
||||
onChange?.(value);
|
||||
} else {
|
||||
const previousValue = valueRef.current ?? min;
|
||||
setValue(previousValue);
|
||||
onChange?.(previousValue);
|
||||
}
|
||||
}, [min, onChange, value]);
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const currentValue = e.target.value;
|
||||
const newValue = Number(currentValue);
|
||||
|
||||
const style = useMemo(
|
||||
() => ({
|
||||
height: height ? `${height.toString().replace('px', '')}px` : 'auto',
|
||||
}),
|
||||
[height],
|
||||
);
|
||||
return (
|
||||
<div
|
||||
className={`flex h-10 items-center space-x-2 border-[1px] rounded-lg w-[150px] ${className || ''}`}
|
||||
style={style}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="w-10 p-2 focus:outline-none border-r-[1px]"
|
||||
onClick={handleDecrement}
|
||||
if (trim(currentValue) === '') {
|
||||
if (isNumber(value)) {
|
||||
valueRef.current = value;
|
||||
}
|
||||
setValue('');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isNaN(newValue)) {
|
||||
if (newValue > max || newValue < min) {
|
||||
return;
|
||||
}
|
||||
setValue(newValue);
|
||||
onChange?.(newValue);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBlur: FocusEventHandler<HTMLInputElement> = useCallback(() => {
|
||||
if (isNumber(value)) {
|
||||
onChange?.(value);
|
||||
} else {
|
||||
const previousValue = valueRef.current ?? min;
|
||||
setValue(previousValue);
|
||||
onChange?.(previousValue);
|
||||
}
|
||||
}, [min, onChange, value]);
|
||||
|
||||
const style = useMemo(
|
||||
() => ({
|
||||
height: height ? `${height.toString().replace('px', '')}px` : 'auto',
|
||||
}),
|
||||
[height],
|
||||
);
|
||||
return (
|
||||
<div
|
||||
className={`flex h-10 items-center space-x-2 border-[1px] rounded-lg w-[150px] ${className || ''}`}
|
||||
style={style}
|
||||
ref={ref}
|
||||
>
|
||||
<MinusIcon size={16} aria-hidden="true" />
|
||||
</button>
|
||||
<input
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
className="w-full flex-1 text-center bg-transparent focus:outline-none"
|
||||
style={style}
|
||||
min={min}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="w-10 p-2 focus:outline-none border-l-[1px]"
|
||||
onClick={handleIncrement}
|
||||
style={style}
|
||||
>
|
||||
<PlusIcon size={16} aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
<button
|
||||
type="button"
|
||||
className="w-10 p-2 focus:outline-none border-r-[1px]"
|
||||
onClick={handleDecrement}
|
||||
style={style}
|
||||
>
|
||||
<MinusIcon size={16} aria-hidden="true" />
|
||||
</button>
|
||||
<input
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
className="w-full flex-1 text-center bg-transparent focus:outline-none"
|
||||
style={style}
|
||||
min={min}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="w-10 p-2 focus:outline-none border-l-[1px]"
|
||||
onClick={handleIncrement}
|
||||
style={style}
|
||||
>
|
||||
<PlusIcon size={16} aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default NumberInput;
|
||||
|
||||
@ -10,7 +10,7 @@ export const RunTooltip = ({ children }: PropsWithChildren) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger>{children}</TooltipTrigger>
|
||||
<TooltipTrigger asChild>{children}</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{t('flow.testRun')}</p>
|
||||
</TooltipContent>
|
||||
|
||||
@ -181,4 +181,10 @@ export const FormConfigMap = {
|
||||
[Operator.ExitLoop]: {
|
||||
component: () => <></>,
|
||||
},
|
||||
[Operator.LoopStart]: {
|
||||
component: () => <></>,
|
||||
},
|
||||
[Operator.ExcelProcessor]: {
|
||||
component: () => <></>,
|
||||
},
|
||||
};
|
||||
|
||||
@ -11,17 +11,19 @@ import {
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Input, InputProps } from '@/components/ui/input';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { BlurTextarea } from '@/components/ui/textarea';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { useUpdateNodeInternals } from '@xyflow/react';
|
||||
import humanId from 'human-id';
|
||||
import trim from 'lodash/trim';
|
||||
import { ChevronsUpDown, X } from 'lucide-react';
|
||||
import { ChevronsUpDown, Trash2 } from 'lucide-react';
|
||||
import {
|
||||
ChangeEventHandler,
|
||||
FocusEventHandler,
|
||||
forwardRef,
|
||||
memo,
|
||||
useCallback,
|
||||
useEffect,
|
||||
@ -32,7 +34,7 @@ import { v4 as uuid } from 'uuid';
|
||||
import { z } from 'zod';
|
||||
import useGraphStore from '../../store';
|
||||
import DynamicExample from './dynamic-example';
|
||||
import { useCreateCategorizeFormSchema } from './use-form-schema';
|
||||
import { CreateCategorizeFormSchema } from './use-form-schema';
|
||||
|
||||
interface IProps {
|
||||
nodeId?: string;
|
||||
@ -58,12 +60,10 @@ const getOtherFieldValues = (
|
||||
x !== form.getValues(`${formListName}.${index}.${latestField}`),
|
||||
);
|
||||
|
||||
const InnerNameInput = ({
|
||||
value,
|
||||
onChange,
|
||||
otherNames,
|
||||
validate,
|
||||
}: INameInputProps) => {
|
||||
const InnerNameInput = forwardRef<
|
||||
HTMLInputElement,
|
||||
InputProps & INameInputProps
|
||||
>(function InnerNameInput({ value, onChange, otherNames, validate }, ref) {
|
||||
const [name, setName] = useState<string | undefined>();
|
||||
const { t } = useTranslate('flow');
|
||||
|
||||
@ -103,9 +103,10 @@ const InnerNameInput = ({
|
||||
value={name}
|
||||
onChange={handleNameChange}
|
||||
onBlur={handleNameBlur}
|
||||
ref={ref}
|
||||
></Input>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
const NameInput = memo(InnerNameInput);
|
||||
|
||||
@ -127,7 +128,6 @@ const InnerFormSet = ({ index }: IProps & { index: number }) => {
|
||||
name={buildFieldName('name')}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('categoryName')}</FormLabel>
|
||||
<FormControl>
|
||||
<NameInput
|
||||
{...field}
|
||||
@ -174,12 +174,11 @@ const FormSet = memo(InnerFormSet);
|
||||
|
||||
const DynamicCategorize = ({ nodeId }: IProps) => {
|
||||
const updateNodeInternals = useUpdateNodeInternals();
|
||||
const FormSchema = useCreateCategorizeFormSchema();
|
||||
|
||||
const deleteCategorizeCaseEdges = useGraphStore(
|
||||
(state) => state.deleteEdgesBySourceAndSourceHandle,
|
||||
);
|
||||
const form = useFormContext<z.infer<typeof FormSchema>>();
|
||||
const form = useFormContext<z.infer<CreateCategorizeFormSchema>>();
|
||||
const { t } = useTranslate('flow');
|
||||
const { fields, remove, append } = useFieldArray({
|
||||
name: 'items',
|
||||
@ -208,41 +207,42 @@ const DynamicCategorize = ({ nodeId }: IProps) => {
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4 ">
|
||||
<section className="flex flex-col gap-4 ">
|
||||
{fields.map((field, index) => (
|
||||
<Collapsible key={field.id} defaultOpen>
|
||||
<div className="flex items-center justify-between space-x-4">
|
||||
<h4 className="font-bold">
|
||||
{form.getValues(`items.${index}.name`)}
|
||||
</h4>
|
||||
<CollapsibleTrigger asChild>
|
||||
<div className="flex gap-4">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="w-9 p-0"
|
||||
onClick={handleRemove(index)}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" className="w-9 p-0">
|
||||
<ChevronsUpDown className="h-4 w-4" />
|
||||
<span className="sr-only">Toggle</span>
|
||||
</Button>
|
||||
</div>
|
||||
</CollapsibleTrigger>
|
||||
</div>
|
||||
<CollapsibleContent>
|
||||
<FormSet nodeId={nodeId} index={index}></FormSet>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
<div key={field.id}>
|
||||
<Collapsible defaultOpen>
|
||||
<div className="flex items-center justify-between space-x-4 pb-5">
|
||||
<span>{form.getValues(`items.${index}.name`)}</span>
|
||||
<CollapsibleTrigger asChild>
|
||||
<div className="flex gap-4">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="w-9 p-0"
|
||||
onClick={handleRemove(index)}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" className="w-9 p-0">
|
||||
<ChevronsUpDown className="h-4 w-4" />
|
||||
<span className="sr-only">Toggle</span>
|
||||
</Button>
|
||||
</div>
|
||||
</CollapsibleTrigger>
|
||||
</div>
|
||||
<CollapsibleContent>
|
||||
<FormSet nodeId={nodeId} index={index}></FormSet>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
<Separator />
|
||||
</div>
|
||||
))}
|
||||
|
||||
<Button type={'button'} onClick={handleAdd}>
|
||||
<PlusOutlined />
|
||||
{t('addCategory')}
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { Collapse } from '@/components/collapse';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
FormControl,
|
||||
@ -7,7 +8,7 @@ import {
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Plus, X } from 'lucide-react';
|
||||
import { Plus, Trash2 } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { useFieldArray, useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -24,44 +25,49 @@ const DynamicExample = ({ name }: DynamicExampleProps) => {
|
||||
});
|
||||
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel tooltip={t('flow.msgTip')}>{t('flow.examples')}</FormLabel>
|
||||
<div className="space-y-4">
|
||||
{fields.map((field, index) => (
|
||||
<div key={field.id} className="flex items-start gap-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`${name}.${index}.value`}
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormControl>
|
||||
<Textarea {...field}> </Textarea>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
<Collapse
|
||||
title={
|
||||
<FormLabel tooltip={t('flow.msgTip')}>{t('flow.examples')}</FormLabel>
|
||||
}
|
||||
>
|
||||
<FormItem>
|
||||
<div className="space-y-4">
|
||||
{fields.map((field, index) => (
|
||||
<div key={field.id} className="flex items-start gap-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`${name}.${index}.value`}
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormControl>
|
||||
<Textarea {...field}> </Textarea>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{index === 0 ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant={'ghost'}
|
||||
onClick={() => append({ value: '' })}
|
||||
>
|
||||
<Plus />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
type="button"
|
||||
variant={'ghost'}
|
||||
onClick={() => remove(index)}
|
||||
>
|
||||
<Trash2 />
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
{index === 0 ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant={'ghost'}
|
||||
onClick={() => append({ value: '' })}
|
||||
>
|
||||
<Plus />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
type="button"
|
||||
variant={'ghost'}
|
||||
onClick={() => remove(index)}
|
||||
>
|
||||
<X />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</Collapse>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { FormContainer } from '@/components/form-container';
|
||||
import { LargeModelFormField } from '@/components/large-model-form-field';
|
||||
import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item';
|
||||
import { Form } from '@/components/ui/form';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { memo } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
@ -33,13 +33,12 @@ function CategorizeForm({ node }: INextOperatorForm) {
|
||||
return (
|
||||
<Form {...form}>
|
||||
<FormWrapper>
|
||||
<FormContainer>
|
||||
<QueryVariable></QueryVariable>
|
||||
<LargeModelFormField></LargeModelFormField>
|
||||
</FormContainer>
|
||||
<QueryVariable></QueryVariable>
|
||||
<LargeModelFormField></LargeModelFormField>
|
||||
<MessageHistoryWindowSizeFormField
|
||||
min={0}
|
||||
></MessageHistoryWindowSizeFormField>
|
||||
<Separator />
|
||||
<DynamicCategorize nodeId={node?.id}></DynamicCategorize>
|
||||
<Output list={outputList}></Output>
|
||||
</FormWrapper>
|
||||
|
||||
@ -30,3 +30,7 @@ export function useCreateCategorizeFormSchema() {
|
||||
|
||||
return FormSchema;
|
||||
}
|
||||
|
||||
export type CreateCategorizeFormSchema = ReturnType<
|
||||
typeof useCreateCategorizeFormSchema
|
||||
>;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Input, InputProps } from '@/components/ui/input';
|
||||
import { PenLine } from 'lucide-react';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useHandleNameChange } from './use-handle-name-change';
|
||||
|
||||
type NameInputProps = {
|
||||
@ -8,7 +8,10 @@ type NameInputProps = {
|
||||
onChange: (value: string) => void;
|
||||
};
|
||||
|
||||
export function NameInput({ value, onChange }: NameInputProps) {
|
||||
export const NameInput = forwardRef<
|
||||
HTMLInputElement,
|
||||
InputProps & NameInputProps
|
||||
>(function NameInput({ value, onChange }, ref) {
|
||||
const { name, handleNameBlur, handleNameChange } = useHandleNameChange(value);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
@ -33,7 +36,7 @@ export function NameInput({ value, onChange }: NameInputProps) {
|
||||
}, [isEditingMode]);
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-1 flex-1">
|
||||
<div className="flex items-center gap-1 flex-1" ref={ref}>
|
||||
{isEditingMode ? (
|
||||
<Input
|
||||
ref={inputRef}
|
||||
@ -52,4 +55,4 @@ export function NameInput({ value, onChange }: NameInputProps) {
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user