1 | import * as React from "react";
|
2 | import { createComponent } from "reakit-system/createComponent";
|
3 | import { createHook } from "reakit-system/createHook";
|
4 | import { As, PropsWithAs } from "reakit-utils/types";
|
5 | import { useLiveRef } from "reakit-utils/useLiveRef";
|
6 | import { RadioHTMLProps, useRadio } from "../Radio/Radio";
|
7 | import { RoleOptions } from "../Role";
|
8 | import { FormRadioGroupContext } from "./FormRadioGroup";
|
9 | import { unstable_FormStateReturn } from "./FormState";
|
10 | import { unstable_getIn } from "./utils/getIn";
|
11 | import { formatInputName } from "./__utils/formatInputName";
|
12 | import { DeepPath, DeepPathValue } from "./__utils/types";
|
13 | import { FORM_RADIO_KEYS } from "./__keys";
|
14 |
|
15 | export type unstable_FormRadioOptions<
|
16 | V,
|
17 | P extends DeepPath<V, P>
|
18 | > = RoleOptions &
|
19 | Pick<unstable_FormStateReturn<V>, "values" | "update" | "blur"> & {
|
20 | |
21 |
|
22 |
|
23 | name: P;
|
24 | |
25 |
|
26 |
|
27 | value: DeepPathValue<V, P>;
|
28 | };
|
29 |
|
30 | export type unstable_FormRadioHTMLProps = RadioHTMLProps;
|
31 |
|
32 | export type unstable_FormRadioProps<
|
33 | V,
|
34 | P extends DeepPath<V, P>
|
35 | > = unstable_FormRadioOptions<V, P> & unstable_FormRadioHTMLProps;
|
36 |
|
37 | export const unstable_useFormRadio = createHook<
|
38 | unstable_FormRadioOptions<any, any>,
|
39 | unstable_FormRadioHTMLProps
|
40 | >({
|
41 | name: "FormRadio",
|
42 | compose: useRadio,
|
43 | keys: FORM_RADIO_KEYS,
|
44 |
|
45 | useOptions(options, htmlProps) {
|
46 | const name = options.name || htmlProps.name;
|
47 | const value =
|
48 | typeof options.value !== "undefined" ? options.value : htmlProps.value;
|
49 | const composite = React.useContext(FormRadioGroupContext);
|
50 | const currentChecked = unstable_getIn(options.values, name);
|
51 | const checked = currentChecked === value;
|
52 |
|
53 | if (!composite) {
|
54 |
|
55 | throw new Error("Missing FormRadioGroup");
|
56 | }
|
57 |
|
58 | return { ...options, ...composite, checked, name, value };
|
59 | },
|
60 |
|
61 | useProps(
|
62 | options,
|
63 | { onChange: htmlOnChange, onBlur: htmlOnBlur, ...htmlProps }
|
64 | ) {
|
65 | const onChangeRef = useLiveRef(htmlOnChange);
|
66 | const onBlurRef = useLiveRef(htmlOnBlur);
|
67 |
|
68 | const onChange = React.useCallback(
|
69 | (event: React.ChangeEvent) => {
|
70 | onChangeRef.current?.(event);
|
71 | if (event.defaultPrevented) return;
|
72 | options.update?.(options.name, options.value);
|
73 | },
|
74 | [options.update, options.name, options.value]
|
75 | );
|
76 |
|
77 | const onBlur = React.useCallback(
|
78 | (event: React.FocusEvent) => {
|
79 | onBlurRef.current?.(event);
|
80 | if (event.defaultPrevented) return;
|
81 | options.blur?.(options.name);
|
82 | },
|
83 | [options.blur, options.name]
|
84 | );
|
85 |
|
86 | return {
|
87 | name: formatInputName(options.name),
|
88 | onChange,
|
89 | onBlur,
|
90 | ...htmlProps,
|
91 | };
|
92 | },
|
93 | }) as <V, P extends DeepPath<V, P>>(
|
94 | options: unstable_FormRadioOptions<V, P>,
|
95 | htmlProps?: unstable_FormRadioHTMLProps
|
96 | ) => unstable_FormRadioHTMLProps;
|
97 |
|
98 | export const unstable_FormRadio = (createComponent({
|
99 | as: "input",
|
100 | memo: true,
|
101 | useHook: unstable_useFormRadio,
|
102 | }) as unknown) as <V, P extends DeepPath<V, P>, T extends As = "input">(
|
103 | props: PropsWithAs<unstable_FormRadioOptions<V, P>, T>
|
104 | ) => JSX.Element;
|