UNPKG

3.21 kBPlain TextView Raw
1import * as React from "react";
2import { createComponent } from "reakit-system/createComponent";
3import { createHook } from "reakit-system/createHook";
4import { As, PropsWithAs } from "reakit-utils/types";
5import { useLiveRef } from "reakit-utils/useLiveRef";
6import { InputOptions, InputHTMLProps, useInput } from "../Input/Input";
7import { DeepPath } from "./__utils/types";
8import { getInputId } from "./__utils/getInputId";
9import { getMessageId } from "./__utils/getMessageId";
10import { getLabelId } from "./__utils/getLabelId";
11import { shouldShowError } from "./__utils/shouldShowError";
12import { formatInputName } from "./__utils/formatInputName";
13import { unstable_getIn } from "./utils/getIn";
14import { unstable_FormStateReturn } from "./FormState";
15import { FORM_INPUT_KEYS } from "./__keys";
16
17export type unstable_FormInputOptions<
18 V,
19 P extends DeepPath<V, P>
20> = InputOptions &
21 Pick<
22 unstable_FormStateReturn<V>,
23 "baseId" | "values" | "touched" | "errors" | "update" | "blur"
24 > & {
25 /**
26 * FormInput's name as in form values.
27 */
28 name: P;
29 };
30
31export type unstable_FormInputHTMLProps = InputHTMLProps &
32 React.InputHTMLAttributes<any>;
33
34export type unstable_FormInputProps<
35 V,
36 P extends DeepPath<V, P>
37> = unstable_FormInputOptions<V, P> & unstable_FormInputHTMLProps;
38
39export const unstable_useFormInput = createHook<
40 unstable_FormInputOptions<any, any>,
41 unstable_FormInputHTMLProps
42>({
43 name: "FormInput",
44 compose: useInput,
45 keys: FORM_INPUT_KEYS,
46
47 useOptions(options, { name }) {
48 return {
49 ...options,
50 name: options.name || name,
51 };
52 },
53
54 useProps(
55 options,
56 { onChange: htmlOnChange, onBlur: htmlOnBlur, ...htmlProps }
57 ) {
58 const onChangeRef = useLiveRef(htmlOnChange);
59 const onBlurRef = useLiveRef(htmlOnBlur);
60
61 const onChange = React.useCallback(
62 (event: React.ChangeEvent<HTMLInputElement>) => {
63 onChangeRef.current?.(event);
64 if (event.defaultPrevented) return;
65 options.update?.(options.name, event.target.value);
66 },
67 [options.update, options.name]
68 );
69
70 const onBlur = React.useCallback(
71 (event: React.FocusEvent) => {
72 onBlurRef.current?.(event);
73 if (event.defaultPrevented) return;
74 options.blur?.(options.name);
75 },
76 [options.blur, options.name]
77 );
78
79 return {
80 id: getInputId(options.name, options.baseId),
81 name: formatInputName(options.name),
82 value: unstable_getIn(options.values, options.name, ""),
83 "aria-describedby": getMessageId(options.name, options.baseId),
84 "aria-labelledby": getLabelId(options.name, options.baseId),
85 "aria-invalid": shouldShowError(options, options.name),
86 onChange,
87 onBlur,
88 ...htmlProps,
89 };
90 },
91}) as <V, P extends DeepPath<V, P>>(
92 options: unstable_FormInputOptions<V, P>,
93 htmlProps?: unstable_FormInputHTMLProps
94) => unstable_FormInputHTMLProps;
95
96export const unstable_FormInput = (createComponent({
97 as: "input",
98 memo: true,
99 useHook: unstable_useFormInput,
100}) as unknown) as <V, P extends DeepPath<V, P>, T extends As = "input">(
101 props: PropsWithAs<unstable_FormInputOptions<V, P>, T>
102) => JSX.Element;