mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
Feat: Add InnerBlurInput component to avoid frequent updates of zustand causing the input box to lose focus #3221 (#7955)
### What problem does this PR solve? Feat: Add InnerBlurInput component to avoid frequent updates of zustand causing the input box to lose focus #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
import { Form, InputNumber } from 'antd';
|
||||
import { useMemo } from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
@ -8,7 +9,7 @@ import {
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from './ui/form';
|
||||
import { Input } from './ui/input';
|
||||
import { BlurInput, Input } from './ui/input';
|
||||
|
||||
const MessageHistoryWindowSizeItem = ({
|
||||
initialValue,
|
||||
@ -31,10 +32,20 @@ const MessageHistoryWindowSizeItem = ({
|
||||
|
||||
export default MessageHistoryWindowSizeItem;
|
||||
|
||||
export function MessageHistoryWindowSizeFormField() {
|
||||
type MessageHistoryWindowSizeFormFieldProps = {
|
||||
useBlurInput?: boolean;
|
||||
};
|
||||
|
||||
export function MessageHistoryWindowSizeFormField({
|
||||
useBlurInput = false,
|
||||
}: MessageHistoryWindowSizeFormFieldProps) {
|
||||
const form = useFormContext();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const NextInput = useMemo(() => {
|
||||
return useBlurInput ? BlurInput : Input;
|
||||
}, [useBlurInput]);
|
||||
|
||||
return (
|
||||
<FormField
|
||||
control={form.control}
|
||||
@ -45,7 +56,7 @@ export function MessageHistoryWindowSizeFormField() {
|
||||
{t('flow.messageHistoryWindowSize')}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} type={'number'}></Input>
|
||||
<NextInput {...field} type={'number'}></NextInput>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@ -1,7 +1,14 @@
|
||||
'use client';
|
||||
|
||||
import { CheckIcon, ChevronDownIcon } from 'lucide-react';
|
||||
import { Fragment, useCallback, useEffect, useId, useState } from 'react';
|
||||
import {
|
||||
Fragment,
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useId,
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
@ -72,11 +79,10 @@ export type SelectWithSearchFlagProps = {
|
||||
onChange?(value: string): void;
|
||||
};
|
||||
|
||||
export function SelectWithSearch({
|
||||
value: val = '',
|
||||
onChange,
|
||||
options = countries,
|
||||
}: SelectWithSearchFlagProps) {
|
||||
export const SelectWithSearch = forwardRef<
|
||||
React.ElementRef<typeof Button>,
|
||||
SelectWithSearchFlagProps
|
||||
>(({ value: val = '', onChange, options = countries }, ref) => {
|
||||
const id = useId();
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
const [value, setValue] = useState<string>('');
|
||||
@ -102,6 +108,7 @@ export function SelectWithSearch({
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
ref={ref}
|
||||
className="bg-background hover:bg-background border-input w-full justify-between px-3 font-normal outline-offset-0 outline-none focus-visible:outline-[3px]"
|
||||
>
|
||||
{value ? (
|
||||
@ -160,4 +167,4 @@ export function SelectWithSearch({
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@ -67,4 +67,42 @@ const SearchInput = (props: InputProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
type Value = string | readonly string[] | number | undefined;
|
||||
|
||||
export const InnerBlurInput = React.forwardRef<
|
||||
HTMLInputElement,
|
||||
InputProps & { value: Value; onChange(value: Value): void }
|
||||
>(({ value, onChange, ...props }, ref) => {
|
||||
const [val, setVal] = React.useState<Value>();
|
||||
|
||||
const handleChange: React.ChangeEventHandler<HTMLInputElement> =
|
||||
React.useCallback((e) => {
|
||||
setVal(e.target.value);
|
||||
}, []);
|
||||
|
||||
const handleBlur: React.FocusEventHandler<HTMLInputElement> =
|
||||
React.useCallback(
|
||||
(e) => {
|
||||
onChange?.(e.target.value);
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
setVal(value);
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<Input
|
||||
{...props}
|
||||
value={val}
|
||||
onBlur={handleBlur}
|
||||
onChange={handleChange}
|
||||
ref={ref}
|
||||
></Input>
|
||||
);
|
||||
});
|
||||
|
||||
export const BlurInput = React.memo(InnerBlurInput);
|
||||
|
||||
export { ExpandedInput, Input, SearchInput };
|
||||
|
||||
@ -20,3 +20,42 @@ const Textarea = React.forwardRef<
|
||||
Textarea.displayName = 'Textarea';
|
||||
|
||||
export { Textarea };
|
||||
|
||||
type Value = string | readonly string[] | number | undefined;
|
||||
|
||||
export const BlurTextarea = React.forwardRef<
|
||||
HTMLTextAreaElement,
|
||||
React.ComponentProps<'textarea'> & {
|
||||
value: Value;
|
||||
onChange(value: Value): void;
|
||||
}
|
||||
>(({ value, onChange, ...props }, ref) => {
|
||||
const [val, setVal] = React.useState<Value>();
|
||||
|
||||
const handleChange: React.ChangeEventHandler<HTMLTextAreaElement> =
|
||||
React.useCallback((e) => {
|
||||
setVal(e.target.value);
|
||||
}, []);
|
||||
|
||||
const handleBlur: React.FocusEventHandler<HTMLTextAreaElement> =
|
||||
React.useCallback(
|
||||
(e) => {
|
||||
onChange?.(e.target.value);
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
setVal(value);
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<Textarea
|
||||
{...props}
|
||||
value={val}
|
||||
onBlur={handleBlur}
|
||||
onChange={handleChange}
|
||||
ref={ref}
|
||||
></Textarea>
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user