UNPKG

3.84 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 { getDocument } from "reakit-utils/getDocument";
7import { ButtonOptions, ButtonHTMLProps, useButton } from "../Button/Button";
8import { unstable_FormStateReturn } from "./FormState";
9import { getInputId } from "./__utils/getInputId";
10import { getPushButtonId } from "./__utils/getPushButtonId";
11import { DeepPath } from "./__utils/types";
12import { FORM_REMOVE_BUTTON_KEYS } from "./__keys";
13
14export type unstable_FormRemoveButtonOptions<
15 V,
16 P extends DeepPath<V, P>
17> = ButtonOptions &
18 Pick<unstable_FormStateReturn<V>, "baseId" | "values" | "remove"> & {
19 /**
20 * FormInput's name as in form values. This should point to array value.
21 */
22 name: P;
23 /**
24 * The index in `form.values[name]` that will be removed.
25 */
26 index: number;
27 };
28
29export type unstable_FormRemoveButtonHTMLProps = ButtonHTMLProps;
30
31export type unstable_FormRemoveButtonProps<
32 V,
33 P extends DeepPath<V, P>
34> = unstable_FormRemoveButtonOptions<V, P> & unstable_FormRemoveButtonHTMLProps;
35
36export const unstable_useFormRemoveButton = createHook<
37 unstable_FormRemoveButtonOptions<any, any>,
38 unstable_FormRemoveButtonHTMLProps
39>({
40 name: "FormRemoveButton",
41 compose: useButton,
42 keys: FORM_REMOVE_BUTTON_KEYS,
43
44 useOptions(options, { name }) {
45 return {
46 ...options,
47 name: options.name || name,
48 };
49 },
50
51 useProps(options, { onClick: htmlOnClick, ...htmlProps }) {
52 const onClickRef = useLiveRef(htmlOnClick);
53
54 const onClick = React.useCallback(
55 (event: React.MouseEvent) => {
56 onClickRef.current?.(event);
57 if (event.defaultPrevented) return;
58
59 options.remove?.(options.name, options.index);
60
61 const inputId = getInputId(options.name, options.baseId);
62 if (!inputId) return;
63
64 const document = getDocument(event.currentTarget);
65
66 window.requestAnimationFrame(() => {
67 const selector = `[id^="${inputId}-"]`;
68 const inputs = document.querySelectorAll<HTMLInputElement>(selector);
69
70 if (inputs.length) {
71 const inputsArray = Array.from(inputs);
72 const nextIdx = inputsArray.reduce((final, input) => {
73 const match = input.id.match(new RegExp(`${inputId}-([0-9]+)`));
74 if (!match) return final;
75 const [, idx] = match;
76 if (Number(idx) > final && options.index >= final) {
77 return Number(idx);
78 }
79 return final;
80 }, 0);
81 const nextSelector = `[id^="${inputId}-${nextIdx}"]`;
82 const input = document.querySelector<HTMLInputElement>(
83 nextSelector
84 );
85 if (input) {
86 input.focus();
87 return;
88 }
89 }
90 const pushButtonId = getPushButtonId(options.name, options.baseId);
91 if (pushButtonId) {
92 const pushButton = document.getElementById(pushButtonId);
93 pushButton?.focus();
94 }
95 });
96 },
97 [options.remove, options.name, options.index, options.baseId]
98 );
99
100 return { onClick, ...htmlProps };
101 },
102}) as <V, P extends DeepPath<V, P>>(
103 options: unstable_FormRemoveButtonOptions<V, P>,
104 htmlProps?: unstable_FormRemoveButtonHTMLProps
105) => unstable_FormRemoveButtonHTMLProps;
106
107export const unstable_FormRemoveButton = (createComponent({
108 as: "button",
109 memo: true,
110 useHook: unstable_useFormRemoveButton,
111}) as unknown) as <V, P extends DeepPath<V, P>, T extends As = "button">(
112 props: PropsWithAs<unstable_FormRemoveButtonOptions<V, P>, T>
113) => JSX.Element;