@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.
Files changed (153) hide show
  1. package/dist/datetime/NativeDateField.d.ts +24 -0
  2. package/dist/datetime/NativeDateField.js +63 -0
  3. package/dist/datetime/NativeDateField.js.map +1 -0
  4. package/dist/datetime/NativeTimeField.d.ts +26 -0
  5. package/dist/datetime/NativeTimeField.js +63 -0
  6. package/dist/datetime/NativeTimeField.js.map +1 -0
  7. package/dist/datetime/useDateField.d.ts +120 -0
  8. package/dist/datetime/useDateField.js +35 -0
  9. package/dist/datetime/useDateField.js.map +1 -0
  10. package/dist/datetime/useTimeField.d.ts +124 -0
  11. package/dist/datetime/useTimeField.js +65 -0
  12. package/dist/datetime/useTimeField.js.map +1 -0
  13. package/dist/datetime/utils.d.ts +34 -0
  14. package/dist/datetime/utils.js +27 -0
  15. package/dist/datetime/utils.js.map +1 -0
  16. package/dist/draggable/utils.d.ts +3 -6
  17. package/dist/draggable/utils.js.map +1 -1
  18. package/dist/expansion-panel/ExpansionList.js +1 -1
  19. package/dist/expansion-panel/ExpansionList.js.map +1 -1
  20. package/dist/expansion-panel/useExpansionList.d.ts +2 -7
  21. package/dist/expansion-panel/useExpansionList.js.map +1 -1
  22. package/dist/form/FormMessage.js +3 -1
  23. package/dist/form/FormMessage.js.map +1 -1
  24. package/dist/form/FormMessageContainer.d.ts +2 -1
  25. package/dist/form/FormMessageContainer.js +3 -2
  26. package/dist/form/FormMessageContainer.js.map +1 -1
  27. package/dist/form/FormMessageCounter.d.ts +3 -2
  28. package/dist/form/FormMessageCounter.js +5 -2
  29. package/dist/form/FormMessageCounter.js.map +1 -1
  30. package/dist/form/Listbox.d.ts +3 -10
  31. package/dist/form/Listbox.js +8 -27
  32. package/dist/form/Listbox.js.map +1 -1
  33. package/dist/form/ListboxProvider.d.ts +17 -0
  34. package/dist/form/ListboxProvider.js +33 -1
  35. package/dist/form/ListboxProvider.js.map +1 -1
  36. package/dist/form/NativeSelect.js +1 -0
  37. package/dist/form/NativeSelect.js.map +1 -1
  38. package/dist/form/TextArea.js +1 -0
  39. package/dist/form/TextArea.js.map +1 -1
  40. package/dist/form/TextField.js +1 -0
  41. package/dist/form/TextField.js.map +1 -1
  42. package/dist/form/_form-message.scss +13 -0
  43. package/dist/form/_text-field.scss +12 -3
  44. package/dist/form/formMessageContainerStyles.d.ts +7 -0
  45. package/dist/form/formMessageContainerStyles.js +4 -2
  46. package/dist/form/formMessageContainerStyles.js.map +1 -1
  47. package/dist/form/sliderUtils.d.ts +3 -7
  48. package/dist/form/sliderUtils.js.map +1 -1
  49. package/dist/form/types.d.ts +13 -0
  50. package/dist/form/types.js.map +1 -1
  51. package/dist/form/useCombobox.d.ts +6 -2
  52. package/dist/form/useCombobox.js +8 -9
  53. package/dist/form/useCombobox.js.map +1 -1
  54. package/dist/form/useFormReset.d.ts +4 -1
  55. package/dist/form/useFormReset.js +9 -4
  56. package/dist/form/useFormReset.js.map +1 -1
  57. package/dist/form/useNumberField.d.ts +5 -5
  58. package/dist/form/useNumberField.js +10 -2
  59. package/dist/form/useNumberField.js.map +1 -1
  60. package/dist/form/useSelectCombobox.js +2 -2
  61. package/dist/form/useSelectCombobox.js.map +1 -1
  62. package/dist/form/useTextField.d.ts +76 -59
  63. package/dist/form/useTextField.js +7 -1
  64. package/dist/form/useTextField.js.map +1 -1
  65. package/dist/interaction/utils.d.ts +14 -0
  66. package/dist/interaction/utils.js +23 -12
  67. package/dist/interaction/utils.js.map +1 -1
  68. package/dist/menu/MenuBar.js +1 -1
  69. package/dist/menu/MenuBar.js.map +1 -1
  70. package/dist/menu/MenuItemTextField.d.ts +1 -2
  71. package/dist/menu/MenuItemTextField.js.map +1 -1
  72. package/dist/menu/MenuWidget.js +3 -2
  73. package/dist/menu/MenuWidget.js.map +1 -1
  74. package/dist/movement/constants.d.ts +10 -0
  75. package/dist/movement/constants.js +20 -4
  76. package/dist/movement/constants.js.map +1 -1
  77. package/dist/movement/types.d.ts +59 -10
  78. package/dist/movement/types.js.map +1 -1
  79. package/dist/movement/useKeyboardMovementProvider.d.ts +5 -1
  80. package/dist/movement/useKeyboardMovementProvider.js +171 -73
  81. package/dist/movement/useKeyboardMovementProvider.js.map +1 -1
  82. package/dist/tabs/useTabList.js +1 -1
  83. package/dist/tabs/useTabList.js.map +1 -1
  84. package/dist/test-utils/drag.d.ts +6 -9
  85. package/dist/transition/useCarousel.d.ts +2 -2
  86. package/dist/transition/useCarousel.js.map +1 -1
  87. package/dist/tree/Tree.js +1 -1
  88. package/dist/tree/Tree.js.map +1 -1
  89. package/dist/tree/useTreeMovement.d.ts +2 -1
  90. package/dist/tree/useTreeMovement.js +2 -1
  91. package/dist/tree/useTreeMovement.js.map +1 -1
  92. package/dist/types.d.ts +14 -0
  93. package/dist/types.js.map +1 -1
  94. package/dist/utils/getMiddleOfRange.d.ts +2 -3
  95. package/dist/utils/getMiddleOfRange.js.map +1 -1
  96. package/dist/utils/getPercentage.d.ts +2 -9
  97. package/dist/utils/getPercentage.js +1 -1
  98. package/dist/utils/getPercentage.js.map +1 -1
  99. package/dist/utils/getRangeSteps.d.ts +2 -3
  100. package/dist/utils/getRangeSteps.js +0 -3
  101. package/dist/utils/getRangeSteps.js.map +1 -1
  102. package/dist/utils/nearest.d.ts +2 -3
  103. package/dist/utils/nearest.js +0 -3
  104. package/dist/utils/nearest.js.map +1 -1
  105. package/dist/utils/trigonometry.d.ts +31 -0
  106. package/dist/utils/trigonometry.js +25 -0
  107. package/dist/utils/trigonometry.js.map +1 -0
  108. package/dist/window-splitter/useWindowSplitter.d.ts +1 -1
  109. package/dist/window-splitter/useWindowSplitter.js.map +1 -1
  110. package/package.json +1 -1
  111. package/src/datetime/NativeDateField.tsx +92 -0
  112. package/src/datetime/NativeTimeField.tsx +94 -0
  113. package/src/datetime/useDateField.ts +193 -0
  114. package/src/datetime/useTimeField.ts +233 -0
  115. package/src/datetime/utils.ts +48 -0
  116. package/src/draggable/utils.ts +3 -6
  117. package/src/expansion-panel/ExpansionList.tsx +2 -1
  118. package/src/expansion-panel/useExpansionList.ts +6 -12
  119. package/src/form/FormMessage.tsx +4 -0
  120. package/src/form/FormMessageContainer.tsx +8 -4
  121. package/src/form/FormMessageCounter.tsx +17 -6
  122. package/src/form/Listbox.tsx +18 -46
  123. package/src/form/ListboxProvider.ts +61 -1
  124. package/src/form/NativeSelect.tsx +1 -0
  125. package/src/form/TextArea.tsx +1 -0
  126. package/src/form/TextField.tsx +1 -0
  127. package/src/form/formMessageContainerStyles.ts +10 -2
  128. package/src/form/sliderUtils.ts +3 -7
  129. package/src/form/types.ts +15 -0
  130. package/src/form/useCombobox.ts +15 -10
  131. package/src/form/useFormReset.ts +12 -5
  132. package/src/form/useNumberField.ts +17 -14
  133. package/src/form/useSelectCombobox.ts +2 -2
  134. package/src/form/useTextField.ts +102 -69
  135. package/src/interaction/utils.ts +18 -20
  136. package/src/menu/MenuBar.tsx +1 -1
  137. package/src/menu/MenuItemTextField.tsx +1 -3
  138. package/src/menu/MenuWidget.tsx +4 -2
  139. package/src/movement/constants.ts +26 -4
  140. package/src/movement/types.ts +84 -19
  141. package/src/movement/useKeyboardMovementProvider.ts +209 -95
  142. package/src/tabs/useTabList.ts +1 -1
  143. package/src/test-utils/drag.ts +8 -12
  144. package/src/transition/useCarousel.ts +2 -2
  145. package/src/tree/Tree.tsx +1 -1
  146. package/src/tree/useTreeMovement.ts +4 -0
  147. package/src/types.ts +16 -0
  148. package/src/utils/getMiddleOfRange.ts +2 -3
  149. package/src/utils/getPercentage.ts +3 -11
  150. package/src/utils/getRangeSteps.ts +3 -3
  151. package/src/utils/nearest.ts +3 -3
  152. package/src/utils/trigonometry.ts +46 -0
  153. 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
