@crisp-ui-kit/crisp 0.27.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/multiselect/multiselect.js +7 -0
- package/dist/cjs/components/radiogroup/radiogroup.js +8 -0
- package/dist/cjs/components/select/select.js +6 -0
- package/dist/cjs/components/switch/switch.js +11 -1
- package/dist/cjs/core/index.js +3 -1
- package/dist/cjs/core/use-form-reset.js +78 -0
- package/dist/esm/components/multiselect/multiselect.js +8 -1
- package/dist/esm/components/radiogroup/radiogroup.js +8 -0
- package/dist/esm/components/select/select.js +7 -1
- package/dist/esm/components/switch/switch.js +11 -1
- package/dist/esm/core/index.js +1 -0
- package/dist/esm/core/use-form-reset.js +42 -0
- package/dist/types/core/index.d.ts +1 -0
- package/dist/types/core/use-form-reset.d.ts +28 -0
- package/package.json +1 -1
|
@@ -78,6 +78,13 @@ function MultiSelect({ options, value: valueProp, defaultValue, onValueChange, n
|
|
|
78
78
|
const [query, setQuery] = React.useState("");
|
|
79
79
|
const [highlight, setHighlight] = React.useState(0);
|
|
80
80
|
const rootRef = React.useRef(null);
|
|
81
|
+
// `form.reset()` restores native controls; a button + listbox holds its value
|
|
82
|
+
// in React, so it has to listen (#245). Controlled multiselects are left
|
|
83
|
+
// alone.
|
|
84
|
+
(0, core_1.useFormReset)(rootRef, () => {
|
|
85
|
+
if (valueProp === undefined)
|
|
86
|
+
setInternalValue(defaultValue ?? []);
|
|
87
|
+
});
|
|
81
88
|
const triggerRef = React.useRef(null);
|
|
82
89
|
const searchRef = React.useRef(null);
|
|
83
90
|
const [internalValue, setInternalValue] = React.useState(defaultValue ?? []);
|
|
@@ -37,6 +37,7 @@ exports.RadioGroup = RadioGroup;
|
|
|
37
37
|
exports.Radio = Radio;
|
|
38
38
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
39
39
|
const React = __importStar(require("react"));
|
|
40
|
+
const core_1 = require("../../core");
|
|
40
41
|
const radiogroup_recipe_1 = require("./radiogroup.recipe");
|
|
41
42
|
const radiogroup_keyboard_1 = require("./radiogroup-keyboard");
|
|
42
43
|
const RadioContext = React.createContext(null);
|
|
@@ -57,6 +58,13 @@ function RadioGroup({ value, defaultValue, onValueChange, variant = "default", n
|
|
|
57
58
|
onValueChange?.(v);
|
|
58
59
|
}, [value, onValueChange]);
|
|
59
60
|
const { onKeyDown, groupRef } = (0, radiogroup_keyboard_1.useRadioGroupKeyboard)({ select });
|
|
61
|
+
// `form.reset()` restores native controls; a group of role="radio" buttons
|
|
62
|
+
// holds its answer in React, so it has to listen (#245). A controlled group
|
|
63
|
+
// is left alone — the value is the consumer's.
|
|
64
|
+
(0, core_1.useFormReset)(groupRef, () => {
|
|
65
|
+
if (value === undefined)
|
|
66
|
+
setInternal(defaultValue);
|
|
67
|
+
});
|
|
60
68
|
return ((0, jsx_runtime_1.jsx)(RadioContext.Provider, { value: { value: selected, select, name: groupName, variant }, children: (0, jsx_runtime_1.jsxs)("div", { ref: groupRef, role: "radiogroup", "aria-label": ariaLabel, "data-variant": variant, className: (0, radiogroup_recipe_1.radiogroupRecipe)({ className }), ...rest, onKeyDown: onKeyDown, children: [children, name && selected && ((0, jsx_runtime_1.jsx)("input", { type: "hidden", name: name, value: selected }))] }) }));
|
|
61
69
|
}
|
|
62
70
|
RadioGroup.displayName = "RadioGroup";
|
|
@@ -69,6 +69,12 @@ function Select({ options, value, defaultValue, onValueChange, name, placeholder
|
|
|
69
69
|
const [highlight, setHighlight] = React.useState(0);
|
|
70
70
|
const [query, setQuery] = React.useState("");
|
|
71
71
|
const rootRef = React.useRef(null);
|
|
72
|
+
// `form.reset()` restores native controls; a button + listbox holds its value
|
|
73
|
+
// in React, so it has to listen (#245). Controlled selects are left alone.
|
|
74
|
+
(0, core_1.useFormReset)(rootRef, () => {
|
|
75
|
+
if (value === undefined)
|
|
76
|
+
setInternal(defaultValue);
|
|
77
|
+
});
|
|
72
78
|
const triggerRef = React.useRef(null);
|
|
73
79
|
const searchRef = React.useRef(null);
|
|
74
80
|
const selectedOption = options.find((o) => o.value === selected);
|
|
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.Switch = void 0;
|
|
37
37
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
38
38
|
const React = __importStar(require("react"));
|
|
39
|
+
const core_1 = require("../../core");
|
|
39
40
|
const switch_recipe_1 = require("./switch.recipe");
|
|
40
41
|
/**
|
|
41
42
|
* An on/off toggle — the settings switch, measured from Attio. Renders a
|
|
@@ -46,7 +47,16 @@ const switch_recipe_1 = require("./switch.recipe");
|
|
|
46
47
|
exports.Switch = React.forwardRef(({ checked, defaultChecked = false, onCheckedChange, disabled, className, onClick, name, value = "on", ...props }, ref) => {
|
|
47
48
|
const [internal, setInternal] = React.useState(defaultChecked);
|
|
48
49
|
const on = checked ?? internal;
|
|
49
|
-
|
|
50
|
+
// `form.reset()` restores native controls; a role="switch" button holds its
|
|
51
|
+
// state in React, so it has to listen (#245). A controlled switch is left
|
|
52
|
+
// alone — the value is the consumer's.
|
|
53
|
+
const rootRef = React.useRef(null);
|
|
54
|
+
React.useImperativeHandle(ref, () => rootRef.current);
|
|
55
|
+
(0, core_1.useFormReset)(rootRef, () => {
|
|
56
|
+
if (checked === undefined)
|
|
57
|
+
setInternal(defaultChecked);
|
|
58
|
+
});
|
|
59
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("button", { ref: rootRef, type: "button", role: "switch", "aria-checked": on, disabled: disabled, "data-checked": on ? "" : undefined, className: (0, switch_recipe_1.switchRecipe)({ className }), onClick: (e) => {
|
|
50
60
|
onClick?.(e);
|
|
51
61
|
if (e.defaultPrevented)
|
|
52
62
|
return;
|
package/dist/cjs/core/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.usePrefersReducedMotion = exports.useFocusAfterRemoval = exports.resolveRecipe = exports.Primitive = exports.placeFloating = exports.isRtlElement = exports.withDescribedBy = exports.defineRecipe = exports.cx = void 0;
|
|
17
|
+
exports.usePrefersReducedMotion = exports.useFormReset = exports.useFocusAfterRemoval = exports.resolveRecipe = exports.Primitive = exports.placeFloating = exports.isRtlElement = exports.withDescribedBy = exports.defineRecipe = exports.cx = void 0;
|
|
18
18
|
var cx_1 = require("./cx");
|
|
19
19
|
Object.defineProperty(exports, "cx", { enumerable: true, get: function () { return cx_1.cx; } });
|
|
20
20
|
var define_1 = require("./define");
|
|
@@ -32,5 +32,7 @@ Object.defineProperty(exports, "resolveRecipe", { enumerable: true, get: functio
|
|
|
32
32
|
__exportStar(require("./rtl"), exports);
|
|
33
33
|
var use_focus_after_removal_1 = require("./use-focus-after-removal");
|
|
34
34
|
Object.defineProperty(exports, "useFocusAfterRemoval", { enumerable: true, get: function () { return use_focus_after_removal_1.useFocusAfterRemoval; } });
|
|
35
|
+
var use_form_reset_1 = require("./use-form-reset");
|
|
36
|
+
Object.defineProperty(exports, "useFormReset", { enumerable: true, get: function () { return use_form_reset_1.useFormReset; } });
|
|
35
37
|
var use_prefers_reduced_motion_1 = require("./use-prefers-reduced-motion");
|
|
36
38
|
Object.defineProperty(exports, "usePrefersReducedMotion", { enumerable: true, get: function () { return use_prefers_reduced_motion_1.usePrefersReducedMotion; } });
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.useFormReset = useFormReset;
|
|
37
|
+
const React = __importStar(require("react"));
|
|
38
|
+
/**
|
|
39
|
+
* Restore a control's default when the form around it is reset.
|
|
40
|
+
*
|
|
41
|
+
* `form.reset()` restores native controls — crisp's Input and Checkbox come
|
|
42
|
+
* back on their own. The controls that are buttons and listboxes hold their
|
|
43
|
+
* value in React state, which the platform cannot touch, so a Reset button
|
|
44
|
+
* used to snap half a form back and leave the other half as the user set it
|
|
45
|
+
* (#245). The hidden inputs that carry the value to `FormData` do not help:
|
|
46
|
+
* React rewrites them from the same state a moment later.
|
|
47
|
+
*
|
|
48
|
+
* Only the UNCONTROLLED case is restored here. A controlled component's value
|
|
49
|
+
* belongs to the consumer, and React already behaves this way for a controlled
|
|
50
|
+
* `<input>`: the platform resets the DOM, the next render puts the prop
|
|
51
|
+
* straight back. A consumer who wants a controlled control to follow the reset
|
|
52
|
+
* listens for the same event.
|
|
53
|
+
*
|
|
54
|
+
* The listener lives on the form the element is in, found from the element
|
|
55
|
+
* rather than passed in, because the consumer owns the markup between them.
|
|
56
|
+
*
|
|
57
|
+
* The `value === undefined` guard in each caller is deliberate and is NOT
|
|
58
|
+
* covered by a test, because it cannot be: with a controlled component the
|
|
59
|
+
* internal state is never read — `value ?? internal` — so writing to it
|
|
60
|
+
* changes nothing observable. It is kept because a component that later reads
|
|
61
|
+
* that state (a consumer switching from controlled to uncontrolled) would
|
|
62
|
+
* otherwise find a value the reset put there behind its back.
|
|
63
|
+
*/
|
|
64
|
+
function useFormReset(ref, onReset) {
|
|
65
|
+
// the handler is read through a ref so a fresh closure each render does not
|
|
66
|
+
// resubscribe: the listener is bound to the form, not to this render.
|
|
67
|
+
const latest = React.useRef(onReset);
|
|
68
|
+
latest.current = onReset;
|
|
69
|
+
React.useEffect(() => {
|
|
70
|
+
const el = ref.current;
|
|
71
|
+
const form = el?.closest("form");
|
|
72
|
+
if (!form)
|
|
73
|
+
return;
|
|
74
|
+
const handler = () => latest.current();
|
|
75
|
+
form.addEventListener("reset", handler);
|
|
76
|
+
return () => form.removeEventListener("reset", handler);
|
|
77
|
+
}, [ref]);
|
|
78
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Check, ChevronDown, Search } from "lucide-react";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import { placeFloating } from "../../core/index.js";
|
|
4
|
+
import { placeFloating, useFormReset } from "../../core/index.js";
|
|
5
5
|
import { useListboxKeyboard } from "../select/index.js";
|
|
6
6
|
import { multiselectRecipe } from "./multiselect.recipe.js";
|
|
7
7
|
const TONES = [
|
|
@@ -42,6 +42,13 @@ export function MultiSelect({ options, value: valueProp, defaultValue, onValueCh
|
|
|
42
42
|
const [query, setQuery] = React.useState("");
|
|
43
43
|
const [highlight, setHighlight] = React.useState(0);
|
|
44
44
|
const rootRef = React.useRef(null);
|
|
45
|
+
// `form.reset()` restores native controls; a button + listbox holds its value
|
|
46
|
+
// in React, so it has to listen (#245). Controlled multiselects are left
|
|
47
|
+
// alone.
|
|
48
|
+
useFormReset(rootRef, () => {
|
|
49
|
+
if (valueProp === undefined)
|
|
50
|
+
setInternalValue(defaultValue ?? []);
|
|
51
|
+
});
|
|
45
52
|
const triggerRef = React.useRef(null);
|
|
46
53
|
const searchRef = React.useRef(null);
|
|
47
54
|
const [internalValue, setInternalValue] = React.useState(defaultValue ?? []);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
|
+
import { useFormReset } from "../../core/index.js";
|
|
3
4
|
import { radiogroupRecipe } from "./radiogroup.recipe.js";
|
|
4
5
|
import { useRadioGroupKeyboard } from "./radiogroup-keyboard.js";
|
|
5
6
|
const RadioContext = React.createContext(null);
|
|
@@ -20,6 +21,13 @@ export function RadioGroup({ value, defaultValue, onValueChange, variant = "defa
|
|
|
20
21
|
onValueChange?.(v);
|
|
21
22
|
}, [value, onValueChange]);
|
|
22
23
|
const { onKeyDown, groupRef } = useRadioGroupKeyboard({ select });
|
|
24
|
+
// `form.reset()` restores native controls; a group of role="radio" buttons
|
|
25
|
+
// holds its answer in React, so it has to listen (#245). A controlled group
|
|
26
|
+
// is left alone — the value is the consumer's.
|
|
27
|
+
useFormReset(groupRef, () => {
|
|
28
|
+
if (value === undefined)
|
|
29
|
+
setInternal(defaultValue);
|
|
30
|
+
});
|
|
23
31
|
return (_jsx(RadioContext.Provider, { value: { value: selected, select, name: groupName, variant }, children: _jsxs("div", { ref: groupRef, role: "radiogroup", "aria-label": ariaLabel, "data-variant": variant, className: radiogroupRecipe({ className }), ...rest, onKeyDown: onKeyDown, children: [children, name && selected && (_jsx("input", { type: "hidden", name: name, value: selected }))] }) }));
|
|
24
32
|
}
|
|
25
33
|
RadioGroup.displayName = "RadioGroup";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Check, ChevronDown, Search } from "lucide-react";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import { placeFloating } from "../../core/index.js";
|
|
4
|
+
import { placeFloating, useFormReset } from "../../core/index.js";
|
|
5
5
|
import { useListboxKeyboard } from "./listbox-keyboard.js";
|
|
6
6
|
import { selectRecipe } from "./select.recipe.js";
|
|
7
7
|
function optionText(o) {
|
|
@@ -33,6 +33,12 @@ export function Select({ options, value, defaultValue, onValueChange, name, plac
|
|
|
33
33
|
const [highlight, setHighlight] = React.useState(0);
|
|
34
34
|
const [query, setQuery] = React.useState("");
|
|
35
35
|
const rootRef = React.useRef(null);
|
|
36
|
+
// `form.reset()` restores native controls; a button + listbox holds its value
|
|
37
|
+
// in React, so it has to listen (#245). Controlled selects are left alone.
|
|
38
|
+
useFormReset(rootRef, () => {
|
|
39
|
+
if (value === undefined)
|
|
40
|
+
setInternal(defaultValue);
|
|
41
|
+
});
|
|
36
42
|
const triggerRef = React.useRef(null);
|
|
37
43
|
const searchRef = React.useRef(null);
|
|
38
44
|
const selectedOption = options.find((o) => o.value === selected);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
|
+
import { useFormReset } from "../../core/index.js";
|
|
3
4
|
import { switchRecipe } from "./switch.recipe.js";
|
|
4
5
|
/**
|
|
5
6
|
* An on/off toggle — the settings switch, measured from Attio. Renders a
|
|
@@ -10,7 +11,16 @@ import { switchRecipe } from "./switch.recipe.js";
|
|
|
10
11
|
export const Switch = React.forwardRef(({ checked, defaultChecked = false, onCheckedChange, disabled, className, onClick, name, value = "on", ...props }, ref) => {
|
|
11
12
|
const [internal, setInternal] = React.useState(defaultChecked);
|
|
12
13
|
const on = checked ?? internal;
|
|
13
|
-
|
|
14
|
+
// `form.reset()` restores native controls; a role="switch" button holds its
|
|
15
|
+
// state in React, so it has to listen (#245). A controlled switch is left
|
|
16
|
+
// alone — the value is the consumer's.
|
|
17
|
+
const rootRef = React.useRef(null);
|
|
18
|
+
React.useImperativeHandle(ref, () => rootRef.current);
|
|
19
|
+
useFormReset(rootRef, () => {
|
|
20
|
+
if (checked === undefined)
|
|
21
|
+
setInternal(defaultChecked);
|
|
22
|
+
});
|
|
23
|
+
return (_jsxs(_Fragment, { children: [_jsx("button", { ref: rootRef, type: "button", role: "switch", "aria-checked": on, disabled: disabled, "data-checked": on ? "" : undefined, className: switchRecipe({ className }), onClick: (e) => {
|
|
14
24
|
onClick?.(e);
|
|
15
25
|
if (e.defaultPrevented)
|
|
16
26
|
return;
|
package/dist/esm/core/index.js
CHANGED
|
@@ -7,4 +7,5 @@ export { Primitive } from "./primitive.js";
|
|
|
7
7
|
export { resolveRecipe } from "./resolve.js";
|
|
8
8
|
export * from "./rtl.js";
|
|
9
9
|
export { useFocusAfterRemoval } from "./use-focus-after-removal.js";
|
|
10
|
+
export { useFormReset } from "./use-form-reset.js";
|
|
10
11
|
export { usePrefersReducedMotion } from "./use-prefers-reduced-motion.js";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Restore a control's default when the form around it is reset.
|
|
4
|
+
*
|
|
5
|
+
* `form.reset()` restores native controls — crisp's Input and Checkbox come
|
|
6
|
+
* back on their own. The controls that are buttons and listboxes hold their
|
|
7
|
+
* value in React state, which the platform cannot touch, so a Reset button
|
|
8
|
+
* used to snap half a form back and leave the other half as the user set it
|
|
9
|
+
* (#245). The hidden inputs that carry the value to `FormData` do not help:
|
|
10
|
+
* React rewrites them from the same state a moment later.
|
|
11
|
+
*
|
|
12
|
+
* Only the UNCONTROLLED case is restored here. A controlled component's value
|
|
13
|
+
* belongs to the consumer, and React already behaves this way for a controlled
|
|
14
|
+
* `<input>`: the platform resets the DOM, the next render puts the prop
|
|
15
|
+
* straight back. A consumer who wants a controlled control to follow the reset
|
|
16
|
+
* listens for the same event.
|
|
17
|
+
*
|
|
18
|
+
* The listener lives on the form the element is in, found from the element
|
|
19
|
+
* rather than passed in, because the consumer owns the markup between them.
|
|
20
|
+
*
|
|
21
|
+
* The `value === undefined` guard in each caller is deliberate and is NOT
|
|
22
|
+
* covered by a test, because it cannot be: with a controlled component the
|
|
23
|
+
* internal state is never read — `value ?? internal` — so writing to it
|
|
24
|
+
* changes nothing observable. It is kept because a component that later reads
|
|
25
|
+
* that state (a consumer switching from controlled to uncontrolled) would
|
|
26
|
+
* otherwise find a value the reset put there behind its back.
|
|
27
|
+
*/
|
|
28
|
+
export function useFormReset(ref, onReset) {
|
|
29
|
+
// the handler is read through a ref so a fresh closure each render does not
|
|
30
|
+
// resubscribe: the listener is bound to the form, not to this render.
|
|
31
|
+
const latest = React.useRef(onReset);
|
|
32
|
+
latest.current = onReset;
|
|
33
|
+
React.useEffect(() => {
|
|
34
|
+
const el = ref.current;
|
|
35
|
+
const form = el?.closest("form");
|
|
36
|
+
if (!form)
|
|
37
|
+
return;
|
|
38
|
+
const handler = () => latest.current();
|
|
39
|
+
form.addEventListener("reset", handler);
|
|
40
|
+
return () => form.removeEventListener("reset", handler);
|
|
41
|
+
}, [ref]);
|
|
42
|
+
}
|
|
@@ -8,4 +8,5 @@ export { resolveRecipe } from "./resolve";
|
|
|
8
8
|
export * from "./rtl";
|
|
9
9
|
export type * from "./types";
|
|
10
10
|
export { useFocusAfterRemoval } from "./use-focus-after-removal";
|
|
11
|
+
export { useFormReset } from "./use-form-reset";
|
|
11
12
|
export { usePrefersReducedMotion } from "./use-prefers-reduced-motion";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Restore a control's default when the form around it is reset.
|
|
4
|
+
*
|
|
5
|
+
* `form.reset()` restores native controls — crisp's Input and Checkbox come
|
|
6
|
+
* back on their own. The controls that are buttons and listboxes hold their
|
|
7
|
+
* value in React state, which the platform cannot touch, so a Reset button
|
|
8
|
+
* used to snap half a form back and leave the other half as the user set it
|
|
9
|
+
* (#245). The hidden inputs that carry the value to `FormData` do not help:
|
|
10
|
+
* React rewrites them from the same state a moment later.
|
|
11
|
+
*
|
|
12
|
+
* Only the UNCONTROLLED case is restored here. A controlled component's value
|
|
13
|
+
* belongs to the consumer, and React already behaves this way for a controlled
|
|
14
|
+
* `<input>`: the platform resets the DOM, the next render puts the prop
|
|
15
|
+
* straight back. A consumer who wants a controlled control to follow the reset
|
|
16
|
+
* listens for the same event.
|
|
17
|
+
*
|
|
18
|
+
* The listener lives on the form the element is in, found from the element
|
|
19
|
+
* rather than passed in, because the consumer owns the markup between them.
|
|
20
|
+
*
|
|
21
|
+
* The `value === undefined` guard in each caller is deliberate and is NOT
|
|
22
|
+
* covered by a test, because it cannot be: with a controlled component the
|
|
23
|
+
* internal state is never read — `value ?? internal` — so writing to it
|
|
24
|
+
* changes nothing observable. It is kept because a component that later reads
|
|
25
|
+
* that state (a consumer switching from controlled to uncontrolled) would
|
|
26
|
+
* otherwise find a value the reset put there behind its back.
|
|
27
|
+
*/
|
|
28
|
+
export declare function useFormReset(ref: React.RefObject<HTMLElement | null>, onReset: () => void): void;
|