UNPKG

3.51 kBPlain TextView Raw
1import * as React from "react";
2import { As, PropsWithAs, ArrayValue } from "reakit-utils/types";
3import { createComponent } from "reakit-system/createComponent";
4import { createHook } from "reakit-system/createHook";
5import { useLiveRef } from "reakit-utils/useLiveRef";
6import {
7 CheckboxOptions,
8 CheckboxHTMLProps,
9 useCheckbox,
10} from "../Checkbox/Checkbox";
11import { DeepPath, DeepPathValue } from "./__utils/types";
12import { getInputId } from "./__utils/getInputId";
13import { getLabelId } from "./__utils/getLabelId";
14import { unstable_FormStateReturn } from "./FormState";
15import { unstable_getIn } from "./utils/getIn";
16import { formatInputName } from "./__utils/formatInputName";
17import { getMessageId } from "./__utils/getMessageId";
18import { shouldShowError } from "./__utils/shouldShowError";
19import { FORM_CHECKBOX_KEYS } from "./__keys";
20
21export type unstable_FormCheckboxOptions<V, P extends DeepPath<V, P>> = Omit<
22 CheckboxOptions,
23 "value" | "state" | "setState"
24> &
25 Pick<
26 unstable_FormStateReturn<V>,
27 "baseId" | "values" | "update" | "blur" | "touched" | "errors"
28 > & {
29 /**
30 * Checkbox's name as in form values.
31 */
32 name: P;
33 /**
34 * Checkbox's value is going to be used when multiple checkboxes share the
35 * same state. Checking a checkbox with value will add it to the state
36 * array.
37 */
38 value?: ArrayValue<DeepPathValue<V, P>>;
39 };
40
41export type unstable_FormCheckboxHTMLProps = CheckboxHTMLProps &
42 React.InputHTMLAttributes<any>;
43
44export type unstable_FormCheckboxProps<
45 V,
46 P extends DeepPath<V, P>
47> = unstable_FormCheckboxOptions<V, P> & unstable_FormCheckboxHTMLProps;
48
49export const unstable_useFormCheckbox = createHook<
50 unstable_FormCheckboxOptions<any, any>,
51 unstable_FormCheckboxHTMLProps
52>({
53 name: "FormCheckbox",
54 compose: useCheckbox,
55 keys: FORM_CHECKBOX_KEYS,
56
57 useOptions(options, htmlProps) {
58 const name = options.name || htmlProps.name;
59 const value =
60 typeof options.value !== "undefined" ? options.value : htmlProps.value;
61 const state = unstable_getIn(options.values, name);
62 const setState = (val: any) => options.update(name, val);
63 return { ...options, state, setState, name, value };
64 },
65
66 useProps(options, { onBlur: htmlOnBlur, ...htmlProps }) {
67 const onBlurRef = useLiveRef(htmlOnBlur);
68 const isBoolean = typeof options.value === "undefined";
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 "aria-invalid": shouldShowError(options, options.name),
81 name: formatInputName(options.name),
82 onBlur,
83 ...(isBoolean
84 ? {
85 id: getInputId(options.name, options.baseId),
86 "aria-describedby": getMessageId(options.name, options.baseId),
87 "aria-labelledby": getLabelId(options.name, options.baseId),
88 }
89 : {}),
90 ...htmlProps,
91 };
92 },
93}) as <V, P extends DeepPath<V, P>>(
94 options: unstable_FormCheckboxOptions<V, P>,
95 htmlProps?: unstable_FormCheckboxHTMLProps
96) => unstable_FormCheckboxHTMLProps;
97
98export const unstable_FormCheckbox = (createComponent({
99 as: "input",
100 memo: true,
101 useHook: unstable_useFormCheckbox,
102}) as unknown) as <V, P extends DeepPath<V, P>, T extends As = "input">(
103 props: PropsWithAs<unstable_FormCheckboxOptions<V, P>, T>
104) => JSX.Element;