- triggerManualChangeEvent(element, defaultValue);
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: string;\n}\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport function useFormReset(options: FormResetOptions): void {\n const { form, elementRef, defaultValue } = options;\n\n useEffect(() => {\n const element = elementRef.current;\n if (!element) {\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 triggerManualChangeEvent(element, defaultValue);\n };\n\n formElement.addEventListener(\"reset\", handleReset);\n return () => {\n formElement.removeEventListener(\"reset\", handleReset);\n };\n }, [defaultValue, elementRef, form]);\n}\n"],"names":["useEffect","triggerManualChangeEvent","useFormReset","options","form","elementRef","defaultValue","element","current","formElement","document","getElementById","closest","handleReset","addEventListener","removeEventListener"],"mappings":"AAAA;AAEA,SAAyBA,SAAS,QAAQ,QAAQ;AAElD,SAEEC,wBAAwB,QACnB,aAAa;AAYpB;;;CAGC,GACD,OAAO,SAASC,aAAaC,OAAyB;IACpD,MAAM,EAAEC,IAAI,EAAEC,UAAU,EAAEC,YAAY,EAAE,GAAGH;IAE3CH,UAAU;QACR,MAAMO,UAAUF,WAAWG,OAAO;QAClC,IAAI,CAACD,SAAS;YACZ;QACF;QAEA,MAAME,cACJ,AAACL,QAAQM,SAASC,cAAc,CAACP,SACjCG,QAAQK,OAAO,CAAkB,WACjC;QACF,IAAI,CAACH,aAAa;YAChB;QACF;QAEA,MAAMI,cAAc;YAClBZ,yBAAyBM,SAASD;QACpC;QAEAG,YAAYK,gBAAgB,CAAC,SAASD;QACtC,OAAO;YACLJ,YAAYM,mBAAmB,CAAC,SAASF;QAC3C;IACF,GAAG;QAACP;QAAcD;QAAYD;KAAK;AACrC"}
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<HTMLInputElement>, "defaultValue" | "isNumber">, NumberFieldConstraints {
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<HTMLInputElement>, NumberFieldConstraints {
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<HTMLInputElement>, NumberFieldConstraints {
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<HTMLInputElement>, "value" | "setState"> {
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<HTMLInputElement>, "value" | "setState"> {
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":"AAAA;AAEA,SAASA,WAAW,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,QAAQ;AAGtD,SAASC,WAAW,QAAQ,0BAA0B;AACtD,SAOEC,YAAY,QACP,oBAAoB;AAE3B,MAAMC,OAAO;AACX,aAAa;AACf;AA2UA;;;;;;CAMC,GACD,OAAO,SAASC,eACdC,OAA+B;IAE/B,MAAM,EACJC,GAAG,EACHC,GAAG,EACHC,IAAI,EACJC,SAASN,IAAI,EACbO,WAAWP,IAAI,EACfQ,cAAc,QAAQ,EACtBC,oBAAoB,IAAI,EACxBC,YAAY,EACZ,GAAGC,aACJ,GAAGT;IAEJ,MAAM,CAACU,QAAQC,UAAU,GAAGhB,SAASa;IACrC,MAAMI,UAAUlB,OAAOgB;IACvB,MAAM,EACJG,OAAOC,MAAM,EACbC,OAAOC,cAAc,EACrBC,UAAU,EACVC,UAAUC,iBAAiB,EAC3B,GAAGC,WACJ,GAAGvB,aAAa;QACf,GAAGY,WAAW;QACdY,UAAU;QACVb,cAAc,GAAGE,UAAU,IAAI;QAC/BN,QAAOkB,KAAK;YACVlB,OAAOkB;YACP,IAAIA,MAAMC,oBAAoB,IAAI;gBAChC;YACF;YAEA,MAAMC,QAAQF,MAAMG,aAAa;YACjCD,MAAME,iBAAiB,CAAC;YACxBF,MAAMG,aAAa;YACnB,IACE,CAACpB,qBACD,sEAAsE;YACtE,uDAAuD;YACvDiB,MAAMI,QAAQ,CAACC,QAAQ,EACvB;gBACA;YACF;YAEA,IAAIhB,QAAQW,MAAMM,aAAa;YAC/B,IAAIN,MAAMX,KAAK,KAAK,MAAM,OAAOD,QAAQmB,OAAO,KAAK,UAAU;gBAC7DlB,QAAQZ,OAAOW,QAAQmB,OAAO;YAChC;YAEA,wEAAwE;YACxE,qCAAqC;YACrClB,QAAQjB,YAAY;gBAAEK;gBAAKC;gBAAKW;YAAM;YACtC,IAAI,CAACmB,OAAOC,KAAK,CAACpB,QAAQ;gBACxBF,UAAUE;gBACVW,MAAMX,KAAK,GAAG,GAAGA,OAAO;YAC1B,OAAO,IAAI,OAAOD,QAAQmB,OAAO,KAAK,aAAa;gBACjDpB,UAAUuB;YACZ;QACF;QACA7B,UAASiB,KAAK;YACZjB,SAASiB;YACT,IAAIA,MAAMC,oBAAoB,MAAMjB,gBAAgB,QAAQ;gBAC1D;YACF;YAEA,MAAMkB,QAAQF,MAAMG,aAAa;YACjCD,MAAMG,aAAa;YACnB,MAAMd,QAAQjB,YAAY;gBACxBK;gBACAC;gBACAW,OAAOS,MAAMG,aAAa,CAACK,aAAa;YAC1C;YACA,IACE,CAACN,MAAMI,QAAQ,CAACO,KAAK,IACrB,CAACX,MAAMI,QAAQ,CAACQ,cAAc,IAC9B,CAACZ,MAAMI,QAAQ,CAACS,aAAa,EAC7B;gBACA;YACF;YAEA,IAAI,CAACL,OAAOC,KAAK,CAACpB,QAAQ;gBACxBF,UAAUE;YACZ,OAAO,IAAID,QAAQmB,OAAO,KAAKG,WAAW;gBACxCvB,UAAUuB;YACZ;QACF;IACF;IAEA,MAAMnB,QAAQtB,YAAY;QACxBuB;QACAL,UAAUC,QAAQmB,OAAO;IAC3B,GAAG;QAACf;KAAe;IACnB,MAAME,WAAWzB,YACf,CAAC6C;QACC,IAAI,OAAOA,cAAc,YAAY;YACnC3B,UAAU,CAAC4B;gBACT,IAAIC,aAAiCD;gBACrCpB,kBAAkB,CAACsB;oBACjB,MAAMC,UAAUJ,UAAU;wBACxB,GAAGG,SAAS;wBACZ5B,OAAO0B;oBACT;oBAEAC,aAAaE,QAAQ7B,KAAK;oBAE1B,OAAO;wBACL,GAAG6B,OAAO;wBACV7B,OAAO,GAAG2B,cAAc,IAAI;oBAC9B;gBACF;gBAEA,OAAOA;YACT;YACA;QACF;QAEA,MAAM,EAAE3B,KAAK,EAAE8B,KAAK,EAAEC,YAAY,EAAE,GAAGN;QACvC3B,UAAUE;QACVM,kBAAkB;YAChBN,OAAO,GAAGA,SAAS,IAAI;YACvB8B;YACAC;QACF;IACF,GACA;QAACzB;KAAkB;IAGrB,OAAO;QACL,GAAGC,SAAS;QACZL;QACAF,OAAOH;QACPQ;QACAD,YAAY;YACV,GAAGA,UAAU;YACbhB;YACAC;YACAC;YACA0C,MAAM;QACR;IACF;AACF"}
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, focusLast, visible } = movementData;
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
- focusLast.current = event.key === "End";
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, focusLast, 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 focusLast.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","focusLast","visible","key","preventDefault","stopPropagation","current","getEnterDefaultFocusedIndex","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,SAAS,EAAEC,OAAO,EAAE,GAAGJ;YAC5C,IAAII,SAAS;gBACX;YACF;YAEA,OAAQH,MAAMI,GAAG;gBACf,KAAK;gBACL,KAAK;gBACL,KAAK;oBACHJ,MAAMK,cAAc;oBACpBL,MAAMM,eAAe;oBACrBJ,UAAUK,OAAO,GAAGP,MAAMI,GAAG,KAAK;oBAClCH;oBACA;YACJ;QACF;QACAO,6BAA4Bf,OAAO;YACjC,MAAM,EAAES,SAAS,EAAE,GAAGT;YACtB,IAAIS,aAAa,CAACR,OAAO;gBACvB,OAAOC,OAAOc,MAAM,GAAG;YACzB;YAEA,OAAOC,KAAKC,GAAG,CACb,GACAhB,OAAOiB,SAAS,CAAC,CAACC,SAAWA,WAAWnB;QAE5C;IACF;AACF"}
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
- /** @since 2.5.6 */
106
- export interface TextFieldHookOptions<E extends HTMLInputElement | HTMLTextAreaElement> extends TextFieldValidationOptions, TextFieldChangeHandlers<E> {
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 is also used
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
- * An optional ref that should be merged with the ref returned by this hook.
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,