UNPKG

3.37 kBPlain TextView Raw
1import * as React from "react";
2import { createComponent } from "reakit-system/createComponent";
3import { createHook } from "reakit-system/createHook";
4import { ArrayValue, As, PropsWithAs } from "reakit-utils/types";
5import { useLiveRef } from "reakit-utils/useLiveRef";
6import { getDocument } from "reakit-utils/getDocument";
7import { ButtonOptions, ButtonHTMLProps, useButton } from "../Button/Button";
8import { unstable_FormStateReturn } from "./FormState";
9import { unstable_getIn } from "./utils/getIn";
10import { formatInputName } from "./__utils/formatInputName";
11import { getInputId } from "./__utils/getInputId";
12import { getPushButtonId } from "./__utils/getPushButtonId";
13import { DeepPath, DeepPathValue } from "./__utils/types";
14import { FORM_PUSH_BUTTON_KEYS } from "./__keys";
15
16export type unstable_FormPushButtonOptions<
17 V,
18 P extends DeepPath<V, P>
19> = ButtonOptions &
20 Pick<unstable_FormStateReturn<V>, "baseId" | "values" | "push"> & {
21 /**
22 * FormInput's name as in form values. This should point to array value.
23 */
24 name: P;
25 /**
26 * The value that is going to be pushed to `form.values[name]`.
27 */
28 value: ArrayValue<DeepPathValue<V, P>>;
29 };
30
31export type unstable_FormPushButtonHTMLProps = ButtonHTMLProps;
32
33export type unstable_FormPushButtonProps<
34 V,
35 P extends DeepPath<V, P>
36> = unstable_FormPushButtonOptions<V, P> & unstable_FormPushButtonHTMLProps;
37
38export const unstable_useFormPushButton = createHook<
39 unstable_FormPushButtonOptions<any, any>,
40 unstable_FormPushButtonHTMLProps
41>({
42 name: "FormPushButton",
43 compose: useButton,
44 keys: FORM_PUSH_BUTTON_KEYS,
45
46 useOptions(options, { name, value }) {
47 return {
48 ...options,
49 name: options.name || name,
50 value: options.value ?? value,
51 };
52 },
53
54 useProps(options, { onClick: htmlOnClick, ...htmlProps }) {
55 const onClickRef = useLiveRef(htmlOnClick);
56
57 const onClick = React.useCallback(
58 (event: React.MouseEvent) => {
59 onClickRef.current?.(event);
60 if (event.defaultPrevented) return;
61
62 options.push?.(options.name, options.value);
63
64 const { length } = unstable_getIn(options.values, options.name, []);
65 const inputId = getInputId(
66 `${formatInputName(options.name, "-")}-${length}`,
67 options.baseId
68 );
69
70 if (!inputId) return;
71 const element = event.currentTarget;
72
73 window.requestAnimationFrame(() => {
74 const selector = `[id^="${inputId}"]`;
75 const document = getDocument(element);
76 const input = document.querySelector<HTMLElement>(selector);
77 input?.focus();
78 });
79 },
80 [
81 options.push,
82 options.name,
83 options.value,
84 options.values,
85 options.baseId,
86 ]
87 );
88
89 return {
90 id: getPushButtonId(options.name, options.baseId),
91 onClick,
92 ...htmlProps,
93 };
94 },
95}) as <V, P extends DeepPath<V, P>>(
96 options: unstable_FormPushButtonOptions<V, P>,
97 htmlProps?: unstable_FormPushButtonHTMLProps
98) => unstable_FormPushButtonHTMLProps;
99
100export const unstable_FormPushButton = (createComponent({
101 as: "button",
102 memo: true,
103 useHook: unstable_useFormPushButton,
104}) as unknown) as <V, P extends DeepPath<V, P>, T extends As = "button">(
105 props: PropsWithAs<unstable_FormPushButtonOptions<V, P>, T>
106) => JSX.Element;