Skip to content

Commit

Permalink
fix(ui): update DelayedOnChange to reflect changes in Form
Browse files Browse the repository at this point in the history
  • Loading branch information
Pavel910 committed May 23, 2024
1 parent 0780d96 commit ecdb65d
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 14 deletions.
27 changes: 17 additions & 10 deletions packages/ui/src/DelayedOnChange/DelayedOnChange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ const emptyFunction = (): undefined => {
return undefined;
};

interface ApplyValueCb {
(value: string, cb: (value: string) => void): void;
export interface ApplyValueCb {
(value: string): void;
}
/**
* This component is used to wrap Input and Textarea components to optimize form re-render.
Expand All @@ -16,22 +16,27 @@ interface ApplyValueCb {
* The logic behind this component is to serve as a middleware between Form and Input/Textarea, and only notify form of a change when
* a user stops typing for given period of time (400ms by default).
*/
interface OnChangeCallable {
export interface OnChangeCallable {
(value: string, cb?: ApplyValueCb): void;
}

interface OnBlurCallable {
(ev: React.SyntheticEvent): void;
}

interface OnKeyDownCallable {
(ev: React.KeyboardEvent<HTMLInputElement>): void;
}

interface ChildrenCallableParams {
value: string;
onChange: OnChangeCallable;
}

interface ChildrenCallable {
(params: ChildrenCallableParams): React.ReactElement;
}

export interface DelayedOnChangeProps {
value?: string;
delay?: number;
Expand All @@ -40,6 +45,7 @@ export interface DelayedOnChangeProps {
onKeyDown?: OnKeyDownCallable;
children: React.ReactNode | ChildrenCallable;
}

export const DelayedOnChange = ({ children, ...other }: DelayedOnChangeProps) => {
const { onChange, delay = 400, value: initialValue } = other;
const [value, setValue] = useState<string | undefined>(initialValue);
Expand All @@ -52,13 +58,13 @@ export const DelayedOnChange = ({ children, ...other }: DelayedOnChangeProps) =>

const localTimeout = React.useRef<number | null>(null);

const applyValue = (value: string, callback: ApplyValueCb = emptyFunction) => {
const applyValue = (value: string) => {
localTimeout.current && clearTimeout(localTimeout.current);
localTimeout.current = null;
if (!onChange) {
return;
}
onChange(value, callback);
onChange(value);
};

const onChangeLocal = React.useCallback((value: string) => {
Expand Down Expand Up @@ -108,22 +114,23 @@ export const DelayedOnChange = ({ children, ...other }: DelayedOnChangeProps) =>
return;
}
ev.persist();
applyValue((ev.target as HTMLInputElement).value, () => realOnBlur(ev));
applyValue((ev.target as HTMLInputElement).value);
realOnBlur(ev);
};

// Need to listen for TAB key to apply new value immediately, without delay. Otherwise validation will be triggered with old value.
const onKeyDown: OnKeyDownCallable = ev => {
ev.persist();
if (ev.key === "Tab") {
applyValue((ev.target as HTMLInputElement).value, () => realOnKeyDown(ev));
applyValue((ev.target as HTMLInputElement).value);
realOnKeyDown(ev);
} else if (ev.key === "Enter" && props["data-on-enter"]) {
applyValue((ev.target as HTMLInputElement).value, () => realOnKeyDown(ev));
applyValue((ev.target as HTMLInputElement).value);
realOnKeyDown(ev);
} else {
realOnKeyDown(ev);
}
};

return React.cloneElement(child, { ...props, onBlur, onKeyDown });
};

export default DelayedOnChange;
2 changes: 1 addition & 1 deletion packages/ui/src/DelayedOnChange/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { default as DelayedOnChange } from "./DelayedOnChange";
export * from "./DelayedOnChange";
export { default as withDelayedOnChange } from "./withDelayedOnChange";
6 changes: 3 additions & 3 deletions packages/ui/src/DelayedOnChange/withDelayedOnChange.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import React from "react";
import Delayed, { DelayedOnChangeProps } from "./DelayedOnChange";
import { DelayedOnChange, DelayedOnChangeProps } from "./DelayedOnChange";

export default function withDelayedOnChange() {
return function decorator(Component: React.ComponentType<DelayedOnChangeProps>) {
return function WithDelayedOnChange(props: DelayedOnChangeProps) {
const { value, onChange, ...rest } = props;
return (
<Delayed value={value} onChange={onChange}>
<DelayedOnChange value={value} onChange={onChange}>
{({ value, onChange }) => (
<Component {...rest} value={value} onChange={onChange} />
)}
</Delayed>
</DelayedOnChange>
);
};
};
Expand Down

0 comments on commit ecdb65d

Please sign in to comment.