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 { getDocument } from "reakit-utils/getDocument";
|
7 | import { ButtonOptions, ButtonHTMLProps, useButton } from "../Button/Button";
|
8 | import { unstable_FormStateReturn } from "./FormState";
|
9 | import { getInputId } from "./__utils/getInputId";
|
10 | import { getPushButtonId } from "./__utils/getPushButtonId";
|
11 | import { DeepPath } from "./__utils/types";
|
12 | import { FORM_REMOVE_BUTTON_KEYS } from "./__keys";
|
13 |
|
14 | export type unstable_FormRemoveButtonOptions<
|
15 | V,
|
16 | P extends DeepPath<V, P>
|
17 | > = ButtonOptions &
|
18 | Pick<unstable_FormStateReturn<V>, "baseId" | "values" | "remove"> & {
|
19 | |
20 |
|
21 |
|
22 | name: P;
|
23 | |
24 |
|
25 |
|
26 | index: number;
|
27 | };
|
28 |
|
29 | export type unstable_FormRemoveButtonHTMLProps = ButtonHTMLProps;
|
30 |
|
31 | export type unstable_FormRemoveButtonProps<
|
32 | V,
|
33 | P extends DeepPath<V, P>
|
34 | > = unstable_FormRemoveButtonOptions<V, P> & unstable_FormRemoveButtonHTMLProps;
|
35 |
|
36 | export 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 |
|
107 | export 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;
|