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:
balibabu
2025-05-29 19:52:56 +08:00
committed by GitHub
parent 49ff1ca934
commit e97fd2b5e6
11 changed files with 249 additions and 37 deletions

View File

@ -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>

View File

@ -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>
);
}
});

View File

@ -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 };

View File

@ -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>
);
});