@react-md/core 6.2.1 → 6.3.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/datetime/NativeDateField.d.ts +24 -0
- package/dist/datetime/NativeDateField.js +63 -0
- package/dist/datetime/NativeDateField.js.map +1 -0
- package/dist/datetime/NativeTimeField.d.ts +26 -0
- package/dist/datetime/NativeTimeField.js +63 -0
- package/dist/datetime/NativeTimeField.js.map +1 -0
- package/dist/datetime/useDateField.d.ts +120 -0
- package/dist/datetime/useDateField.js +35 -0
- package/dist/datetime/useDateField.js.map +1 -0
- package/dist/datetime/useTimeField.d.ts +124 -0
- package/dist/datetime/useTimeField.js +65 -0
- package/dist/datetime/useTimeField.js.map +1 -0
- package/dist/datetime/utils.d.ts +34 -0
- package/dist/datetime/utils.js +27 -0
- package/dist/datetime/utils.js.map +1 -0
- package/dist/draggable/utils.d.ts +3 -6
- package/dist/draggable/utils.js.map +1 -1
- package/dist/expansion-panel/ExpansionList.js +1 -1
- package/dist/expansion-panel/ExpansionList.js.map +1 -1
- package/dist/expansion-panel/useExpansionList.d.ts +2 -7
- package/dist/expansion-panel/useExpansionList.js.map +1 -1
- package/dist/form/FormMessage.js +3 -1
- package/dist/form/FormMessage.js.map +1 -1
- package/dist/form/FormMessageContainer.d.ts +2 -1
- package/dist/form/FormMessageContainer.js +3 -2
- package/dist/form/FormMessageContainer.js.map +1 -1
- package/dist/form/FormMessageCounter.d.ts +3 -2
- package/dist/form/FormMessageCounter.js +5 -2
- package/dist/form/FormMessageCounter.js.map +1 -1
- package/dist/form/Listbox.d.ts +3 -10
- package/dist/form/Listbox.js +8 -27
- package/dist/form/Listbox.js.map +1 -1
- package/dist/form/ListboxProvider.d.ts +17 -0
- package/dist/form/ListboxProvider.js +33 -1
- package/dist/form/ListboxProvider.js.map +1 -1
- package/dist/form/NativeSelect.js +1 -0
- package/dist/form/NativeSelect.js.map +1 -1
- package/dist/form/TextArea.js +1 -0
- package/dist/form/TextArea.js.map +1 -1
- package/dist/form/TextField.js +1 -0
- package/dist/form/TextField.js.map +1 -1
- package/dist/form/_form-message.scss +13 -0
- package/dist/form/_text-field.scss +12 -3
- package/dist/form/formMessageContainerStyles.d.ts +7 -0
- package/dist/form/formMessageContainerStyles.js +4 -2
- package/dist/form/formMessageContainerStyles.js.map +1 -1
- package/dist/form/sliderUtils.d.ts +3 -7
- package/dist/form/sliderUtils.js.map +1 -1
- package/dist/form/types.d.ts +13 -0
- package/dist/form/types.js.map +1 -1
- package/dist/form/useCombobox.d.ts +6 -2
- package/dist/form/useCombobox.js +8 -9
- package/dist/form/useCombobox.js.map +1 -1
- package/dist/form/useFormReset.d.ts +4 -1
- package/dist/form/useFormReset.js +9 -4
- package/dist/form/useFormReset.js.map +1 -1
- package/dist/form/useNumberField.d.ts +5 -5
- package/dist/form/useNumberField.js +10 -2
- package/dist/form/useNumberField.js.map +1 -1
- package/dist/form/useSelectCombobox.js +2 -2
- package/dist/form/useSelectCombobox.js.map +1 -1
- package/dist/form/useTextField.d.ts +76 -59
- package/dist/form/useTextField.js +7 -1
- package/dist/form/useTextField.js.map +1 -1
- package/dist/interaction/utils.d.ts +14 -0
- package/dist/interaction/utils.js +23 -12
- package/dist/interaction/utils.js.map +1 -1
- package/dist/menu/MenuBar.js +1 -1
- package/dist/menu/MenuBar.js.map +1 -1
- package/dist/menu/MenuItemTextField.d.ts +1 -2
- package/dist/menu/MenuItemTextField.js.map +1 -1
- package/dist/menu/MenuWidget.js +3 -2
- package/dist/menu/MenuWidget.js.map +1 -1
- package/dist/movement/constants.d.ts +10 -0
- package/dist/movement/constants.js +20 -4
- package/dist/movement/constants.js.map +1 -1
- package/dist/movement/types.d.ts +59 -10
- package/dist/movement/types.js.map +1 -1
- package/dist/movement/useKeyboardMovementProvider.d.ts +5 -1
- package/dist/movement/useKeyboardMovementProvider.js +171 -73
- package/dist/movement/useKeyboardMovementProvider.js.map +1 -1
- package/dist/tabs/useTabList.js +1 -1
- package/dist/tabs/useTabList.js.map +1 -1
- package/dist/test-utils/drag.d.ts +6 -9
- package/dist/transition/useCarousel.d.ts +2 -2
- package/dist/transition/useCarousel.js.map +1 -1
- package/dist/tree/Tree.js +1 -1
- package/dist/tree/Tree.js.map +1 -1
- package/dist/tree/useTreeMovement.d.ts +2 -1
- package/dist/tree/useTreeMovement.js +2 -1
- package/dist/tree/useTreeMovement.js.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/types.js.map +1 -1
- package/dist/utils/getMiddleOfRange.d.ts +2 -3
- package/dist/utils/getMiddleOfRange.js.map +1 -1
- package/dist/utils/getPercentage.d.ts +2 -9
- package/dist/utils/getPercentage.js +1 -1
- package/dist/utils/getPercentage.js.map +1 -1
- package/dist/utils/getRangeSteps.d.ts +2 -3
- package/dist/utils/getRangeSteps.js +0 -3
- package/dist/utils/getRangeSteps.js.map +1 -1
- package/dist/utils/nearest.d.ts +2 -3
- package/dist/utils/nearest.js +0 -3
- package/dist/utils/nearest.js.map +1 -1
- package/dist/utils/trigonometry.d.ts +31 -0
- package/dist/utils/trigonometry.js +25 -0
- package/dist/utils/trigonometry.js.map +1 -0
- package/dist/window-splitter/useWindowSplitter.d.ts +1 -1
- package/dist/window-splitter/useWindowSplitter.js.map +1 -1
- package/package.json +1 -1
- package/src/datetime/NativeDateField.tsx +92 -0
- package/src/datetime/NativeTimeField.tsx +94 -0
- package/src/datetime/useDateField.ts +193 -0
- package/src/datetime/useTimeField.ts +233 -0
- package/src/datetime/utils.ts +48 -0
- package/src/draggable/utils.ts +3 -6
- package/src/expansion-panel/ExpansionList.tsx +2 -1
- package/src/expansion-panel/useExpansionList.ts +6 -12
- package/src/form/FormMessage.tsx +4 -0
- package/src/form/FormMessageContainer.tsx +8 -4
- package/src/form/FormMessageCounter.tsx +17 -6
- package/src/form/Listbox.tsx +18 -46
- package/src/form/ListboxProvider.ts +61 -1
- package/src/form/NativeSelect.tsx +1 -0
- package/src/form/TextArea.tsx +1 -0
- package/src/form/TextField.tsx +1 -0
- package/src/form/formMessageContainerStyles.ts +10 -2
- package/src/form/sliderUtils.ts +3 -7
- package/src/form/types.ts +15 -0
- package/src/form/useCombobox.ts +15 -10
- package/src/form/useFormReset.ts +12 -5
- package/src/form/useNumberField.ts +17 -14
- package/src/form/useSelectCombobox.ts +2 -2
- package/src/form/useTextField.ts +102 -69
- package/src/interaction/utils.ts +18 -20
- package/src/menu/MenuBar.tsx +1 -1
- package/src/menu/MenuItemTextField.tsx +1 -3
- package/src/menu/MenuWidget.tsx +4 -2
- package/src/movement/constants.ts +26 -4
- package/src/movement/types.ts +84 -19
- package/src/movement/useKeyboardMovementProvider.ts +209 -95
- package/src/tabs/useTabList.ts +1 -1
- package/src/test-utils/drag.ts +8 -12
- package/src/transition/useCarousel.ts +2 -2
- package/src/tree/Tree.tsx +1 -1
- package/src/tree/useTreeMovement.ts +4 -0
- package/src/types.ts +16 -0
- package/src/utils/getMiddleOfRange.ts +2 -3
- package/src/utils/getPercentage.ts +3 -11
- package/src/utils/getRangeSteps.ts +3 -3
- package/src/utils/nearest.ts +3 -3
- package/src/utils/trigonometry.ts +46 -0
- package/src/window-splitter/useWindowSplitter.ts +3 -2
|
@@ -5,10 +5,10 @@ import { triggerManualChangeEvent } from "./utils.js";
|
|
|
5
5
|
* @since 6.0.0
|
|
6
6
|
* @internal
|
|
7
7
|
*/ export function useFormReset(options) {
|
|
8
|
-
const { form, elementRef, defaultValue } = options;
|
|
8
|
+
const { form, elementRef, defaultValue, onReset } = options;
|
|
9
9
|
useEffect(()=>{
|
|
10
10
|
const element = elementRef.current;
|
|
11
|
-
if (!element) {
|
|
11
|
+
if (!element || typeof defaultValue === "undefined" && !onReset) {
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
14
|
const formElement = form && document.getElementById(form) || element.closest("form") || null;
|
|
@@ -16,7 +16,11 @@ import { triggerManualChangeEvent } from "./utils.js";
|
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
18
|
const handleReset = ()=>{
|
|
19
|
-
|
|
19
|
+
if (onReset) {
|
|
20
|
+
onReset();
|
|
21
|
+
} else if (typeof defaultValue !== "undefined") {
|
|
22
|
+
triggerManualChangeEvent(element, defaultValue);
|
|
23
|
+
}
|
|
20
24
|
};
|
|
21
25
|
formElement.addEventListener("reset", handleReset);
|
|
22
26
|
return ()=>{
|
|
@@ -25,7 +29,8 @@ import { triggerManualChangeEvent } from "./utils.js";
|
|
|
25
29
|
}, [
|
|
26
30
|
defaultValue,
|
|
27
31
|
elementRef,
|
|
28
|
-
form
|
|
32
|
+
form,
|
|
33
|
+
onReset
|
|
29
34
|
]);
|
|
30
35
|
}
|
|
31
36
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/form/useFormReset.ts"],"sourcesContent":["\"use client\";\n\nimport { type RefObject, useEffect } from \"react\";\n\nimport {\n type ChangeableHTMLElement,\n triggerManualChangeEvent,\n} from \"./utils.js\";\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport interface FormResetOptions {\n form?: string;\n elementRef: RefObject<ChangeableHTMLElement>;\n defaultValue
|
|
1
|
+
{"version":3,"sources":["../../src/form/useFormReset.ts"],"sourcesContent":["\"use client\";\n\nimport { type RefObject, useEffect } from \"react\";\n\nimport {\n type ChangeableHTMLElement,\n triggerManualChangeEvent,\n} from \"./utils.js\";\n\n/**\n * @since 6.0.0\n * @since 6.3.0 Added the optional `onReset` callback and updated\n * `defaultValue` to be optional.\n * @internal\n */\nexport interface FormResetOptions {\n form?: string;\n elementRef: RefObject<ChangeableHTMLElement>;\n onReset?: () => void;\n defaultValue?: string;\n}\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport function useFormReset(options: FormResetOptions): void {\n const { form, elementRef, defaultValue, onReset } = options;\n\n useEffect(() => {\n const element = elementRef.current;\n if (!element || (typeof defaultValue === \"undefined\" && !onReset)) {\n return;\n }\n\n const formElement =\n (form && document.getElementById(form)) ||\n element.closest<HTMLFormElement>(\"form\") ||\n null;\n if (!formElement) {\n return;\n }\n\n const handleReset = (): void => {\n if (onReset) {\n onReset();\n } else if (typeof defaultValue !== \"undefined\") {\n triggerManualChangeEvent(element, defaultValue);\n }\n };\n\n formElement.addEventListener(\"reset\", handleReset);\n return () => {\n formElement.removeEventListener(\"reset\", handleReset);\n };\n }, [defaultValue, elementRef, form, onReset]);\n}\n"],"names":["useEffect","triggerManualChangeEvent","useFormReset","options","form","elementRef","defaultValue","onReset","element","current","formElement","document","getElementById","closest","handleReset","addEventListener","removeEventListener"],"mappings":"AAAA;AAEA,SAAyBA,SAAS,QAAQ,QAAQ;AAElD,SAEEC,wBAAwB,QACnB,aAAa;AAepB;;;CAGC,GACD,OAAO,SAASC,aAAaC,OAAyB;IACpD,MAAM,EAAEC,IAAI,EAAEC,UAAU,EAAEC,YAAY,EAAEC,OAAO,EAAE,GAAGJ;IAEpDH,UAAU;QACR,MAAMQ,UAAUH,WAAWI,OAAO;QAClC,IAAI,CAACD,WAAY,OAAOF,iBAAiB,eAAe,CAACC,SAAU;YACjE;QACF;QAEA,MAAMG,cACJ,AAACN,QAAQO,SAASC,cAAc,CAACR,SACjCI,QAAQK,OAAO,CAAkB,WACjC;QACF,IAAI,CAACH,aAAa;YAChB;QACF;QAEA,MAAMI,cAAc;YAClB,IAAIP,SAAS;gBACXA;YACF,OAAO,IAAI,OAAOD,iBAAiB,aAAa;gBAC9CL,yBAAyBO,SAASF;YACpC;QACF;QAEAI,YAAYK,gBAAgB,CAAC,SAASD;QACtC,OAAO;YACLJ,YAAYM,mBAAmB,CAAC,SAASF;QAC3C;IACF,GAAG;QAACR;QAAcD;QAAYD;QAAMG;KAAQ;AAC9C"}
|
|
@@ -24,7 +24,7 @@ export interface NumberFieldConstraints {
|
|
|
24
24
|
* - Removed `updateOnChange` in favor of `updateValue`
|
|
25
25
|
* - Renamed `fixOnBlur` to `updateValueOnBlur`
|
|
26
26
|
*/
|
|
27
|
-
export interface NumberFieldHookOptions extends Omit<TextFieldHookOptions
|
|
27
|
+
export interface NumberFieldHookOptions extends Omit<TextFieldHookOptions, "defaultValue" | "isNumber">, NumberFieldConstraints {
|
|
28
28
|
/**
|
|
29
29
|
* @defaultValue `undefined`
|
|
30
30
|
*/
|
|
@@ -89,15 +89,15 @@ export interface NumberFieldHookState extends Omit<TextFieldHookState, "value">
|
|
|
89
89
|
value: number | undefined;
|
|
90
90
|
}
|
|
91
91
|
/** @since 2.5.6 */
|
|
92
|
-
export interface ProvidedNumberFieldProps extends ProvidedTextFieldProps
|
|
92
|
+
export interface ProvidedNumberFieldProps extends ProvidedTextFieldProps, NumberFieldConstraints {
|
|
93
93
|
type: "number";
|
|
94
94
|
}
|
|
95
95
|
/** @since 2.5.6 */
|
|
96
|
-
export interface ProvidedNumberFieldMessageProps extends ProvidedTextFieldMessageProps
|
|
96
|
+
export interface ProvidedNumberFieldMessageProps extends ProvidedTextFieldMessageProps, NumberFieldConstraints {
|
|
97
97
|
type: "number";
|
|
98
98
|
}
|
|
99
99
|
/** @since 6.0.0 */
|
|
100
|
-
export interface NumberFieldImplementation extends Omit<TextFieldImplementation
|
|
100
|
+
export interface NumberFieldImplementation extends Omit<TextFieldImplementation, "value" | "setState"> {
|
|
101
101
|
value: number | undefined;
|
|
102
102
|
setState: UseStateSetter<NumberFieldHookState>;
|
|
103
103
|
fieldProps: ProvidedNumberFieldProps;
|
|
@@ -107,7 +107,7 @@ export interface NumberFieldWithMessageImplementation extends NumberFieldImpleme
|
|
|
107
107
|
fieldProps: ProvidedNumberFieldMessageProps;
|
|
108
108
|
}
|
|
109
109
|
/** @since 6.0.0 */
|
|
110
|
-
export interface ValidatedNumberFieldImplementation extends Omit<ValidatedTextFieldImplementation
|
|
110
|
+
export interface ValidatedNumberFieldImplementation extends Omit<ValidatedTextFieldImplementation, "value" | "setState"> {
|
|
111
111
|
value: number | undefined;
|
|
112
112
|
setState: UseStateSetter<NumberFieldHookState>;
|
|
113
113
|
fieldProps: ProvidedNumberFieldProps | ProvidedNumberFieldMessageProps;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useCallback, useRef, useState } from "react";
|
|
3
3
|
import { withinRange } from "../utils/withinRange.js";
|
|
4
|
+
import { useFormReset } from "./useFormReset.js";
|
|
4
5
|
import { useTextField } from "./useTextField.js";
|
|
5
6
|
const noop = ()=>{
|
|
6
7
|
// do nothing
|
|
@@ -12,13 +13,14 @@ const noop = ()=>{
|
|
|
12
13
|
* @see {@link useTextField}
|
|
13
14
|
* @see {@link useNumberField} overrides for other examples.
|
|
14
15
|
*/ export function useNumberField(options) {
|
|
15
|
-
const { min, max, step, onBlur = noop, onChange = noop, updateValue = "change", updateValueOnBlur = true, defaultValue, ...textOptions } = options;
|
|
16
|
+
const { min, max, step, form, onBlur = noop, onChange = noop, updateValue = "change", updateValueOnBlur = true, disableReset, defaultValue, ...textOptions } = options;
|
|
16
17
|
const [number, setNumber] = useState(defaultValue);
|
|
17
18
|
const initial = useRef(number);
|
|
18
|
-
const { value: _value, reset: resetTextField, fieldProps, setState: setTextFieldState, ...remaining } = useTextField({
|
|
19
|
+
const { value: _value, reset: resetTextField, fieldRef, fieldProps, setState: setTextFieldState, ...remaining } = useTextField({
|
|
19
20
|
...textOptions,
|
|
20
21
|
isNumber: true,
|
|
21
22
|
defaultValue: `${number ?? ""}`,
|
|
23
|
+
disableReset: true,
|
|
22
24
|
onBlur (event) {
|
|
23
25
|
onBlur(event);
|
|
24
26
|
if (event.isPropagationStopped()) {
|
|
@@ -107,11 +109,17 @@ const noop = ()=>{
|
|
|
107
109
|
}, [
|
|
108
110
|
setTextFieldState
|
|
109
111
|
]);
|
|
112
|
+
useFormReset({
|
|
113
|
+
form,
|
|
114
|
+
elementRef: fieldRef,
|
|
115
|
+
onReset: disableReset ? undefined : reset
|
|
116
|
+
});
|
|
110
117
|
return {
|
|
111
118
|
...remaining,
|
|
112
119
|
reset,
|
|
113
120
|
value: number,
|
|
114
121
|
setState,
|
|
122
|
+
fieldRef,
|
|
115
123
|
fieldProps: {
|
|
116
124
|
...fieldProps,
|
|
117
125
|
min,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/form/useNumberField.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useRef, useState } from \"react\";\n\nimport { type UseStateInitializer, type UseStateSetter } from \"../types.js\";\nimport { withinRange } from \"../utils/withinRange.js\";\nimport {\n type ProvidedTextFieldMessageProps,\n type ProvidedTextFieldProps,\n type TextFieldHookOptions,\n type TextFieldHookState,\n type TextFieldImplementation,\n type ValidatedTextFieldImplementation,\n useTextField,\n} from \"./useTextField.js\";\n\nconst noop = (): void => {\n // do nothing\n};\n\n/** @since 2.5.0 */\nexport interface NumberFieldConstraints {\n /**\n * An optional min value for the number field.\n */\n min?: number;\n\n /**\n * An optional max value for the number field.\n */\n max?: number;\n\n /**\n * An optional step amount to use.\n *\n * Note: The `min` and `max` values must be divisible by this value when any\n * are defined.\n */\n step?: number;\n}\n\n/**\n * @since 2.5.0\n * @since 6.0.0\n * - Removed `updateOnChange` in favor of `updateValue`\n * - Renamed `fixOnBlur` to `updateValueOnBlur`\n */\nexport interface NumberFieldHookOptions\n extends Omit<\n TextFieldHookOptions<HTMLInputElement>,\n \"defaultValue\" | \"isNumber\"\n >,\n NumberFieldConstraints {\n /**\n * @defaultValue `undefined`\n */\n defaultValue?: UseStateInitializer<number>;\n\n /**\n * This controls the behavior for the `value` returned by this hook. If you\n * need access to the current value immediately as the user types to update\n * other components, keep this as the default of `\"change\"`. Otherwise, set\n * this to `\"blur\"`.\n *\n * @example Deferring Updates on Blur\n * ```tsx\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useNumberField } from \"@react-md/core/form/useNumberField\";\n * import type { ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * const { fieldProps, value } = useNumberField({\n * min: 0,\n * max: 100,\n * name: \"someName\",\n * defaultValue: 0,\n * updateValue: \"blur\",\n * });\n *\n * const result = useMemo(() => someExpensiveComputation(value), [value]);\n *\n * return <TextField {...fieldProps} label=\"Label\" />;\n * }\n * ```\n *\n * @defaultValue `\"change\"`\n */\n updateValue?: \"blur\" | \"change\";\n\n /**\n * This option is used to update the `number` value and text field value to be\n * within the `min` and `max` range or just format the text field value when\n * the input is blurred. This update will only be applied if the text field\n * contains a valid number. Using `min = 0` and `max = 10`:\n *\n * | text value | updated value |\n * | ---------- | ------------- |\n * | 000001 | 1 |\n * | -1 | 0 |\n * | 20 | 10 |\n * | -12 | 0 |\n * | --1 | --1 |\n * | fjdka | fjdka |\n *\n *\n * Set this to `false` if no changed should be applied and force the user to\n * fix any min/max errors manually and maintain weird formatting.\n *\n * @defaultValue `true`\n * @since 6.0.0 This was renamed from `fixOnBlur` and removed the\n * `\"min\"` and `\"max\"` behavior.\n */\n updateValueOnBlur?: boolean;\n}\n\n/** @since 6.0.0 */\nexport interface NumberFieldHookState\n extends Omit<TextFieldHookState, \"value\"> {\n value: number | undefined;\n}\n\n/** @since 2.5.6 */\nexport interface ProvidedNumberFieldProps\n extends ProvidedTextFieldProps<HTMLInputElement>,\n NumberFieldConstraints {\n type: \"number\";\n}\n\n/** @since 2.5.6 */\nexport interface ProvidedNumberFieldMessageProps\n extends ProvidedTextFieldMessageProps<HTMLInputElement>,\n NumberFieldConstraints {\n type: \"number\";\n}\n\n/** @since 6.0.0 */\nexport interface NumberFieldImplementation\n extends Omit<\n TextFieldImplementation<HTMLInputElement>,\n \"value\" | \"setState\"\n > {\n value: number | undefined;\n setState: UseStateSetter<NumberFieldHookState>;\n fieldProps: ProvidedNumberFieldProps;\n}\n\n/** @since 6.0.0 */\nexport interface NumberFieldWithMessageImplementation\n extends NumberFieldImplementation {\n fieldProps: ProvidedNumberFieldMessageProps;\n}\n\n/** @since 6.0.0 */\nexport interface ValidatedNumberFieldImplementation\n extends Omit<\n ValidatedTextFieldImplementation<HTMLInputElement>,\n \"value\" | \"setState\"\n > {\n value: number | undefined;\n setState: UseStateSetter<NumberFieldHookState>;\n fieldProps: ProvidedNumberFieldProps | ProvidedNumberFieldMessageProps;\n}\n\n/**\n * @example Enforce Number Value and No Error Messages\n * ```tsx\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useNumberField } from \"@react-md/core/form/useNumberField\";\n * import type { ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * const { fieldProps, value } = useNumberField({\n * min: 0,\n * max: 100,\n * name: \"someName\",\n * defaultValue: 0,\n * disableMessage: true,\n * });\n *\n * // this is safe since `value` will always be a number even if there is a\n * // validation error. since the min and max options were provided as well,\n * // number will be between that range as well.\n * const computed = value * 10;\n *\n * return <TextField {...fieldProps} label=\"Label\" />;\n * }\n * ```\n *\n * @see {@link useTextField}\n * @see {@link useNumberField} overrides for other examples.\n * ```\n */\nexport function useNumberField(\n options: NumberFieldHookOptions & {\n disableMessage: true;\n defaultValue: UseStateInitializer<number>;\n }\n): NumberFieldImplementation & {\n value: number;\n setState: UseStateSetter<NumberFieldHookState & { value: number }>;\n};\n\n/**\n * @example No Error Messages\n * ```tsx\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useNumberField } from \"@react-md/core/form/useNumberField\";\n * import type { ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * const { fieldProps, value } = useNumberField({\n * min: 0,\n * max: 100,\n * name: \"someName\",\n * disableMessage: true,\n * });\n *\n * // `value` will be `undefined` until the user enters a valid value once\n * // there is a valid value, `value` will be a `number`. So this might cause\n * // `computed` to be `NaN | number`\n * //\n * // const computed = value * 10;\n *\n * return <TextField {...fieldProps} label=\"Label\" />;\n * }\n * ```\n *\n * @see {@link useTextField}\n * @see {@link useNumberField} overrides for other examples.\n */\nexport function useNumberField(\n options: NumberFieldHookOptions & { disableMessage: true }\n): NumberFieldImplementation;\n\n/**\n * @example Enforce Number Value\n * ```tsx\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useNumberField } from \"@react-md/core/form/useNumberField\";\n * import type { ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * const { fieldProps, value } = useNumberField({\n * min: 0,\n * max: 100,\n * name: \"someName\",\n * defaultValue: 0,\n * });\n *\n * // this is safe since `value` will always be a number even if there is a\n * // validation error. since the min and max options were provided as well,\n * // number will be between that range as well.\n * const computed = value * 10;\n *\n * return <TextField {...fieldProps} label=\"Label\" />;\n * }\n * ```\n *\n * @example Enforce Number Value and Deferring Updates\n * ```tsx\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useNumberField } from \"@react-md/core/form/useNumberField\";\n * import type { ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * const { fieldProps, value } = useNumberField({\n * min: 0,\n * max: 100,\n * name: \"someName\",\n * defaultValue: 0,\n * updateValue: \"blur\",\n * });\n *\n * // the `value` will only be updated whenever the `TextField` is blurred.\n * // This is helpful if the `value` is used in expensive computations or\n * // updates that do not need to be updated as the user types\n * const computed = value * 10;\n *\n * return <TextField {...fieldProps} label=\"Label\" />;\n * }\n * ```\n *\n * @see {@link useTextField}\n * @see {@link useNumberField} overrides for other examples.\n */\nexport function useNumberField(\n options: NumberFieldHookOptions & {\n defaultValue: UseStateInitializer<number>;\n }\n): NumberFieldWithMessageImplementation & {\n value: number;\n setState: UseStateSetter<NumberFieldHookState & { value: number }>;\n};\n\n/**\n * The `useNumberField` hook is used to control the state of a `TextField` or\n * `<input type=\"number\">`\n *\n * @example Default Implementation\n * ```tsx\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useNumberField } from \"@react-md/core/form/useNumberField\";\n * import type { ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * const { fieldProps, value } = useNumberField({\n * name: \"someName\",\n * });\n *\n * // `value` will be `undefined` until the user enters a valid value once\n * // there is a valid value, `value` will be a `number`. So this might cause\n * // `computed` to be `NaN | number`\n * //\n * // const computed = value * 10;\n *\n * // whenever there is an error, an error message will be displayed below the\n * // `TextField`\n * return <TextField {...fieldProps} label=\"Label\" />;\n * }\n * ```\n *\n * @example Adding Constraints\n * ```tsx\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useNumberField } from \"@react-md/core/form/useNumberField\";\n * import type { ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * const { fieldProps, value } = useNumberField({\n * name: \"someName\",\n * min: 0,\n * max: 100,\n * step: 2,\n * required: true,\n * });\n *\n * return <TextField {...fieldProps} label=\"Label\" />;\n * }\n * ```\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation\n * @see {@link https://react-md.dev/components/text-field#number | Number TextField Demos}\n * @see {@link https://react-md.dev/hooks/use-number-field | useNumberField Demos}\n * @see {@link useTextField}\n */\nexport function useNumberField(\n options: NumberFieldHookOptions\n): NumberFieldWithMessageImplementation;\n\n/**\n * @internal\n * @see {@link https://react-md.dev/components/text-field#number | Number TextField Demos}\n * @see {@link https://react-md.dev/hooks/use-number-field | useNumberField Demos}\n * @see {@link useTextField}\n * @see {@link useNumberField} overrides for other examples.\n */\nexport function useNumberField(\n options: NumberFieldHookOptions\n): ValidatedNumberFieldImplementation {\n const {\n min,\n max,\n step,\n onBlur = noop,\n onChange = noop,\n updateValue = \"change\",\n updateValueOnBlur = true,\n defaultValue,\n ...textOptions\n } = options;\n\n const [number, setNumber] = useState(defaultValue);\n const initial = useRef(number);\n const {\n value: _value,\n reset: resetTextField,\n fieldProps,\n setState: setTextFieldState,\n ...remaining\n } = useTextField({\n ...textOptions,\n isNumber: true,\n defaultValue: `${number ?? \"\"}`,\n onBlur(event) {\n onBlur(event);\n if (event.isPropagationStopped()) {\n return;\n }\n\n const input = event.currentTarget;\n input.setCustomValidity(\"\");\n input.checkValidity();\n if (\n !updateValueOnBlur ||\n // do nothing else since it's a weird value like: `\"--0\"` which causes\n // the value to be `\"\"` and `numberAsValue` to be `NaN`\n input.validity.badInput\n ) {\n return;\n }\n\n let value = input.valueAsNumber;\n if (input.value === \"\" && typeof initial.current === \"number\") {\n value = min ?? initial.current;\n }\n\n // can't have both rangeUnderflow and rangeOverflow at the same time, so\n // it's \"safe\" to always provide both\n value = withinRange({ min, max, value });\n if (!Number.isNaN(value)) {\n setNumber(value);\n input.value = `${value}`;\n } else if (typeof initial.current === \"undefined\") {\n setNumber(undefined);\n }\n },\n onChange(event) {\n onChange(event);\n if (event.isPropagationStopped() || updateValue === \"blur\") {\n return;\n }\n\n const input = event.currentTarget;\n input.checkValidity();\n const value = withinRange({\n min,\n max,\n value: event.currentTarget.valueAsNumber,\n });\n if (\n !input.validity.valid &&\n !input.validity.rangeUnderflow &&\n !input.validity.rangeOverflow\n ) {\n return;\n }\n\n if (!Number.isNaN(value)) {\n setNumber(value);\n } else if (initial.current === undefined) {\n setNumber(undefined);\n }\n },\n });\n\n const reset = useCallback(() => {\n resetTextField();\n setNumber(initial.current);\n }, [resetTextField]);\n const setState = useCallback<UseStateSetter<NumberFieldHookState>>(\n (nextState) => {\n if (typeof nextState === \"function\") {\n setNumber((prevNumber) => {\n let nextNumber: number | undefined = prevNumber;\n setTextFieldState((prevState) => {\n const updated = nextState({\n ...prevState,\n value: prevNumber,\n });\n\n nextNumber = updated.value;\n\n return {\n ...updated,\n value: `${nextNumber ?? \"\"}`,\n };\n });\n\n return nextNumber;\n });\n return;\n }\n\n const { value, error, errorMessage } = nextState;\n setNumber(value);\n setTextFieldState({\n value: `${value ?? \"\"}`,\n error,\n errorMessage,\n });\n },\n [setTextFieldState]\n );\n\n return {\n ...remaining,\n reset,\n value: number,\n setState,\n fieldProps: {\n ...fieldProps,\n min,\n max,\n step,\n type: \"number\",\n },\n };\n}\n"],"names":["useCallback","useRef","useState","withinRange","useTextField","noop","useNumberField","options","min","max","step","onBlur","onChange","updateValue","updateValueOnBlur","defaultValue","textOptions","number","setNumber","initial","value","_value","reset","resetTextField","fieldProps","setState","setTextFieldState","remaining","isNumber","event","isPropagationStopped","input","currentTarget","setCustomValidity","checkValidity","validity","badInput","valueAsNumber","current","Number","isNaN","undefined","valid","rangeUnderflow","rangeOverflow","nextState","prevNumber","nextNumber","prevState","updated","error","errorMessage","type"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/form/useNumberField.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useRef, useState } from \"react\";\n\nimport { type UseStateInitializer, type UseStateSetter } from \"../types.js\";\nimport { withinRange } from \"../utils/withinRange.js\";\nimport { useFormReset } from \"./useFormReset.js\";\nimport {\n type ProvidedTextFieldMessageProps,\n type ProvidedTextFieldProps,\n type TextFieldHookOptions,\n type TextFieldHookState,\n type TextFieldImplementation,\n type ValidatedTextFieldImplementation,\n useTextField,\n} from \"./useTextField.js\";\n\nconst noop = (): void => {\n // do nothing\n};\n\n/** @since 2.5.0 */\nexport interface NumberFieldConstraints {\n /**\n * An optional min value for the number field.\n */\n min?: number;\n\n /**\n * An optional max value for the number field.\n */\n max?: number;\n\n /**\n * An optional step amount to use.\n *\n * Note: The `min` and `max` values must be divisible by this value when any\n * are defined.\n */\n step?: number;\n}\n\n/**\n * @since 2.5.0\n * @since 6.0.0\n * - Removed `updateOnChange` in favor of `updateValue`\n * - Renamed `fixOnBlur` to `updateValueOnBlur`\n */\nexport interface NumberFieldHookOptions\n extends Omit<TextFieldHookOptions, \"defaultValue\" | \"isNumber\">,\n NumberFieldConstraints {\n /**\n * @defaultValue `undefined`\n */\n defaultValue?: UseStateInitializer<number>;\n\n /**\n * This controls the behavior for the `value` returned by this hook. If you\n * need access to the current value immediately as the user types to update\n * other components, keep this as the default of `\"change\"`. Otherwise, set\n * this to `\"blur\"`.\n *\n * @example Deferring Updates on Blur\n * ```tsx\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useNumberField } from \"@react-md/core/form/useNumberField\";\n * import type { ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * const { fieldProps, value } = useNumberField({\n * min: 0,\n * max: 100,\n * name: \"someName\",\n * defaultValue: 0,\n * updateValue: \"blur\",\n * });\n *\n * const result = useMemo(() => someExpensiveComputation(value), [value]);\n *\n * return <TextField {...fieldProps} label=\"Label\" />;\n * }\n * ```\n *\n * @defaultValue `\"change\"`\n */\n updateValue?: \"blur\" | \"change\";\n\n /**\n * This option is used to update the `number` value and text field value to be\n * within the `min` and `max` range or just format the text field value when\n * the input is blurred. This update will only be applied if the text field\n * contains a valid number. Using `min = 0` and `max = 10`:\n *\n * | text value | updated value |\n * | ---------- | ------------- |\n * | 000001 | 1 |\n * | -1 | 0 |\n * | 20 | 10 |\n * | -12 | 0 |\n * | --1 | --1 |\n * | fjdka | fjdka |\n *\n *\n * Set this to `false` if no changed should be applied and force the user to\n * fix any min/max errors manually and maintain weird formatting.\n *\n * @defaultValue `true`\n * @since 6.0.0 This was renamed from `fixOnBlur` and removed the\n * `\"min\"` and `\"max\"` behavior.\n */\n updateValueOnBlur?: boolean;\n}\n\n/** @since 6.0.0 */\nexport interface NumberFieldHookState\n extends Omit<TextFieldHookState, \"value\"> {\n value: number | undefined;\n}\n\n/** @since 2.5.6 */\nexport interface ProvidedNumberFieldProps\n extends ProvidedTextFieldProps,\n NumberFieldConstraints {\n type: \"number\";\n}\n\n/** @since 2.5.6 */\nexport interface ProvidedNumberFieldMessageProps\n extends ProvidedTextFieldMessageProps,\n NumberFieldConstraints {\n type: \"number\";\n}\n\n/** @since 6.0.0 */\nexport interface NumberFieldImplementation\n extends Omit<TextFieldImplementation, \"value\" | \"setState\"> {\n value: number | undefined;\n setState: UseStateSetter<NumberFieldHookState>;\n fieldProps: ProvidedNumberFieldProps;\n}\n\n/** @since 6.0.0 */\nexport interface NumberFieldWithMessageImplementation\n extends NumberFieldImplementation {\n fieldProps: ProvidedNumberFieldMessageProps;\n}\n\n/** @since 6.0.0 */\nexport interface ValidatedNumberFieldImplementation\n extends Omit<ValidatedTextFieldImplementation, \"value\" | \"setState\"> {\n value: number | undefined;\n setState: UseStateSetter<NumberFieldHookState>;\n fieldProps: ProvidedNumberFieldProps | ProvidedNumberFieldMessageProps;\n}\n\n/**\n * @example Enforce Number Value and No Error Messages\n * ```tsx\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useNumberField } from \"@react-md/core/form/useNumberField\";\n * import type { ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * const { fieldProps, value } = useNumberField({\n * min: 0,\n * max: 100,\n * name: \"someName\",\n * defaultValue: 0,\n * disableMessage: true,\n * });\n *\n * // this is safe since `value` will always be a number even if there is a\n * // validation error. since the min and max options were provided as well,\n * // number will be between that range as well.\n * const computed = value * 10;\n *\n * return <TextField {...fieldProps} label=\"Label\" />;\n * }\n * ```\n *\n * @see {@link useTextField}\n * @see {@link useNumberField} overrides for other examples.\n * ```\n */\nexport function useNumberField(\n options: NumberFieldHookOptions & {\n disableMessage: true;\n defaultValue: UseStateInitializer<number>;\n }\n): NumberFieldImplementation & {\n value: number;\n setState: UseStateSetter<NumberFieldHookState & { value: number }>;\n};\n\n/**\n * @example No Error Messages\n * ```tsx\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useNumberField } from \"@react-md/core/form/useNumberField\";\n * import type { ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * const { fieldProps, value } = useNumberField({\n * min: 0,\n * max: 100,\n * name: \"someName\",\n * disableMessage: true,\n * });\n *\n * // `value` will be `undefined` until the user enters a valid value once\n * // there is a valid value, `value` will be a `number`. So this might cause\n * // `computed` to be `NaN | number`\n * //\n * // const computed = value * 10;\n *\n * return <TextField {...fieldProps} label=\"Label\" />;\n * }\n * ```\n *\n * @see {@link useTextField}\n * @see {@link useNumberField} overrides for other examples.\n */\nexport function useNumberField(\n options: NumberFieldHookOptions & { disableMessage: true }\n): NumberFieldImplementation;\n\n/**\n * @example Enforce Number Value\n * ```tsx\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useNumberField } from \"@react-md/core/form/useNumberField\";\n * import type { ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * const { fieldProps, value } = useNumberField({\n * min: 0,\n * max: 100,\n * name: \"someName\",\n * defaultValue: 0,\n * });\n *\n * // this is safe since `value` will always be a number even if there is a\n * // validation error. since the min and max options were provided as well,\n * // number will be between that range as well.\n * const computed = value * 10;\n *\n * return <TextField {...fieldProps} label=\"Label\" />;\n * }\n * ```\n *\n * @example Enforce Number Value and Deferring Updates\n * ```tsx\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useNumberField } from \"@react-md/core/form/useNumberField\";\n * import type { ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * const { fieldProps, value } = useNumberField({\n * min: 0,\n * max: 100,\n * name: \"someName\",\n * defaultValue: 0,\n * updateValue: \"blur\",\n * });\n *\n * // the `value` will only be updated whenever the `TextField` is blurred.\n * // This is helpful if the `value` is used in expensive computations or\n * // updates that do not need to be updated as the user types\n * const computed = value * 10;\n *\n * return <TextField {...fieldProps} label=\"Label\" />;\n * }\n * ```\n *\n * @see {@link useTextField}\n * @see {@link useNumberField} overrides for other examples.\n */\nexport function useNumberField(\n options: NumberFieldHookOptions & {\n defaultValue: UseStateInitializer<number>;\n }\n): NumberFieldWithMessageImplementation & {\n value: number;\n setState: UseStateSetter<NumberFieldHookState & { value: number }>;\n};\n\n/**\n * The `useNumberField` hook is used to control the state of a `TextField` or\n * `<input type=\"number\">`\n *\n * @example Default Implementation\n * ```tsx\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useNumberField } from \"@react-md/core/form/useNumberField\";\n * import type { ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * const { fieldProps, value } = useNumberField({\n * name: \"someName\",\n * });\n *\n * // `value` will be `undefined` until the user enters a valid value once\n * // there is a valid value, `value` will be a `number`. So this might cause\n * // `computed` to be `NaN | number`\n * //\n * // const computed = value * 10;\n *\n * // whenever there is an error, an error message will be displayed below the\n * // `TextField`\n * return <TextField {...fieldProps} label=\"Label\" />;\n * }\n * ```\n *\n * @example Adding Constraints\n * ```tsx\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useNumberField } from \"@react-md/core/form/useNumberField\";\n * import type { ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * const { fieldProps, value } = useNumberField({\n * name: \"someName\",\n * min: 0,\n * max: 100,\n * step: 2,\n * required: true,\n * });\n *\n * return <TextField {...fieldProps} label=\"Label\" />;\n * }\n * ```\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation\n * @see {@link https://react-md.dev/components/text-field#number | Number TextField Demos}\n * @see {@link https://react-md.dev/hooks/use-number-field | useNumberField Demos}\n * @see {@link useTextField}\n */\nexport function useNumberField(\n options: NumberFieldHookOptions\n): NumberFieldWithMessageImplementation;\n\n/**\n * @internal\n * @see {@link https://react-md.dev/components/text-field#number | Number TextField Demos}\n * @see {@link https://react-md.dev/hooks/use-number-field | useNumberField Demos}\n * @see {@link useTextField}\n * @see {@link useNumberField} overrides for other examples.\n */\nexport function useNumberField(\n options: NumberFieldHookOptions\n): ValidatedNumberFieldImplementation {\n const {\n min,\n max,\n step,\n form,\n onBlur = noop,\n onChange = noop,\n updateValue = \"change\",\n updateValueOnBlur = true,\n disableReset,\n defaultValue,\n ...textOptions\n } = options;\n\n const [number, setNumber] = useState(defaultValue);\n const initial = useRef(number);\n const {\n value: _value,\n reset: resetTextField,\n fieldRef,\n fieldProps,\n setState: setTextFieldState,\n ...remaining\n } = useTextField({\n ...textOptions,\n isNumber: true,\n defaultValue: `${number ?? \"\"}`,\n disableReset: true,\n onBlur(event) {\n onBlur(event);\n if (event.isPropagationStopped()) {\n return;\n }\n\n const input = event.currentTarget;\n input.setCustomValidity(\"\");\n input.checkValidity();\n if (\n !updateValueOnBlur ||\n // do nothing else since it's a weird value like: `\"--0\"` which causes\n // the value to be `\"\"` and `numberAsValue` to be `NaN`\n input.validity.badInput\n ) {\n return;\n }\n\n let value = input.valueAsNumber;\n if (input.value === \"\" && typeof initial.current === \"number\") {\n value = min ?? initial.current;\n }\n\n // can't have both rangeUnderflow and rangeOverflow at the same time, so\n // it's \"safe\" to always provide both\n value = withinRange({ min, max, value });\n if (!Number.isNaN(value)) {\n setNumber(value);\n input.value = `${value}`;\n } else if (typeof initial.current === \"undefined\") {\n setNumber(undefined);\n }\n },\n onChange(event) {\n onChange(event);\n if (event.isPropagationStopped() || updateValue === \"blur\") {\n return;\n }\n\n const input = event.currentTarget;\n input.checkValidity();\n const value = withinRange({\n min,\n max,\n value: event.currentTarget.valueAsNumber,\n });\n if (\n !input.validity.valid &&\n !input.validity.rangeUnderflow &&\n !input.validity.rangeOverflow\n ) {\n return;\n }\n\n if (!Number.isNaN(value)) {\n setNumber(value);\n } else if (initial.current === undefined) {\n setNumber(undefined);\n }\n },\n });\n\n const reset = useCallback(() => {\n resetTextField();\n setNumber(initial.current);\n }, [resetTextField]);\n const setState = useCallback<UseStateSetter<NumberFieldHookState>>(\n (nextState) => {\n if (typeof nextState === \"function\") {\n setNumber((prevNumber) => {\n let nextNumber: number | undefined = prevNumber;\n setTextFieldState((prevState) => {\n const updated = nextState({\n ...prevState,\n value: prevNumber,\n });\n\n nextNumber = updated.value;\n\n return {\n ...updated,\n value: `${nextNumber ?? \"\"}`,\n };\n });\n\n return nextNumber;\n });\n return;\n }\n\n const { value, error, errorMessage } = nextState;\n setNumber(value);\n setTextFieldState({\n value: `${value ?? \"\"}`,\n error,\n errorMessage,\n });\n },\n [setTextFieldState]\n );\n\n useFormReset({\n form,\n elementRef: fieldRef,\n onReset: disableReset ? undefined : reset,\n });\n\n return {\n ...remaining,\n reset,\n value: number,\n setState,\n fieldRef,\n fieldProps: {\n ...fieldProps,\n min,\n max,\n step,\n type: \"number\",\n },\n };\n}\n"],"names":["useCallback","useRef","useState","withinRange","useFormReset","useTextField","noop","useNumberField","options","min","max","step","form","onBlur","onChange","updateValue","updateValueOnBlur","disableReset","defaultValue","textOptions","number","setNumber","initial","value","_value","reset","resetTextField","fieldRef","fieldProps","setState","setTextFieldState","remaining","isNumber","event","isPropagationStopped","input","currentTarget","setCustomValidity","checkValidity","validity","badInput","valueAsNumber","current","Number","isNaN","undefined","valid","rangeUnderflow","rangeOverflow","nextState","prevNumber","nextNumber","prevState","updated","error","errorMessage","elementRef","onReset","type"],"mappings":"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"}
|
|
@@ -8,7 +8,7 @@ import { useCombobox } from "./useCombobox.js";
|
|
|
8
8
|
...comboboxOptions,
|
|
9
9
|
searchable: true,
|
|
10
10
|
extendKeyDown (movementData) {
|
|
11
|
-
const { event, show,
|
|
11
|
+
const { event, show, focusLastRef, visible } = movementData;
|
|
12
12
|
if (visible) {
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
@@ -18,7 +18,7 @@ import { useCombobox } from "./useCombobox.js";
|
|
|
18
18
|
case "End":
|
|
19
19
|
event.preventDefault();
|
|
20
20
|
event.stopPropagation();
|
|
21
|
-
|
|
21
|
+
focusLastRef.current = event.key === "End";
|
|
22
22
|
show();
|
|
23
23
|
break;
|
|
24
24
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/form/useSelectCombobox.ts"],"sourcesContent":["\"use client\";\n\nimport {\n type ComboboxImplementation,\n type ConfigurableComboboxOptions,\n useCombobox,\n} from \"./useCombobox.js\";\n\n/**\n * @since 6.0.0\n */\nexport interface SelectComboboxOptions<\n ComboboxEl extends HTMLElement = HTMLInputElement,\n PopupEl extends HTMLElement = HTMLElement,\n> extends ConfigurableComboboxOptions<ComboboxEl, PopupEl> {\n value: string;\n values: readonly string[];\n}\n\n/**\n * @since 6.0.0\n */\nexport type SelectComboboxImplementation<\n ComboboxEl extends HTMLElement = HTMLInputElement,\n PopupEl extends HTMLElement = HTMLElement,\n> = ComboboxImplementation<ComboboxEl, PopupEl>;\n\n/**\n * @since 6.0.0\n */\nexport function useSelectCombobox<\n ComboboxEl extends HTMLElement = HTMLInputElement,\n PopupEl extends HTMLElement = HTMLElement,\n>(\n options: SelectComboboxOptions<ComboboxEl, PopupEl>\n): SelectComboboxImplementation<ComboboxEl, PopupEl> {\n const { value, values, ...comboboxOptions } = options;\n\n return useCombobox({\n ...comboboxOptions,\n searchable: true,\n extendKeyDown(movementData) {\n const { event, show,
|
|
1
|
+
{"version":3,"sources":["../../src/form/useSelectCombobox.ts"],"sourcesContent":["\"use client\";\n\nimport {\n type ComboboxImplementation,\n type ConfigurableComboboxOptions,\n useCombobox,\n} from \"./useCombobox.js\";\n\n/**\n * @since 6.0.0\n */\nexport interface SelectComboboxOptions<\n ComboboxEl extends HTMLElement = HTMLInputElement,\n PopupEl extends HTMLElement = HTMLElement,\n> extends ConfigurableComboboxOptions<ComboboxEl, PopupEl> {\n value: string;\n values: readonly string[];\n}\n\n/**\n * @since 6.0.0\n */\nexport type SelectComboboxImplementation<\n ComboboxEl extends HTMLElement = HTMLInputElement,\n PopupEl extends HTMLElement = HTMLElement,\n> = ComboboxImplementation<ComboboxEl, PopupEl>;\n\n/**\n * @since 6.0.0\n */\nexport function useSelectCombobox<\n ComboboxEl extends HTMLElement = HTMLInputElement,\n PopupEl extends HTMLElement = HTMLElement,\n>(\n options: SelectComboboxOptions<ComboboxEl, PopupEl>\n): SelectComboboxImplementation<ComboboxEl, PopupEl> {\n const { value, values, ...comboboxOptions } = options;\n\n return useCombobox({\n ...comboboxOptions,\n searchable: true,\n extendKeyDown(movementData) {\n const { event, show, focusLastRef, visible } = movementData;\n if (visible) {\n return;\n }\n\n switch (event.key) {\n case \" \":\n case \"Home\":\n case \"End\":\n event.preventDefault();\n event.stopPropagation();\n focusLastRef.current = event.key === \"End\";\n show();\n break;\n }\n },\n getEnterDefaultFocusedIndex(options) {\n const { focusLast } = options;\n if (focusLast && !value) {\n return values.length - 1;\n }\n\n return Math.max(\n 0,\n values.findIndex((option) => option === value)\n );\n },\n });\n}\n"],"names":["useCombobox","useSelectCombobox","options","value","values","comboboxOptions","searchable","extendKeyDown","movementData","event","show","focusLastRef","visible","key","preventDefault","stopPropagation","current","getEnterDefaultFocusedIndex","focusLast","length","Math","max","findIndex","option"],"mappings":"AAAA;AAEA,SAGEA,WAAW,QACN,mBAAmB;AAqB1B;;CAEC,GACD,OAAO,SAASC,kBAIdC,OAAmD;IAEnD,MAAM,EAAEC,KAAK,EAAEC,MAAM,EAAE,GAAGC,iBAAiB,GAAGH;IAE9C,OAAOF,YAAY;QACjB,GAAGK,eAAe;QAClBC,YAAY;QACZC,eAAcC,YAAY;YACxB,MAAM,EAAEC,KAAK,EAAEC,IAAI,EAAEC,YAAY,EAAEC,OAAO,EAAE,GAAGJ;YAC/C,IAAII,SAAS;gBACX;YACF;YAEA,OAAQH,MAAMI,GAAG;gBACf,KAAK;gBACL,KAAK;gBACL,KAAK;oBACHJ,MAAMK,cAAc;oBACpBL,MAAMM,eAAe;oBACrBJ,aAAaK,OAAO,GAAGP,MAAMI,GAAG,KAAK;oBACrCH;oBACA;YACJ;QACF;QACAO,6BAA4Bf,OAAO;YACjC,MAAM,EAAEgB,SAAS,EAAE,GAAGhB;YACtB,IAAIgB,aAAa,CAACf,OAAO;gBACvB,OAAOC,OAAOe,MAAM,GAAG;YACzB;YAEA,OAAOC,KAAKC,GAAG,CACb,GACAjB,OAAOkB,SAAS,CAAC,CAACC,SAAWA,WAAWpB;QAE5C;IACF;AACF"}
|
|
@@ -7,9 +7,9 @@ import { type GetErrorIcon, type GetErrorMessage, type IsErrored, type TextField
|
|
|
7
7
|
* @since 2.5.0
|
|
8
8
|
* @since 6.0.0 Added the `onInvalid` handler
|
|
9
9
|
*/
|
|
10
|
-
export type TextFieldChangeHandlers<E extends HTMLInputElement | HTMLTextAreaElement> = Pick<HTMLAttributes<E>, "onBlur" | "onChange" | "onInvalid">;
|
|
10
|
+
export type TextFieldChangeHandlers<E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement> = Pick<HTMLAttributes<E>, "onBlur" | "onChange" | "onInvalid">;
|
|
11
11
|
/** @since 6.0.0 */
|
|
12
|
-
export interface ErrorChangeHandlerOptions<E extends HTMLInputElement | HTMLTextAreaElement> {
|
|
12
|
+
export interface ErrorChangeHandlerOptions<E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement> {
|
|
13
13
|
/**
|
|
14
14
|
* A ref containing the `TextField` or `TextArea` if you need access to that
|
|
15
15
|
* DOM node for error reporting.
|
|
@@ -51,7 +51,7 @@ export interface ErrorChangeHandlerOptions<E extends HTMLInputElement | HTMLText
|
|
|
51
51
|
* @since 2.5.0
|
|
52
52
|
* @since 6.0.0 Changed to object argument.
|
|
53
53
|
*/
|
|
54
|
-
export type ErrorChangeHandler<E extends HTMLInputElement | HTMLTextAreaElement> = (options: ErrorChangeHandlerOptions<E>) => void;
|
|
54
|
+
export type ErrorChangeHandler<E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement> = (options: ErrorChangeHandlerOptions<E>) => void;
|
|
55
55
|
/** @since 2.5.6 */
|
|
56
56
|
export interface TextFieldHookState {
|
|
57
57
|
/**
|
|
@@ -83,7 +83,7 @@ export interface ProvidedFormMessageProps extends Pick<FormMessageProps, "id" |
|
|
|
83
83
|
*
|
|
84
84
|
* @since 2.5.0
|
|
85
85
|
*/
|
|
86
|
-
export interface ProvidedTextFieldProps<E extends HTMLInputElement | HTMLTextAreaElement> extends TextFieldValidationOptions, TextFieldChangeHandlers<E>, Required<Pick<TextFieldProps, "id" | "name" | "value" | "error">>, Pick<TextFieldProps, "aria-describedby" | "rightAddon"> {
|
|
86
|
+
export interface ProvidedTextFieldProps<E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement> extends TextFieldValidationOptions, TextFieldChangeHandlers<E>, Required<Pick<TextFieldProps, "id" | "name" | "value" | "error">>, Pick<TextFieldProps, "aria-describedby" | "rightAddon"> {
|
|
87
87
|
/**
|
|
88
88
|
* A ref that must be passed to the `TextField`/`TextArea` so that the custom
|
|
89
89
|
* validity behavior can work.
|
|
@@ -95,54 +95,33 @@ export interface ProvidedTextFieldProps<E extends HTMLInputElement | HTMLTextAre
|
|
|
95
95
|
/**
|
|
96
96
|
* @since 2.5.0
|
|
97
97
|
*/
|
|
98
|
-
export interface ProvidedTextFieldMessageProps<E extends HTMLInputElement | HTMLTextAreaElement> extends ProvidedTextFieldProps<E> {
|
|
98
|
+
export interface ProvidedTextFieldMessageProps<E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement> extends ProvidedTextFieldProps<E> {
|
|
99
99
|
/**
|
|
100
100
|
* These props will be defined as long as the `disableMessage` prop is not
|
|
101
101
|
* `true` from the `useTextField` hook.
|
|
102
102
|
*/
|
|
103
103
|
messageProps: ProvidedFormMessageProps;
|
|
104
104
|
}
|
|
105
|
-
/**
|
|
106
|
-
|
|
105
|
+
/**
|
|
106
|
+
* @since 6.3.0
|
|
107
|
+
*/
|
|
108
|
+
export interface TextFieldHookComponentOptions<E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement> {
|
|
107
109
|
/**
|
|
108
|
-
* An optional id to use for the `TextField` or `TextArea` that
|
|
109
|
-
* to create an id for the inline help/error messages.
|
|
110
|
+
* An optional id to use for the `TextField`, `Password`, or `TextArea` that
|
|
111
|
+
* is also used to create an id for the inline help/error messages.
|
|
110
112
|
*
|
|
111
113
|
* @defaultValue `"text-field-" + useId()`
|
|
112
114
|
*/
|
|
113
115
|
id?: string;
|
|
114
116
|
/**
|
|
115
|
-
*
|
|
116
|
-
* This should really only be used if you are making a custom component using
|
|
117
|
-
* this hook and forwarding refs. If you need a ref to access the `<input>` or
|
|
118
|
-
* `<textarea>` DOM node, you can use the `fieldRef` returned by this hook
|
|
119
|
-
* instead.
|
|
120
|
-
*
|
|
121
|
-
* @example Accessing TextField DOM Node
|
|
122
|
-
* ```tsx
|
|
123
|
-
* import { TextField } from "@react-md/core/form/TextField";
|
|
124
|
-
* import { useTextField } from "@react-md/core/form/useTextField";
|
|
125
|
-
* import { useEffect } from "react";
|
|
126
|
-
* import type { ReactElement } from "react";
|
|
127
|
-
*
|
|
128
|
-
* function Example(): ReactElement {
|
|
129
|
-
* const { fieldRef, fieldProps } = useTextField({ name: "example" });
|
|
130
|
-
*
|
|
131
|
-
* useEffect(() => {
|
|
132
|
-
* fieldRef.current;
|
|
133
|
-
* // ^ HTMLInputElement | null
|
|
134
|
-
* }, [fieldRef]);
|
|
135
|
-
*
|
|
136
|
-
* return <TextField {...fieldProps} label="Example" />;
|
|
137
|
-
* }
|
|
138
|
-
* ```
|
|
139
|
-
*/
|
|
140
|
-
ref?: Ref<E>;
|
|
141
|
-
/**
|
|
142
|
-
* A unique name to attach to the `TextField`, `TextArea` or `Password`
|
|
117
|
+
* A unique name to attach to the `TextField`, `TextArea`, or `Password`
|
|
143
118
|
* component.
|
|
144
119
|
*/
|
|
145
120
|
name: string;
|
|
121
|
+
/**
|
|
122
|
+
* @since 6.3.0
|
|
123
|
+
*/
|
|
124
|
+
form?: string;
|
|
146
125
|
/**
|
|
147
126
|
* Boolean if the `FormMessage` should also display a counter for the
|
|
148
127
|
* remaining letters allowed based on the `maxLength`.
|
|
@@ -153,23 +132,6 @@ export interface TextFieldHookOptions<E extends HTMLInputElement | HTMLTextAreaE
|
|
|
153
132
|
* @defaultValue `false`
|
|
154
133
|
*/
|
|
155
134
|
counter?: boolean;
|
|
156
|
-
/**
|
|
157
|
-
* This is used internally for the `useNumberField` hook and probably
|
|
158
|
-
* shouldn't be used otherwise. This is just passed into the
|
|
159
|
-
* {@link getErrorMessage} options and is used for additional validation.
|
|
160
|
-
*
|
|
161
|
-
* @defaultValue `false`
|
|
162
|
-
*/
|
|
163
|
-
isNumber?: boolean;
|
|
164
|
-
/**
|
|
165
|
-
* The default value to use for the `TextField` or `TextArea` one initial
|
|
166
|
-
* render. If you want to manually change the value to something else after
|
|
167
|
-
* the initial render, either change the `key` for the component containing
|
|
168
|
-
* this hook, or use the `setState` function returned from this hook.
|
|
169
|
-
*
|
|
170
|
-
* @defaultValue `""`
|
|
171
|
-
*/
|
|
172
|
-
defaultValue?: UseStateInitializer<string>;
|
|
173
135
|
/**
|
|
174
136
|
* An optional help text to display in the `FormMessage` component when there
|
|
175
137
|
* is not an error.
|
|
@@ -214,6 +176,14 @@ export interface TextFieldHookOptions<E extends HTMLInputElement | HTMLTextAreaE
|
|
|
214
176
|
* @defaultValue `() => {}`
|
|
215
177
|
*/
|
|
216
178
|
onErrorChange?: ErrorChangeHandler<E>;
|
|
179
|
+
/**
|
|
180
|
+
* Set to `true` to prevent the state from automatically resetting with a
|
|
181
|
+
* form's `reset` event.
|
|
182
|
+
*
|
|
183
|
+
* @defaultValue `false`
|
|
184
|
+
* @since 6.3.0
|
|
185
|
+
*/
|
|
186
|
+
disableReset?: boolean;
|
|
217
187
|
/**
|
|
218
188
|
* Set this to `true` to prevent the `errorMessage` from being
|
|
219
189
|
* rendered inline below the `TextField`.
|
|
@@ -238,19 +208,66 @@ export interface TextFieldHookOptions<E extends HTMLInputElement | HTMLTextAreaE
|
|
|
238
208
|
*/
|
|
239
209
|
validationType?: TextFieldValidationType;
|
|
240
210
|
}
|
|
211
|
+
/** @since 2.5.6 */
|
|
212
|
+
export interface TextFieldHookOptions<E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement> extends TextFieldValidationOptions, TextFieldHookComponentOptions<E>, TextFieldChangeHandlers<E> {
|
|
213
|
+
/**
|
|
214
|
+
* An optional ref that should be merged with the ref returned by this hook.
|
|
215
|
+
* This should really only be used if you are making a custom component using
|
|
216
|
+
* this hook and forwarding refs. If you need a ref to access the `<input>` or
|
|
217
|
+
* `<textarea>` DOM node, you can use the `fieldRef` returned by this hook
|
|
218
|
+
* instead.
|
|
219
|
+
*
|
|
220
|
+
* @example Accessing TextField DOM Node
|
|
221
|
+
* ```tsx
|
|
222
|
+
* import { TextField } from "@react-md/core/form/TextField";
|
|
223
|
+
* import { useTextField } from "@react-md/core/form/useTextField";
|
|
224
|
+
* import { useEffect } from "react";
|
|
225
|
+
* import type { ReactElement } from "react";
|
|
226
|
+
*
|
|
227
|
+
* function Example(): ReactElement {
|
|
228
|
+
* const { fieldRef, fieldProps } = useTextField({ name: "example" });
|
|
229
|
+
*
|
|
230
|
+
* useEffect(() => {
|
|
231
|
+
* fieldRef.current;
|
|
232
|
+
* // ^ HTMLInputElement | null
|
|
233
|
+
* }, [fieldRef]);
|
|
234
|
+
*
|
|
235
|
+
* return <TextField {...fieldProps} label="Example" />;
|
|
236
|
+
* }
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
ref?: Ref<E>;
|
|
240
|
+
/**
|
|
241
|
+
* This is used internally for the `useNumberField` hook and probably
|
|
242
|
+
* shouldn't be used otherwise. This is just passed into the
|
|
243
|
+
* {@link getErrorMessage} options and is used for additional validation.
|
|
244
|
+
*
|
|
245
|
+
* @defaultValue `false`
|
|
246
|
+
*/
|
|
247
|
+
isNumber?: boolean;
|
|
248
|
+
/**
|
|
249
|
+
* The default value to use for the `TextField` or `TextArea` one initial
|
|
250
|
+
* render. If you want to manually change the value to something else after
|
|
251
|
+
* the initial render, either change the `key` for the component containing
|
|
252
|
+
* this hook, or use the `setState` function returned from this hook.
|
|
253
|
+
*
|
|
254
|
+
* @defaultValue `""`
|
|
255
|
+
*/
|
|
256
|
+
defaultValue?: UseStateInitializer<string>;
|
|
257
|
+
}
|
|
241
258
|
/** @since 6.0.0 */
|
|
242
|
-
export interface TextFieldImplementation<E extends HTMLInputElement | HTMLTextAreaElement> extends TextFieldHookState {
|
|
259
|
+
export interface TextFieldImplementation<E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement> extends TextFieldHookState {
|
|
243
260
|
fieldRef: RefObject<E>;
|
|
244
261
|
reset: () => void;
|
|
245
262
|
setState: UseStateSetter<Readonly<TextFieldHookState>>;
|
|
246
263
|
fieldProps: ProvidedTextFieldProps<E>;
|
|
247
264
|
}
|
|
248
265
|
/** @since 6.0.0 */
|
|
249
|
-
export interface TextFieldWithMessageImplementation<E extends HTMLInputElement | HTMLTextAreaElement> extends TextFieldImplementation<E> {
|
|
266
|
+
export interface TextFieldWithMessageImplementation<E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement> extends TextFieldImplementation<E> {
|
|
250
267
|
fieldProps: ProvidedTextFieldMessageProps<E>;
|
|
251
268
|
}
|
|
252
269
|
/** @since 6.0.0 */
|
|
253
|
-
export interface ValidatedTextFieldImplementation<E extends HTMLInputElement | HTMLTextAreaElement> extends TextFieldImplementation<E> {
|
|
270
|
+
export interface ValidatedTextFieldImplementation<E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement> extends TextFieldImplementation<E> {
|
|
254
271
|
fieldProps: ProvidedTextFieldProps<E> | ProvidedTextFieldMessageProps<E>;
|
|
255
272
|
}
|
|
256
273
|
/**
|
|
@@ -278,7 +295,7 @@ export interface ValidatedTextFieldImplementation<E extends HTMLInputElement | H
|
|
|
278
295
|
* @see {@link https://react-md.dev/components/text-field | TextField Demos}
|
|
279
296
|
* @see {@link https://react-md.dev/hooks/use-text-field | useTextField Demos}
|
|
280
297
|
*/
|
|
281
|
-
export declare function useTextField<E extends HTMLInputElement | HTMLTextAreaElement>(options: TextFieldHookOptions<E> & {
|
|
298
|
+
export declare function useTextField<E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement>(options: TextFieldHookOptions<E> & {
|
|
282
299
|
disableMessage: true;
|
|
283
300
|
}): TextFieldImplementation<E>;
|
|
284
301
|
/**
|
|
@@ -393,4 +410,4 @@ export declare function useTextField<E extends HTMLInputElement | HTMLTextAreaEl
|
|
|
393
410
|
* added the ability to display an inline counter and help text while disabling
|
|
394
411
|
* the error messaging.
|
|
395
412
|
*/
|
|
396
|
-
export declare function useTextField<E extends HTMLInputElement | HTMLTextAreaElement>(options: TextFieldHookOptions<E>): TextFieldWithMessageImplementation<E>;
|
|
413
|
+
export declare function useTextField<E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement>(options: TextFieldHookOptions<E>): TextFieldWithMessageImplementation<E>;
|
|
@@ -3,6 +3,7 @@ import { useCallback, useRef, useState } from "react";
|
|
|
3
3
|
import { getIcon } from "../icon/config.js";
|
|
4
4
|
import { useEnsuredId } from "../useEnsuredId.js";
|
|
5
5
|
import { useEnsuredRef } from "../useEnsuredRef.js";
|
|
6
|
+
import { useFormReset } from "./useFormReset.js";
|
|
6
7
|
import { defaultGetErrorIcon, defaultGetErrorMessage, defaultIsErrored } from "./validation.js";
|
|
7
8
|
const noop = ()=>{
|
|
8
9
|
// do nothing
|
|
@@ -12,7 +13,7 @@ const noop = ()=>{
|
|
|
12
13
|
* @see {@link https://react-md.dev/hooks/use-text-field | useTextField Demos}
|
|
13
14
|
* @since 6.0.0
|
|
14
15
|
*/ export function useTextField(options) {
|
|
15
|
-
const { id: propId, ref: propRef, name, defaultValue = "", isNumber = false, required, pattern, minLength, maxLength, onBlur = noop, onChange = noop, onInvalid = noop, counter = false, helpText, validationType = "recommended", disableMessage = false, disableMaxLength = false, errorIcon: propErrorIcon, isErrored = defaultIsErrored, onErrorChange = noop, getErrorIcon = defaultGetErrorIcon, getErrorMessage = defaultGetErrorMessage } = options;
|
|
16
|
+
const { id: propId, ref: propRef, name, form, defaultValue = "", isNumber = false, required, pattern, minLength, maxLength, onBlur = noop, onChange = noop, onInvalid = noop, counter = false, helpText, disableReset, validationType = "recommended", disableMessage = false, disableMaxLength = false, errorIcon: propErrorIcon, isErrored = defaultIsErrored, onErrorChange = noop, getErrorIcon = defaultGetErrorIcon, getErrorMessage = defaultGetErrorMessage } = options;
|
|
16
17
|
const id = useEnsuredId(propId, "text-field");
|
|
17
18
|
const messageId = `${id}-message`;
|
|
18
19
|
const [fieldRef, ref] = useEnsuredRef(propRef);
|
|
@@ -179,6 +180,11 @@ const noop = ()=>{
|
|
|
179
180
|
children: !disableMessage && errorMessage || helpText
|
|
180
181
|
};
|
|
181
182
|
}
|
|
183
|
+
useFormReset({
|
|
184
|
+
form,
|
|
185
|
+
onReset: disableReset ? undefined : reset,
|
|
186
|
+
elementRef: fieldRef
|
|
187
|
+
});
|
|
182
188
|
return {
|
|
183
189
|
...state,
|
|
184
190
|
reset,
|