@mortenbrudvik/waveui 0.8.0 → 0.9.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 (132) hide show
  1. package/README.md +231 -85
  2. package/dist/components/data-display/InfoButton.cjs +2 -0
  3. package/dist/components/data-display/InfoButton.cjs.map +1 -0
  4. package/dist/components/data-display/InfoButton.mjs +173 -0
  5. package/dist/components/data-display/InfoButton.mjs.map +1 -0
  6. package/dist/components/data-display/InfoLabel.cjs +1 -1
  7. package/dist/components/data-display/InfoLabel.cjs.map +1 -1
  8. package/dist/components/data-display/InfoLabel.mjs +39 -96
  9. package/dist/components/data-display/InfoLabel.mjs.map +1 -1
  10. package/dist/components/input/Checkbox.cjs +1 -1
  11. package/dist/components/input/Checkbox.cjs.map +1 -1
  12. package/dist/components/input/Checkbox.mjs +91 -75
  13. package/dist/components/input/Checkbox.mjs.map +1 -1
  14. package/dist/components/input/ColorPicker.parts.cjs +1 -1
  15. package/dist/components/input/ColorPicker.parts.cjs.map +1 -1
  16. package/dist/components/input/ColorPicker.parts.mjs +1 -1
  17. package/dist/components/input/ColorPicker.parts.mjs.map +1 -1
  18. package/dist/components/input/Combobox.cjs +1 -1
  19. package/dist/components/input/Combobox.cjs.map +1 -1
  20. package/dist/components/input/Combobox.expand.cjs +1 -1
  21. package/dist/components/input/Combobox.expand.cjs.map +1 -1
  22. package/dist/components/input/Combobox.expand.mjs +20 -20
  23. package/dist/components/input/Combobox.expand.mjs.map +1 -1
  24. package/dist/components/input/Combobox.mjs +143 -138
  25. package/dist/components/input/Combobox.mjs.map +1 -1
  26. package/dist/components/input/DatePicker.cjs +1 -1
  27. package/dist/components/input/DatePicker.cjs.map +1 -1
  28. package/dist/components/input/DatePicker.mjs +213 -209
  29. package/dist/components/input/DatePicker.mjs.map +1 -1
  30. package/dist/components/input/Dropdown.cjs +1 -1
  31. package/dist/components/input/Dropdown.cjs.map +1 -1
  32. package/dist/components/input/Dropdown.mjs +80 -74
  33. package/dist/components/input/Dropdown.mjs.map +1 -1
  34. package/dist/components/input/Field.cjs +1 -1
  35. package/dist/components/input/Field.cjs.map +1 -1
  36. package/dist/components/input/Field.mjs +99 -66
  37. package/dist/components/input/Field.mjs.map +1 -1
  38. package/dist/components/input/Input.cjs +1 -1
  39. package/dist/components/input/Input.cjs.map +1 -1
  40. package/dist/components/input/Input.mjs +79 -55
  41. package/dist/components/input/Input.mjs.map +1 -1
  42. package/dist/components/input/Label.cjs +1 -1
  43. package/dist/components/input/Label.cjs.map +1 -1
  44. package/dist/components/input/Label.mjs +19 -13
  45. package/dist/components/input/Label.mjs.map +1 -1
  46. package/dist/components/input/Rating.cjs +1 -1
  47. package/dist/components/input/Rating.cjs.map +1 -1
  48. package/dist/components/input/Rating.item.cjs +2 -0
  49. package/dist/components/input/Rating.item.cjs.map +1 -0
  50. package/dist/components/input/Rating.item.mjs +255 -0
  51. package/dist/components/input/Rating.item.mjs.map +1 -0
  52. package/dist/components/input/Rating.mjs +150 -151
  53. package/dist/components/input/Rating.mjs.map +1 -1
  54. package/dist/components/input/SearchBox.cjs +1 -1
  55. package/dist/components/input/SearchBox.cjs.map +1 -1
  56. package/dist/components/input/SearchBox.mjs +146 -115
  57. package/dist/components/input/SearchBox.mjs.map +1 -1
  58. package/dist/components/input/Select.cjs +1 -1
  59. package/dist/components/input/Select.cjs.map +1 -1
  60. package/dist/components/input/Select.mjs +34 -25
  61. package/dist/components/input/Select.mjs.map +1 -1
  62. package/dist/components/input/Slider.cjs +1 -1
  63. package/dist/components/input/Slider.cjs.map +1 -1
  64. package/dist/components/input/Slider.mjs +77 -54
  65. package/dist/components/input/Slider.mjs.map +1 -1
  66. package/dist/components/input/SpinButton.cjs +1 -1
  67. package/dist/components/input/SpinButton.cjs.map +1 -1
  68. package/dist/components/input/SpinButton.mjs +198 -96
  69. package/dist/components/input/SpinButton.mjs.map +1 -1
  70. package/dist/components/input/Switch.cjs +1 -1
  71. package/dist/components/input/Switch.cjs.map +1 -1
  72. package/dist/components/input/Switch.mjs +71 -55
  73. package/dist/components/input/Switch.mjs.map +1 -1
  74. package/dist/components/input/TagPicker.cjs +1 -1
  75. package/dist/components/input/TagPicker.cjs.map +1 -1
  76. package/dist/components/input/TagPicker.mjs +142 -116
  77. package/dist/components/input/TagPicker.mjs.map +1 -1
  78. package/dist/components/input/Textarea.cjs +1 -1
  79. package/dist/components/input/Textarea.cjs.map +1 -1
  80. package/dist/components/input/Textarea.mjs +28 -21
  81. package/dist/components/input/Textarea.mjs.map +1 -1
  82. package/dist/components/input/TimePicker.cjs +1 -1
  83. package/dist/components/input/TimePicker.cjs.map +1 -1
  84. package/dist/components/input/TimePicker.mjs +189 -184
  85. package/dist/components/input/TimePicker.mjs.map +1 -1
  86. package/dist/components/input/fieldLabel.cjs +2 -0
  87. package/dist/components/input/fieldLabel.cjs.map +1 -0
  88. package/dist/components/input/fieldLabel.mjs +17 -0
  89. package/dist/components/input/fieldLabel.mjs.map +1 -0
  90. package/dist/components/input/inputLook.cjs +2 -0
  91. package/dist/components/input/inputLook.cjs.map +1 -0
  92. package/dist/components/input/inputLook.mjs +28 -0
  93. package/dist/components/input/inputLook.mjs.map +1 -0
  94. package/dist/components/input/pickerStyles.cjs +1 -1
  95. package/dist/components/input/pickerStyles.cjs.map +1 -1
  96. package/dist/components/input/pickerStyles.mjs +42 -4
  97. package/dist/components/input/pickerStyles.mjs.map +1 -1
  98. package/dist/components/navigation/Menu.popover.cjs +1 -1
  99. package/dist/components/navigation/Menu.popover.cjs.map +1 -1
  100. package/dist/components/navigation/Menu.popover.mjs +10 -10
  101. package/dist/components/navigation/Menu.root.cjs +1 -1
  102. package/dist/components/navigation/Menu.root.cjs.map +1 -1
  103. package/dist/components/navigation/Menu.root.mjs +10 -10
  104. package/dist/components/overlays/Popover.cjs +1 -1
  105. package/dist/components/overlays/Popover.cjs.map +1 -1
  106. package/dist/components/overlays/Popover.mjs +203 -208
  107. package/dist/components/overlays/Popover.mjs.map +1 -1
  108. package/dist/components/overlays/Popover.shared.cjs +1 -1
  109. package/dist/components/overlays/Popover.shared.cjs.map +1 -1
  110. package/dist/components/overlays/Popover.shared.mjs +82 -77
  111. package/dist/components/overlays/Popover.shared.mjs.map +1 -1
  112. package/dist/components/provider/WaveProvider.cjs +1 -1
  113. package/dist/components/provider/WaveProvider.cjs.map +1 -1
  114. package/dist/components/provider/WaveProvider.mjs +25 -19
  115. package/dist/components/provider/WaveProvider.mjs.map +1 -1
  116. package/dist/hooks/useFieldControl.cjs.map +1 -1
  117. package/dist/hooks/useFieldControl.mjs.map +1 -1
  118. package/dist/index.cjs +1 -1
  119. package/dist/index.d.cts +805 -99
  120. package/dist/index.d.ts +805 -99
  121. package/dist/index.mjs +101 -99
  122. package/dist/lib/classNames.cjs +1 -1
  123. package/dist/lib/classNames.cjs.map +1 -1
  124. package/dist/lib/classNames.mjs +74 -66
  125. package/dist/lib/classNames.mjs.map +1 -1
  126. package/dist/lib/styles.cjs +1 -1
  127. package/dist/lib/styles.cjs.map +1 -1
  128. package/dist/lib/styles.mjs +21 -4
  129. package/dist/lib/styles.mjs.map +1 -1
  130. package/dist/styles.css +1 -1
  131. package/package.json +1 -1
  132. package/src/styles/tokens.css +13 -0
@@ -1,80 +1,103 @@
1
1
  "use client";
2
2
  import { cn as e } from "../../lib/cn.mjs";
3
- import { composeEventHandlers as t } from "../../lib/composeEventHandlers.mjs";
4
- import { sliderClassNames as n } from "../../lib/classNames.mjs";
5
- import { useEventCallback as r } from "../../hooks/useEventCallback.mjs";
6
- import { useMergedRefs as i } from "../../hooks/useMergedRefs.mjs";
7
- import { useFieldControl as a } from "../../hooks/useFieldControl.mjs";
8
- import { focusRing as o, forcedColors as s } from "../../lib/styles.mjs";
9
- import { useFormReset as c } from "../../hooks/useFormReset.mjs";
10
- import * as l from "react";
11
- import { jsx as u } from "react/jsx-runtime";
3
+ import { warnOnce as t } from "../../lib/dev.mjs";
4
+ import { composeEventHandlers as n } from "../../lib/composeEventHandlers.mjs";
5
+ import { sliderClassNames as r } from "../../lib/classNames.mjs";
6
+ import { useEventCallback as i } from "../../hooks/useEventCallback.mjs";
7
+ import { useMergedRefs as a } from "../../hooks/useMergedRefs.mjs";
8
+ import { useFieldControl as o } from "../../hooks/useFieldControl.mjs";
9
+ import { focusRing as s, forcedColors as c } from "../../lib/styles.mjs";
10
+ import { useFormReset as l } from "../../hooks/useFormReset.mjs";
11
+ import * as u from "react";
12
+ import { jsx as d } from "react/jsx-runtime";
12
13
  //#region src/components/input/Slider.tsx
13
- var d = "--wave-slider-progress";
14
- function f(e, t, n) {
14
+ var f = "--wave-slider-progress";
15
+ function p(e, t, n) {
15
16
  let r = n > t ? (e - t) / (n - t) * 100 : 0;
16
17
  return `${Number.isFinite(r) ? Math.min(100, Math.max(0, r)) : 0}%`;
17
18
  }
18
- var p = [
19
- "[&::-webkit-slider-runnable-track]:h-1 [&::-webkit-slider-runnable-track]:rounded-full [&::-webkit-slider-runnable-track]:bg-stroke-accessible",
20
- "[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_right,var(--wave-primary)_var(--wave-slider-progress),var(--wave-stroke-accessible)_var(--wave-slider-progress))]",
21
- "wave-rtl:[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_left,var(--wave-primary)_var(--wave-slider-progress),var(--wave-stroke-accessible)_var(--wave-slider-progress))]",
22
- "[&::-moz-range-track]:h-1 [&::-moz-range-track]:rounded-full [&::-moz-range-track]:bg-stroke-accessible",
23
- "[&::-moz-range-progress]:h-1 [&::-moz-range-progress]:rounded-full [&::-moz-range-progress]:bg-primary"
24
- ], m = [
19
+ var m = {
20
+ medium: {
21
+ rail: [
22
+ "[&::-webkit-slider-runnable-track]:h-1 [&::-webkit-slider-runnable-track]:rounded-full [&::-webkit-slider-runnable-track]:bg-stroke-accessible",
23
+ "[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_right,var(--wave-primary)_var(--wave-slider-progress),var(--wave-stroke-accessible)_var(--wave-slider-progress))]",
24
+ "wave-rtl:[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_left,var(--wave-primary)_var(--wave-slider-progress),var(--wave-stroke-accessible)_var(--wave-slider-progress))]",
25
+ "[&::-moz-range-track]:h-1 [&::-moz-range-track]:rounded-full [&::-moz-range-track]:bg-stroke-accessible",
26
+ "[&::-moz-range-progress]:h-1 [&::-moz-range-progress]:rounded-full [&::-moz-range-progress]:bg-primary"
27
+ ],
28
+ thumbWebkit: "[&::-webkit-slider-thumb]:-mt-2 [&::-webkit-slider-thumb]:h-5 [&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-background [&::-webkit-slider-thumb]:bg-primary [&::-webkit-slider-thumb]:shadow",
29
+ thumbMoz: "[&::-moz-range-thumb]:h-5 [&::-moz-range-thumb]:w-5 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-background [&::-moz-range-thumb]:bg-primary [&::-moz-range-thumb]:shadow"
30
+ },
31
+ small: {
32
+ rail: [
33
+ "[&::-webkit-slider-runnable-track]:h-0.5 [&::-webkit-slider-runnable-track]:rounded-full [&::-webkit-slider-runnable-track]:bg-stroke-accessible",
34
+ "[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_right,var(--wave-primary)_var(--wave-slider-progress),var(--wave-stroke-accessible)_var(--wave-slider-progress))]",
35
+ "wave-rtl:[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_left,var(--wave-primary)_var(--wave-slider-progress),var(--wave-stroke-accessible)_var(--wave-slider-progress))]",
36
+ "[&::-moz-range-track]:h-0.5 [&::-moz-range-track]:rounded-full [&::-moz-range-track]:bg-stroke-accessible",
37
+ "[&::-moz-range-progress]:h-0.5 [&::-moz-range-progress]:rounded-full [&::-moz-range-progress]:bg-primary"
38
+ ],
39
+ thumbWebkit: "[&::-webkit-slider-thumb]:-mt-1.75 [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-background [&::-webkit-slider-thumb]:bg-primary [&::-webkit-slider-thumb]:shadow",
40
+ thumbMoz: "[&::-moz-range-thumb]:h-4 [&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-background [&::-moz-range-thumb]:bg-primary [&::-moz-range-thumb]:shadow"
41
+ }
42
+ }, h = [
25
43
  "forced-colors:[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_right,Highlight_var(--wave-slider-progress),CanvasText_var(--wave-slider-progress))]",
26
44
  "forced-colors:wave-rtl:[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_left,Highlight_var(--wave-slider-progress),CanvasText_var(--wave-slider-progress))]",
27
45
  "forced-colors:[&::-moz-range-progress]:bg-[Highlight]",
28
46
  "forced-colors:disabled:[&::-webkit-slider-runnable-track]:bg-none forced-colors:disabled:[&::-moz-range-progress]:bg-[GrayText]"
29
- ], h = ({ className: h, style: g, label: _, value: v, defaultValue: y, min: b, max: x, step: S, form: C, onChange: w, onValueChange: T, ref: E, id: D, "aria-label": O, "aria-labelledby": k, "aria-describedby": A, "aria-invalid": j, "aria-required": M, required: N, ...P }) => {
30
- let F = a({
31
- id: D,
32
- "aria-label": O ?? _,
33
- "aria-labelledby": k,
34
- "aria-describedby": A,
35
- "aria-invalid": j,
36
- "aria-required": M,
37
- required: N
38
- }, { nativeRequired: !0 }), I = l.useRef(null), L = i(I, E), R = b ?? 0, z = x ?? 100, B = f(v ?? y ?? R + (z - R) / 2, R, z), V = r(() => {
39
- let e = I.current;
47
+ ], g = ({ className: g, style: _, label: v, value: y, defaultValue: b, min: x, max: S, step: C, size: w, form: T, onChange: E, onValueChange: D, ref: O, id: k, "aria-label": A, "aria-labelledby": j, "aria-describedby": M, "aria-invalid": N, "aria-required": P, required: F, ...I }) => {
48
+ let L = typeof w == "number";
49
+ u.useEffect(() => {
50
+ L && t("Slider:size-number", "Slider: a numeric `size` has no effect (a range input ignores the native attribute) and is removed in 1.0; remove it.");
51
+ }, [L]);
52
+ let R = w === "small" ? "small" : "medium", z = m[R], B = o({
53
+ id: k,
54
+ "aria-label": A ?? v,
55
+ "aria-labelledby": j,
56
+ "aria-describedby": M,
57
+ "aria-invalid": N,
58
+ "aria-required": P,
59
+ required: F
60
+ }, { nativeRequired: !0 }), V = u.useRef(null), H = a(V, O), U = x ?? 0, W = S ?? 100, G = p(y ?? b ?? U + (W - U) / 2, U, W), K = i(() => {
61
+ let e = V.current;
40
62
  if (!e) return;
41
- let t = f(Number(e.value), R, z);
42
- e.style.getPropertyValue(d) !== t && e.style.setProperty(d, t);
63
+ let t = p(Number(e.value), U, W);
64
+ e.style.getPropertyValue(f) !== t && e.style.setProperty(f, t);
43
65
  });
44
- l.useLayoutEffect(() => {
45
- V();
66
+ u.useLayoutEffect(() => {
67
+ K();
46
68
  }, [
47
- v,
48
69
  y,
49
70
  b,
50
71
  x,
51
72
  S,
52
- V
53
- ]), c(I, () => queueMicrotask(V), C);
54
- let H = t(w, (e) => {
55
- v === void 0 && V(), T?.(Number(e.currentTarget.value));
73
+ C,
74
+ K
75
+ ]), l(V, () => queueMicrotask(K), T);
76
+ let q = n(E, (e) => {
77
+ y === void 0 && K(), D?.(Number(e.currentTarget.value));
56
78
  }, { checkDefaultPrevented: !1 });
57
- return /* @__PURE__ */ u("input", {
58
- ref: L,
79
+ return /* @__PURE__ */ d("input", {
80
+ ref: H,
59
81
  type: "range",
60
- className: e(n.root, "w-full cursor-pointer appearance-none bg-transparent", p, "[&::-webkit-slider-thumb]:-mt-2 [&::-webkit-slider-thumb]:h-5 [&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-background [&::-webkit-slider-thumb]:bg-primary [&::-webkit-slider-thumb]:shadow", "[&::-moz-range-thumb]:h-5 [&::-moz-range-thumb]:w-5 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-background [&::-moz-range-thumb]:bg-primary [&::-moz-range-thumb]:shadow", "disabled:cursor-not-allowed disabled:opacity-50", o, s.rangeInput, m, h),
82
+ className: e(r.root, "w-full cursor-pointer appearance-none bg-transparent", R === "small" && "h-6", z.rail, z.thumbWebkit, z.thumbMoz, "disabled:cursor-not-allowed disabled:opacity-50", s, c.rangeInput, h, g),
61
83
  style: {
62
- ...g,
63
- [d]: B
84
+ ..._,
85
+ [f]: G
64
86
  },
65
- value: v,
66
- defaultValue: y,
67
- min: b,
68
- max: x,
69
- step: S,
70
- form: C,
71
- ...P,
72
- ...F,
73
- onChange: H
87
+ value: y,
88
+ defaultValue: b,
89
+ min: x,
90
+ max: S,
91
+ step: C,
92
+ form: T,
93
+ "data-size": R,
94
+ ...I,
95
+ ...B,
96
+ onChange: q
74
97
  });
75
98
  };
76
- h.displayName = "Slider";
99
+ g.displayName = "Slider";
77
100
  //#endregion
78
- export { h as Slider };
101
+ export { g as Slider };
79
102
 
80
103
  //# sourceMappingURL=Slider.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Slider.mjs","names":["React","PROGRESS_VAR","progressPercent","share","max","min","value","railClasses","forcedColorsFill","Slider","ariaLabel","ariaLabelledBy","ariaDescribedBy","ariaInvalid","ariaRequired","props","fieldProps","useFieldControl","label","inputRef","mergedRef","useMergedRefs","ref","rangeMin","rangeMax","renderedProgress","defaultValue","updateFill","useEventCallback","input","next","step","useFormReset","form","handleChange","composeEventHandlers","onChange","onValueChange","event","cn","sliderClassNames","focusRing","forcedColors","className","style"],"sources":["../../../src/components/input/Slider.tsx"],"sourcesContent":["import * as React from 'react';\nimport { sliderClassNames } from '../../lib/classNames';\nimport { cn } from '../../lib/cn';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { focusRing, forcedColors } from '../../lib/styles';\nimport { useEventCallback } from '../../hooks/useEventCallback';\nimport { useFieldControl } from '../../hooks/useFieldControl';\nimport { useFormReset } from '../../hooks/useFormReset';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\n\n/** Properties for the Slider component. */\nexport interface SliderProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'> {\n /** Controlled slider value. */\n value?: number;\n /** Initial value for uncontrolled usage. */\n defaultValue?: number;\n /** Native change event handler (`event.target.value` is a string). */\n onChange?: React.ChangeEventHandler<HTMLInputElement>;\n /** Called with the new value as a number on every change, next to the native `onChange`. */\n onValueChange?: (value: number) => void;\n /** Minimum allowed value. */\n min?: number;\n /** Maximum allowed value. */\n max?: number;\n /** Step increment between values. */\n step?: number;\n /**\n * Accessible label for the slider (sets `aria-label`; an explicit `aria-label` wins). Inside a\n * `Field`, leave it out so the Field's label names the slider.\n */\n label?: string;\n /** Ref to the `<input type=\"range\">` element. */\n ref?: React.Ref<HTMLInputElement>;\n}\n\n/** The CSS variable that holds how much of the rail the fill covers (`<p>%`). */\nconst PROGRESS_VAR = '--wave-slider-progress';\n\n/** The share of the rail up to `value`, as a CSS percentage: 0–100, and 0 when `max <= min`. */\nfunction progressPercent(value: number, min: number, max: number): string {\n const share = max > min ? ((value - min) / (max - min)) * 100 : 0;\n return `${Number.isFinite(share) ? Math.min(100, Math.max(0, share)) : 0}%`;\n}\n\n/**\n * The rail: the primary fill up to the thumb, the accessible stroke after it. WebKit and Blink\n * draw it as a gradient on the track (mirrored for right-to-left); Firefox has a progress part and\n * mirrors it itself. The track color stays underneath (the whole rail without the variable).\n */\nconst railClasses = [\n // The rail color gives the rail itself 3:1 non-text contrast.\n '[&::-webkit-slider-runnable-track]:h-1 [&::-webkit-slider-runnable-track]:rounded-full [&::-webkit-slider-runnable-track]:bg-stroke-accessible',\n '[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_right,var(--wave-primary)_var(--wave-slider-progress),var(--wave-stroke-accessible)_var(--wave-slider-progress))]',\n 'wave-rtl:[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_left,var(--wave-primary)_var(--wave-slider-progress),var(--wave-stroke-accessible)_var(--wave-slider-progress))]',\n '[&::-moz-range-track]:h-1 [&::-moz-range-track]:rounded-full [&::-moz-range-track]:bg-stroke-accessible',\n '[&::-moz-range-progress]:h-1 [&::-moz-range-progress]:rounded-full [&::-moz-range-progress]:bg-primary',\n];\n\n/**\n * Forced colors, after `forcedColors.rangeInput` (which draws the whole rail in CanvasText): the\n * filled part in Highlight; a disabled rail stays GrayText, without the fill.\n */\nconst forcedColorsFill = [\n 'forced-colors:[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_right,Highlight_var(--wave-slider-progress),CanvasText_var(--wave-slider-progress))]',\n 'forced-colors:wave-rtl:[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_left,Highlight_var(--wave-slider-progress),CanvasText_var(--wave-slider-progress))]',\n 'forced-colors:[&::-moz-range-progress]:bg-[Highlight]',\n 'forced-colors:disabled:[&::-webkit-slider-runnable-track]:bg-none forced-colors:disabled:[&::-moz-range-progress]:bg-[GrayText]',\n];\n\n/**\n * A native range input with Fluent styling: a rail drawn in the accessible stroke color (3:1\n * against the page), filled in the primary color up to a primary thumb; in forced-colors mode the\n * rail, fill and thumb are drawn in system colors. Inside a `Field` it picks up the label, hint,\n * validation message, `required` and invalid state automatically.\n *\n * The fill follows the value through the `--wave-slider-progress` variable on the input (also in\n * the server HTML), including the browser's snapping to `step` and a form reset.\n *\n * @example\n * <Slider label=\"Volume\" min={0} max={100} value={volume} onValueChange={setVolume} />\n */\nexport const Slider = ({\n className,\n style,\n label,\n value,\n defaultValue,\n min,\n max,\n step,\n form,\n onChange,\n onValueChange,\n ref,\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-invalid': ariaInvalid,\n 'aria-required': ariaRequired,\n required,\n ...props\n}: SliderProps) => {\n const fieldProps = useFieldControl(\n {\n id,\n 'aria-label': ariaLabel ?? label,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-invalid': ariaInvalid,\n 'aria-required': ariaRequired,\n required,\n },\n { nativeRequired: true },\n );\n\n const inputRef = React.useRef<HTMLInputElement>(null);\n const mergedRef = useMergedRefs(inputRef, ref);\n const rangeMin = min ?? 0;\n const rangeMax = max ?? 100;\n // During render (and on the server): the value, else the default, else the native midpoint.\n const renderedProgress = progressPercent(\n value ?? defaultValue ?? rangeMin + (rangeMax - rangeMin) / 2,\n rangeMin,\n rangeMax,\n );\n\n /** Writes the fill of the value the input really has (the browser snaps it to `step`). */\n const updateFill = useEventCallback(() => {\n const input = inputRef.current;\n if (!input) return;\n const next = progressPercent(Number(input.value), rangeMin, rangeMax);\n if (input.style.getPropertyValue(PROGRESS_VAR) !== next) {\n input.style.setProperty(PROGRESS_VAR, next);\n }\n });\n\n // On mount and whenever the value or its range changes: React wrote the variable from the props,\n // the input may hold another value (step snapping, an uncontrolled value the user moved).\n React.useLayoutEffect(() => {\n updateFill();\n }, [value, defaultValue, min, max, step, updateFill]);\n\n // The reset event fires before the form restores the value: read it a microtask later.\n useFormReset(inputRef, () => queueMicrotask(updateFill), form);\n\n const handleChange = composeEventHandlers(\n onChange,\n (event: React.ChangeEvent<HTMLInputElement>) => {\n // A controlled fill follows `value` (a parent that rejects the change keeps it).\n if (value === undefined) updateFill();\n onValueChange?.(Number(event.currentTarget.value));\n },\n { checkDefaultPrevented: false },\n );\n\n return (\n <input\n ref={mergedRef}\n type=\"range\"\n className={cn(\n sliderClassNames.root,\n 'w-full cursor-pointer appearance-none bg-transparent',\n railClasses,\n // Thumb. WebKit and Blink align its top with the rail's top, so it is pulled up by half the\n // difference of the two heights (5 and 1 spacing units): it stays centred at any root\n // font size.\n '[&::-webkit-slider-thumb]:-mt-2 [&::-webkit-slider-thumb]:h-5 [&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-background [&::-webkit-slider-thumb]:bg-primary [&::-webkit-slider-thumb]:shadow',\n // Firefox thumb\n '[&::-moz-range-thumb]:h-5 [&::-moz-range-thumb]:w-5 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-background [&::-moz-range-thumb]:bg-primary [&::-moz-range-thumb]:shadow',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n focusRing,\n // Forced colors replace the author backgrounds of the rail and thumb with Canvas: give\n // every part a system color instead.\n forcedColors.rangeInput,\n forcedColorsFill,\n className,\n )}\n style={{ ...style, [PROGRESS_VAR]: renderedProgress } as React.CSSProperties}\n value={value}\n defaultValue={defaultValue}\n min={min}\n max={max}\n step={step}\n form={form}\n {...props}\n {...fieldProps}\n onChange={handleChange}\n />\n );\n};\n\nSlider.displayName = 'Slider';\n"],"mappings":";;;;;;;;;;;;AAoCA,IAAMC,IAAe;AAGrB,SAASC,EAAgB,GAAe,GAAa,GAAqB;CACxE,IAAMC,IAAQC,IAAMC,KAAQC,IAAQD,MAAQD,IAAMC,KAAQ,MAAM;CAChE,OAAO,GAAG,OAAO,SAASF,CAAK,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,GAAGA,CAAK,CAAC,IAAI,EAAE;AAC3E;AAOA,IAAMI,IAAc;CAElB;CACA;CACA;CACA;CACA;AACF,GAMMC,IAAmB;CACvB;CACA;CACA;CACA;AACF,GAcaC,KAAU,EACrB,cACA,UACA,UACA,UACA,iBACA,QACA,QACA,SACA,SACA,aACA,kBACA,QACA,OACA,cAAcC,GACd,mBAAmBC,GACnB,oBAAoBC,GACpB,gBAAgBC,GAChB,iBAAiBC,GACjB,aACA,GAAGC,QACc;CACjB,IAAMC,IAAaC,EACjB;EACE;EACA,cAAcP,KAAaQ;EAC3B,mBAAmBP;EACnB,oBAAoBC;EACpB,gBAAgBC;EAChB,iBAAiBC;EACjB;CACF,GACA,EAAE,gBAAgB,GAAK,CACzB,GAEMK,IAAWnB,EAAM,OAAyB,IAAI,GAC9CoB,IAAYC,EAAcF,GAAUG,CAAG,GACvCC,IAAWlB,KAAO,GAClBmB,IAAWpB,KAAO,KAElBqB,IAAmBvB,EACvBI,KAASoB,KAAgBH,KAAYC,IAAWD,KAAY,GAC5DA,GACAC,CACF,GAGMG,IAAaC,QAAuB;EACxC,IAAMC,IAAQV,EAAS;EACvB,IAAI,CAACU,GAAO;EACZ,IAAMC,IAAO5B,EAAgB,OAAO2B,EAAM,KAAK,GAAGN,GAAUC,CAAQ;EACpE,AAAIK,EAAM,MAAM,iBAAiB5B,CAAY,MAAM6B,KACjDD,EAAM,MAAM,YAAY5B,GAAc6B,CAAI;CAE9C,CAAC;CASD,AALA9B,EAAM,sBAAsB;EAC1B,EAAW;CACb,GAAG;EAACM;EAAOoB;EAAcrB;EAAKD;EAAK2B;EAAMJ;CAAU,CAAC,GAGpDK,EAAab,SAAgB,eAAeQ,CAAU,GAAGM,CAAI;CAE7D,IAAMC,IAAeC,EACnBC,IACC,MAA+C;EAG9C,AADI9B,MAAU,KAAA,KAAWqB,EAAW,GACpCU,IAAgB,OAAOC,EAAM,cAAc,KAAK,CAAC;CACnD,GACA,EAAE,uBAAuB,GAAM,CACjC;CAEA,OACE,kBAAC,SAAD;EACE,KAAKlB;EACL,MAAK;EACL,WAAWmB,EACTC,EAAiB,MACjB,wDACAjC,GAIA,qUAEA,+NACA,mDACAkC,GAGAC,EAAa,YACblC,GACAmC,CACF;EACA,OAAO;GAAE,GAAGC;IAAQ3C,IAAewB;EAAiB;EAC7C;EACO;EACT;EACA;EACC;EACA;EACN,GAAIV;EACJ,GAAIC;EACJ,UAAUkB;CACX,CAAA;AAEL;AAEA,EAAO,cAAc"}
1
+ {"version":3,"file":"Slider.mjs","names":["React","PROGRESS_VAR","progressPercent","share","max","min","value","SLIDER_SIZE","forcedColorsFill","Slider","sizeProp","ariaLabel","ariaLabelledBy","ariaDescribedBy","ariaInvalid","ariaRequired","props","numericSize","warnOnce","size","sizeClasses","fieldProps","useFieldControl","label","inputRef","mergedRef","useMergedRefs","ref","rangeMin","rangeMax","renderedProgress","defaultValue","updateFill","useEventCallback","input","next","step","useFormReset","form","handleChange","composeEventHandlers","onChange","onValueChange","event","cn","sliderClassNames","focusRing","forcedColors","className","style"],"sources":["../../../src/components/input/Slider.tsx"],"sourcesContent":["import * as React from 'react';\nimport { sliderClassNames } from '../../lib/classNames';\nimport { cn } from '../../lib/cn';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { warnOnce } from '../../lib/dev';\nimport { focusRing, forcedColors } from '../../lib/styles';\nimport type { Size } from '../../lib/types';\nimport { useEventCallback } from '../../hooks/useEventCallback';\nimport { useFieldControl } from '../../hooks/useFieldControl';\nimport { useFormReset } from '../../hooks/useFormReset';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\n\n/** Size of the thumb and rail: a small 16px thumb on a 2px rail, or the 0.7 20px thumb on a 4px rail. */\nexport type SliderSize = Extract<Size, 'small' | 'medium'>;\n\n/** Properties for the Slider component. */\nexport interface SliderProps extends Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n 'type' | 'size'\n> {\n /** Controlled slider value. */\n value?: number;\n /** Initial value for uncontrolled usage. */\n defaultValue?: number;\n /** Native change event handler (`event.target.value` is a string). */\n onChange?: React.ChangeEventHandler<HTMLInputElement>;\n /** Called with the new value as a number on every change, next to the native `onChange`. */\n onValueChange?: (value: number) => void;\n /** Minimum allowed value. */\n min?: number;\n /** Maximum allowed value. */\n max?: number;\n /** Step increment between values. */\n step?: number;\n /**\n * Accessible label for the slider (sets `aria-label`; an explicit `aria-label` wins). Inside a\n * `Field`, leave it out so the Field's label names the slider.\n */\n label?: string;\n /**\n * Size of the thumb and rail: the 0.7 20px thumb on a 4px rail, or a small 16px thumb on a 2px\n * rail in a 24px-tall input. Unlike the text controls and pickers, Slider does not read a\n * surrounding `Field`'s size or a `WaveProvider`'s input defaults: this prop is the only source\n * of its size.\n *\n * `SliderProps` inherited the native HTML `size` attribute, which a range input ignores anyway.\n * A number is deprecated: it renders nothing (not even the native attribute), is reported once\n * in development, and is a type error from 1.0.\n * @default 'medium'\n */\n size?: SliderSize | number;\n /** Ref to the `<input type=\"range\">` element. */\n ref?: React.Ref<HTMLInputElement>;\n}\n\n/** The CSS variable that holds how much of the rail the fill covers (`<p>%`). */\nconst PROGRESS_VAR = '--wave-slider-progress';\n\n/** The share of the rail up to `value`, as a CSS percentage: 0–100, and 0 when `max <= min`. */\nfunction progressPercent(value: number, min: number, max: number): string {\n const share = max > min ? ((value - min) / (max - min)) * 100 : 0;\n return `${Number.isFinite(share) ? Math.min(100, Math.max(0, share)) : 0}%`;\n}\n\n/**\n * Per-size rail and thumb classes (D14). The rail is the primary fill up to the thumb, the\n * accessible stroke after it: WebKit and Blink draw it as a gradient on the track (mirrored for\n * right-to-left), Firefox has a progress part and mirrors it itself; the track color stays\n * underneath (the whole rail without the variable). WebKit and Blink align the thumb's top with\n * the rail's top, so the thumb is pulled up by half the difference of the two heights: it stays\n * centred at any root font size. Medium keeps the 0.7 strings exactly; small substitutes a\n * thinner rail and a smaller thumb (each `[&::…]` variant written out literally, since the CSS\n * build's class scanner needs whole class names, not assembled ones).\n */\nconst SLIDER_SIZE: Readonly<\n Record<SliderSize, { rail: readonly string[]; thumbWebkit: string; thumbMoz: string }>\n> = {\n medium: {\n rail: [\n // The rail color gives the rail itself 3:1 non-text contrast.\n '[&::-webkit-slider-runnable-track]:h-1 [&::-webkit-slider-runnable-track]:rounded-full [&::-webkit-slider-runnable-track]:bg-stroke-accessible',\n '[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_right,var(--wave-primary)_var(--wave-slider-progress),var(--wave-stroke-accessible)_var(--wave-slider-progress))]',\n 'wave-rtl:[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_left,var(--wave-primary)_var(--wave-slider-progress),var(--wave-stroke-accessible)_var(--wave-slider-progress))]',\n '[&::-moz-range-track]:h-1 [&::-moz-range-track]:rounded-full [&::-moz-range-track]:bg-stroke-accessible',\n '[&::-moz-range-progress]:h-1 [&::-moz-range-progress]:rounded-full [&::-moz-range-progress]:bg-primary',\n ],\n thumbWebkit:\n '[&::-webkit-slider-thumb]:-mt-2 [&::-webkit-slider-thumb]:h-5 [&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-background [&::-webkit-slider-thumb]:bg-primary [&::-webkit-slider-thumb]:shadow',\n thumbMoz:\n '[&::-moz-range-thumb]:h-5 [&::-moz-range-thumb]:w-5 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-background [&::-moz-range-thumb]:bg-primary [&::-moz-range-thumb]:shadow',\n },\n small: {\n rail: [\n '[&::-webkit-slider-runnable-track]:h-0.5 [&::-webkit-slider-runnable-track]:rounded-full [&::-webkit-slider-runnable-track]:bg-stroke-accessible',\n '[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_right,var(--wave-primary)_var(--wave-slider-progress),var(--wave-stroke-accessible)_var(--wave-slider-progress))]',\n 'wave-rtl:[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_left,var(--wave-primary)_var(--wave-slider-progress),var(--wave-stroke-accessible)_var(--wave-slider-progress))]',\n '[&::-moz-range-track]:h-0.5 [&::-moz-range-track]:rounded-full [&::-moz-range-track]:bg-stroke-accessible',\n '[&::-moz-range-progress]:h-0.5 [&::-moz-range-progress]:rounded-full [&::-moz-range-progress]:bg-primary',\n ],\n thumbWebkit:\n '[&::-webkit-slider-thumb]:-mt-1.75 [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-background [&::-webkit-slider-thumb]:bg-primary [&::-webkit-slider-thumb]:shadow',\n thumbMoz:\n '[&::-moz-range-thumb]:h-4 [&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-background [&::-moz-range-thumb]:bg-primary [&::-moz-range-thumb]:shadow',\n },\n};\n\n/**\n * Forced colors, after `forcedColors.rangeInput` (which draws the whole rail in CanvasText): the\n * filled part in Highlight; a disabled rail stays GrayText, without the fill.\n */\nconst forcedColorsFill = [\n 'forced-colors:[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_right,Highlight_var(--wave-slider-progress),CanvasText_var(--wave-slider-progress))]',\n 'forced-colors:wave-rtl:[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_left,Highlight_var(--wave-slider-progress),CanvasText_var(--wave-slider-progress))]',\n 'forced-colors:[&::-moz-range-progress]:bg-[Highlight]',\n 'forced-colors:disabled:[&::-webkit-slider-runnable-track]:bg-none forced-colors:disabled:[&::-moz-range-progress]:bg-[GrayText]',\n];\n\n/**\n * A native range input with Fluent styling: a rail drawn in the accessible stroke color (3:1\n * against the page), filled in the primary color up to a primary thumb; in forced-colors mode the\n * rail, fill and thumb are drawn in system colors. Inside a `Field` it picks up the label, hint,\n * validation message, `required` and invalid state automatically.\n *\n * The fill follows the value through the `--wave-slider-progress` variable on the input (also in\n * the server HTML), including the browser's snapping to `step` and a form reset.\n *\n * `size` (`small`, `medium`) sets the thumb and rail dimensions; the input carries `data-size`.\n * Slider does not read a surrounding `Field`'s size or a `WaveProvider`'s input defaults.\n *\n * @example\n * <Slider label=\"Volume\" min={0} max={100} value={volume} onValueChange={setVolume} />\n */\nexport const Slider = ({\n className,\n style,\n label,\n value,\n defaultValue,\n min,\n max,\n step,\n size: sizeProp,\n form,\n onChange,\n onValueChange,\n ref,\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-invalid': ariaInvalid,\n 'aria-required': ariaRequired,\n required,\n ...props\n}: SliderProps) => {\n // D10: the deprecated numeric form of `size` never reaches the input (it was part of `...props`\n // in 0.7, which rendered it as the meaningless native `size` attribute); report it once.\n const numericSize = typeof sizeProp === 'number';\n React.useEffect(() => {\n if (numericSize) {\n warnOnce(\n 'Slider:size-number',\n 'Slider: a numeric `size` has no effect (a range input ignores the native attribute) and is removed in 1.0; remove it.',\n );\n }\n }, [numericSize]);\n // Any other value (an unknown string from untyped code) also resolves to medium, so the classes\n // and `data-size` always agree.\n const size: SliderSize = sizeProp === 'small' ? 'small' : 'medium';\n const sizeClasses = SLIDER_SIZE[size];\n\n const fieldProps = useFieldControl(\n {\n id,\n 'aria-label': ariaLabel ?? label,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-invalid': ariaInvalid,\n 'aria-required': ariaRequired,\n required,\n },\n { nativeRequired: true },\n );\n\n const inputRef = React.useRef<HTMLInputElement>(null);\n const mergedRef = useMergedRefs(inputRef, ref);\n const rangeMin = min ?? 0;\n const rangeMax = max ?? 100;\n // During render (and on the server): the value, else the default, else the native midpoint.\n const renderedProgress = progressPercent(\n value ?? defaultValue ?? rangeMin + (rangeMax - rangeMin) / 2,\n rangeMin,\n rangeMax,\n );\n\n /** Writes the fill of the value the input really has (the browser snaps it to `step`). */\n const updateFill = useEventCallback(() => {\n const input = inputRef.current;\n if (!input) return;\n const next = progressPercent(Number(input.value), rangeMin, rangeMax);\n if (input.style.getPropertyValue(PROGRESS_VAR) !== next) {\n input.style.setProperty(PROGRESS_VAR, next);\n }\n });\n\n // On mount and whenever the value or its range changes: React wrote the variable from the props,\n // the input may hold another value (step snapping, an uncontrolled value the user moved).\n React.useLayoutEffect(() => {\n updateFill();\n }, [value, defaultValue, min, max, step, updateFill]);\n\n // The reset event fires before the form restores the value: read it a microtask later.\n useFormReset(inputRef, () => queueMicrotask(updateFill), form);\n\n const handleChange = composeEventHandlers(\n onChange,\n (event: React.ChangeEvent<HTMLInputElement>) => {\n // A controlled fill follows `value` (a parent that rejects the change keeps it).\n if (value === undefined) updateFill();\n onValueChange?.(Number(event.currentTarget.value));\n },\n { checkDefaultPrevented: false },\n );\n\n return (\n <input\n ref={mergedRef}\n type=\"range\"\n className={cn(\n sliderClassNames.root,\n 'w-full cursor-pointer appearance-none bg-transparent',\n // A 24px-tall input at the small size, the rail centred in it (D14).\n size === 'small' && 'h-6',\n sizeClasses.rail,\n sizeClasses.thumbWebkit,\n sizeClasses.thumbMoz,\n 'disabled:cursor-not-allowed disabled:opacity-50',\n focusRing,\n // Forced colors replace the author backgrounds of the rail and thumb with Canvas: give\n // every part a system color instead.\n forcedColors.rangeInput,\n forcedColorsFill,\n className,\n )}\n style={{ ...style, [PROGRESS_VAR]: renderedProgress } as React.CSSProperties}\n value={value}\n defaultValue={defaultValue}\n min={min}\n max={max}\n step={step}\n form={form}\n data-size={size}\n {...props}\n {...fieldProps}\n onChange={handleChange}\n />\n );\n};\n\nSlider.displayName = 'Slider';\n"],"mappings":";;;;;;;;;;;;;AAwDA,IAAMC,IAAe;AAGrB,SAASC,EAAgB,GAAe,GAAa,GAAqB;CACxE,IAAMC,IAAQC,IAAMC,KAAQC,IAAQD,MAAQD,IAAMC,KAAQ,MAAM;CAChE,OAAO,GAAG,OAAO,SAASF,CAAK,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,GAAGA,CAAK,CAAC,IAAI,EAAE;AAC3E;AAYA,IAAMI,IAEF;CACF,QAAQ;EACN,MAAM;GAEJ;GACA;GACA;GACA;GACA;EACF;EACA,aACE;EACF,UACE;CACJ;CACA,OAAO;EACL,MAAM;GACJ;GACA;GACA;GACA;GACA;EACF;EACA,aACE;EACF,UACE;CACJ;AACF,GAMMC,IAAmB;CACvB;CACA;CACA;CACA;AACF,GAiBaC,KAAU,EACrB,cACA,UACA,UACA,UACA,iBACA,QACA,QACA,SACA,MAAMC,GACN,SACA,aACA,kBACA,QACA,OACA,cAAcC,GACd,mBAAmBC,GACnB,oBAAoBC,GACpB,gBAAgBC,GAChB,iBAAiBC,GACjB,aACA,GAAGC,QACc;CAGjB,IAAMC,IAAc,OAAOP,KAAa;CACxC,EAAM,gBAAgB;EACpB,AAAIO,KACFC,EACE,sBACA,uHACF;CAEJ,GAAG,CAACD,CAAW,CAAC;CAGhB,IAAME,IAAmBT,MAAa,UAAU,UAAU,UACpDU,IAAcb,EAAYY,IAE1BE,IAAaC,EACjB;EACE;EACA,cAAcX,KAAaY;EAC3B,mBAAmBX;EACnB,oBAAoBC;EACpB,gBAAgBC;EAChB,iBAAiBC;EACjB;CACF,GACA,EAAE,gBAAgB,GAAK,CACzB,GAEMS,IAAWxB,EAAM,OAAyB,IAAI,GAC9CyB,IAAYC,EAAcF,GAAUG,CAAG,GACvCC,IAAWvB,KAAO,GAClBwB,IAAWzB,KAAO,KAElB0B,IAAmB5B,EACvBI,KAASyB,KAAgBH,KAAYC,IAAWD,KAAY,GAC5DA,GACAC,CACF,GAGMG,IAAaC,QAAuB;EACxC,IAAMC,IAAQV,EAAS;EACvB,IAAI,CAACU,GAAO;EACZ,IAAMC,IAAOjC,EAAgB,OAAOgC,EAAM,KAAK,GAAGN,GAAUC,CAAQ;EACpE,AAAIK,EAAM,MAAM,iBAAiBjC,CAAY,MAAMkC,KACjDD,EAAM,MAAM,YAAYjC,GAAckC,CAAI;CAE9C,CAAC;CASD,AALAnC,EAAM,sBAAsB;EAC1B,EAAW;CACb,GAAG;EAACM;EAAOyB;EAAc1B;EAAKD;EAAKgC;EAAMJ;CAAU,CAAC,GAGpDK,EAAab,SAAgB,eAAeQ,CAAU,GAAGM,CAAI;CAE7D,IAAMC,IAAeC,EACnBC,IACC,MAA+C;EAG9C,AADInC,MAAU,KAAA,KAAW0B,EAAW,GACpCU,IAAgB,OAAOC,EAAM,cAAc,KAAK,CAAC;CACnD,GACA,EAAE,uBAAuB,GAAM,CACjC;CAEA,OACE,kBAAC,SAAD;EACE,KAAKlB;EACL,MAAK;EACL,WAAWmB,EACTC,EAAiB,MACjB,wDAEA1B,MAAS,WAAW,OACpBC,EAAY,MACZA,EAAY,aACZA,EAAY,UACZ,mDACA0B,GAGAC,EAAa,YACbvC,GACAwC,CACF;EACA,OAAO;GAAE,GAAGC;IAAQhD,IAAe6B;EAAiB;EAC7C;EACO;EACT;EACA;EACC;EACA;EACN,aAAWX;EACX,GAAIH;EACJ,GAAIK;EACJ,UAAUkB;CACX,CAAA;AAEL;AAEA,EAAO,cAAc"}
@@ -1,2 +1,2 @@
1
- "use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/dev.cjs"),r=require("../../lib/composeEventHandlers.cjs"),i=require("../../lib/classNames.cjs"),ee=require("../../hooks/useControllable.cjs"),te=require("../../hooks/useMergedRefs.cjs"),ne=require("../../hooks/useFieldControl.cjs"),re=require("../../lib/styles.cjs"),a=require("../../lib/icons.cjs"),ie=require("./Input.cjs"),ae=require("../../hooks/useFormReset.cjs"),oe=require("../internal/HiddenInput.cjs");let o=require("react");o=e.__toESM(o,1);let s=require("react/jsx-runtime");var c=/^[+-]?(\d+\.?\d*|\.\d+)(e[+-]?\d+)?$/i,se=/^[+-]?\.?$/;function l(e){let t=e.trim();if(!c.test(t))return null;let n=Number(t);return Number.isFinite(n)?n:null}function u(e){if(!Number.isFinite(e))return 0;let[t,n]=String(e).toLowerCase().split(`e`),r=t.split(`.`)[1]?.length??0;return Math.max(0,r-(n?Number(n):0))}function d(e,t){return Number(e.toFixed(Math.min(t,20)))}var f=t.cn(`flex h-8 w-8 shrink-0 items-center justify-center border-input bg-transparent p-0 text-foreground`,`not-disabled:not-aria-disabled:hover:bg-subtle-hover not-disabled:not-aria-disabled:active:bg-subtle-pressed`,`disabled:pointer-events-none`),p=({value:e,defaultValue:c,onValueChange:p,onChange:m,min:h=-1/0,max:g=1/0,step:_=1,largeStep:v,disabled:y,name:ce,form:b,labels:x,className:le,hidden:ue,ref:de,controlRef:fe,id:pe,"aria-label":me,"aria-labelledby":he,"aria-describedby":S,"aria-invalid":C,"aria-required":w,"aria-errormessage":T,"aria-details":E,"aria-valuetext":D,required:O,readOnly:k,placeholder:A,maxLength:j,spellCheck:M,autoComplete:N=`off`,autoFocus:P,inputMode:F,enterKeyHint:I,tabIndex:L,onFocus:R,onBlur:ge,onKeyDown:_e,onKeyUp:ve,...ye})=>{m!==void 0&&n.warnDeprecated(`SpinButton`,`onChange`,`onValueChange`);let z=c??0,[B,V]=ee.useControllable(e,z,e=>{p?.(e),m?.(e)}),[H,U]=o.useState(null),[be,xe]=o.useState(B);Object.is(be,B)||(xe(B),U(null));let W=H===null?null:l(H),G=W??B,Se=H!==null&&W===null&&H.trim()!==``&&!se.test(H.trim()),Ce=e=>Math.min(g,Math.max(h,e)),K=!y&&!k,q=e=>{U(null),V(Ce(e))},J=e=>{let t=Math.max(u(_),u(e),u(G));q(d(G+e,t))},Y=()=>{H!==null&&(W===null?U(null):q(W))},X=o.useRef(null),we=te.useMergedRefs(X,fe),Z=ne.useFieldControl({id:pe,"aria-label":me,"aria-labelledby":he,"aria-describedby":S,"aria-invalid":C,"aria-required":w,required:O},{nativeRequired:!0}),Q=Se?!0:Z[`aria-invalid`],Te=ie.isInvalidLook(!1,Q);ae.useFormReset(X,()=>{U(null),V(z)},b),o.useEffect(()=>{let e=X.current;e&&(e.hasAttribute(`aria-label`)||e.hasAttribute(`aria-labelledby`)||(e.labels?.length??0)>0||e.hasAttribute(`title`)||n.warnOnce(`SpinButton:unnamed`,"SpinButton: the spinbutton has no accessible name. Pass `aria-label` or `aria-labelledby`, use a <label htmlFor>, or render it inside a Field."))});let Ee=e=>{if(!K||e.altKey||e.ctrlKey||e.metaKey)return;let t=v??d(_*10,u(_));switch(e.key){case`ArrowUp`:case`ArrowDown`:case`PageUp`:case`PageDown`:{e.preventDefault();let n=e.key===`ArrowUp`||e.key===`ArrowDown`?_:t;J(e.key===`ArrowUp`||e.key===`PageUp`?n:-n);return}case`Home`:case`End`:{let t=e.key===`Home`?h:g;if(!Number.isFinite(t))return;e.preventDefault(),q(t);return}case`Enter`:Y();return;case`Escape`:H!==null&&(e.preventDefault(),U(null));return}},De=h<0?`text`:u(_)>0||u(h)>0?`decimal`:`numeric`,$=e=>e.preventDefault();return(0,s.jsxs)(`div`,{ref:de,className:t.cn(i.spinButtonClassNames.root,`relative inline-flex items-center rounded border border-input border-b-stroke-accessible bg-background`,re.inputFocusWithin,Te&&`border-destructive focus-within:border-b-destructive`,y&&`cursor-not-allowed opacity-50`,le),hidden:ue,...ye,children:[(0,s.jsx)(`button`,{type:`button`,tabIndex:-1,onMouseDown:$,onClick:()=>J(-_),disabled:!K||G<=h,"aria-label":x?.decrement??`Decrement`,className:t.cn(i.spinButtonClassNames.decrementButton,f,`border-e`,!y&&`disabled:opacity-50`),children:(0,s.jsx)(a.SubtractIcon,{size:12})}),(0,s.jsx)(`input`,{ref:we,type:`text`,role:`spinbutton`,inputMode:F??De,autoComplete:N,autoFocus:P,enterKeyHint:I,placeholder:A,maxLength:j,spellCheck:M,tabIndex:L,readOnly:k,form:b,...Z,"aria-invalid":Q,"aria-errormessage":T,"aria-details":E,"aria-valuenow":B,"aria-valuemin":Number.isFinite(h)?h:void 0,"aria-valuemax":Number.isFinite(g)?g:void 0,"aria-valuetext":D,value:H??String(B),onChange:e=>U(e.target.value),onFocus:R,onBlur:r.composeEventHandlers(ge,Y,{checkDefaultPrevented:!1}),onKeyDown:r.composeEventHandlers(_e,Ee),onKeyUp:ve,disabled:y,className:t.cn(i.spinButtonClassNames.input,`h-8 w-12 min-w-0 border-none bg-transparent text-center text-body-1 text-foreground focus:outline-hidden disabled:cursor-not-allowed [appearance:textfield]`)}),(0,s.jsx)(`button`,{type:`button`,tabIndex:-1,onMouseDown:$,onClick:()=>J(_),disabled:!K||G>=g,"aria-label":x?.increment??`Increment`,className:t.cn(i.spinButtonClassNames.incrementButton,f,`border-s`,!y&&`disabled:opacity-50`),children:(0,s.jsx)(a.AddIcon,{size:12})}),(0,s.jsx)(oe.HiddenInput,{name:ce,form:b,disabled:y,value:String(B)})]})};p.displayName=`SpinButton`,exports.SpinButton=p;
1
+ "use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/dev.cjs"),r=require("../../lib/composeEventHandlers.cjs"),i=require("../../lib/classNames.cjs"),ee=require("../../hooks/useEventCallback.cjs"),te=require("../../hooks/useControllable.cjs"),ne=require("../../hooks/useMergedRefs.cjs"),a=require("../../hooks/useFieldControl.cjs"),o=require("../../lib/styles.cjs"),re=require("../../lib/icons.cjs"),ie=require("./inputLook.cjs"),ae=require("./Input.cjs"),oe=require("../../hooks/useFormReset.cjs"),se=require("../internal/HiddenInput.cjs");let s=require("react");s=e.__toESM(s,1);let c=require("react/jsx-runtime");var l=/^[+-]?(\d+\.?\d*|\.\d+)(e[+-]?\d+)?$/i,ce=/^[+-]?\.?$/;function le(e){let t=e.trim();if(!l.test(t))return null;let n=Number(t);return Number.isFinite(n)?n:null}function u(e){if(!Number.isFinite(e))return 0;let[t,n]=String(e).toLowerCase().split(`e`),r=t.split(`.`)[1]?.length??0;return Math.max(0,r-(n?Number(n):0))}function d(e,t){return Number(e.toFixed(Math.min(t,20)))}var f=300,p=1e3;function m(e){let t=Math.min(1,e/p);return Math.round(f+-220*t)}function ue(e){let t=f,n=setTimeout(function r(){if(!e())return;let i=m(t);t+=i,n=setTimeout(r,i)},f);return()=>clearTimeout(n)}var h={small:{button:`w-6`,input:`w-10`,glyph:12},medium:{button:`w-8`,input:`w-12`,glyph:12},large:{button:`w-10`,input:`w-14`,glyph:16}},g=t.cn(`flex h-full shrink-0 items-center justify-center bg-transparent p-0 text-foreground`,`not-disabled:not-aria-disabled:hover:bg-subtle-hover not-disabled:not-aria-disabled:active:bg-subtle-pressed`,`disabled:pointer-events-none`,`touch-manipulation select-none [-webkit-touch-callout:none]`),_=e=>{let{allowEmpty:l=!1,value:f,defaultValue:p,onValueChange:m,onChange:_,...de}=e,{min:v=-1/0,max:y=1/0,step:b=1,largeStep:fe,precision:x,disabled:S,name:pe,form:C,labels:me,displayValue:w,size:he,appearance:ge,className:_e,hidden:ve,ref:ye,controlRef:be,id:xe,"aria-label":Se,"aria-labelledby":Ce,"aria-describedby":we,"aria-invalid":Te,"aria-required":Ee,"aria-errormessage":De,"aria-details":Oe,"aria-valuetext":ke,required:T,readOnly:E,placeholder:Ae,maxLength:je,spellCheck:Me,autoComplete:Ne=`off`,autoFocus:Pe,inputMode:Fe,enterKeyHint:Ie,tabIndex:Le,onFocus:Re,onBlur:ze,onKeyDown:Be,onKeyUp:Ve,...He}=de;_!==void 0&&n.warnDeprecated(`SpinButton`,`onChange`,`onValueChange`);let D=p===void 0?l?null:0:p,[O,k,A]=te.useControllable(f,D,e=>{m?.(e),e!==null&&_?.(e)}),[j,M]=s.useState(null),[Ue,We]=s.useState(O);Object.is(Ue,O)||(We(O),M(null));let[N,P]=s.useState(!1),F=s.useRef(!1),I=j===null?null:le(j),L=I??O,Ge=j!==null&&I===null&&j.trim()!==``&&!ce.test(j.trim()),R=e=>Math.min(y,Math.max(v,e)),z=!S&&!E,B=A&&w!==void 0,V=x===void 0||!Number.isFinite(x)?void 0:Math.min(20,Math.max(0,Math.trunc(x))),H=e=>{M(null);let t=V===void 0?e:d(e,V);k(R(t))},U=(e,t)=>{let n=e??0,r=V??Math.max(u(b),u(t),u(n));return R(d(n+t,r))},W=e=>{M(null),k(U(L,e))},G=s.useRef(null),K=s.useRef(!1),q=s.useCallback(()=>{G.current?.stop(),G.current=null},[]),J=e=>{e.pointerId===G.current?.pointerId&&q()},Ke=e=>L!==null&&(e===1?L>=y:L<=v),qe=ee.useEventCallback(()=>{let e=G.current;if(!e)return!1;if(!z||Ke(e.direction))return q(),!1;K.current=!0;let t=e.direction*b;return e.stepped?(M(null),k(e=>U(e,t))):(e.stepped=!0,W(t)),!0}),Je=(e,t)=>{if(!e.isPrimary||(K.current=!1,e.button!==0||e.pointerType===`mouse`&&e.ctrlKey||!z))return;let n=e.target instanceof Element?e.target:e.currentTarget;e.pointerType!==`mouse`&&n.hasPointerCapture?.(e.pointerId)&&n.releasePointerCapture(e.pointerId),q(),G.current={pointerId:e.pointerId,direction:t,stepped:!1,stop:ue(qe)}},Y=(e,t)=>{if(e.detail!==0&&K.current){K.current=!1;return}W(t*b)};s.useEffect(()=>{let e=G.current;e&&(!z||Ke(e.direction))&&q()}),s.useEffect(()=>(window.addEventListener(`blur`,q),()=>{window.removeEventListener(`blur`,q),q()}),[q]);let Ye=()=>{if(j!==null){if(l&&j.trim()===``){M(null),k(null);return}I===null?M(null):H(I)}},X=s.useRef(null),Xe=ne.useMergedRefs(X,be),Ze=a.useFieldContext(),Qe=T??Ze?.required??!1,$e=a.useFieldControl({id:xe,"aria-label":Se,"aria-labelledby":Ce,"aria-describedby":we,"aria-invalid":Te,"aria-required":Ee,required:T},{nativeRequired:!0}),et=Ge?!0:$e[`aria-invalid`],tt=ae.isInvalidLook(!1,et),{size:Z,appearance:Q}=ie.useInputLook(he,ge);oe.useFormReset(X,()=>{M(null),k(D)},C),s.useEffect(()=>{let e=X.current;e&&(e.hasAttribute(`aria-label`)||e.hasAttribute(`aria-labelledby`)||(e.labels?.length??0)>0||e.hasAttribute(`title`)||n.warnOnce(`SpinButton:unnamed`,"SpinButton: the spinbutton has no accessible name. Pass `aria-label` or `aria-labelledby`, use a <label htmlFor>, or render it inside a Field."))}),s.useEffect(()=>{w!==void 0&&!A&&n.warnOnce(`SpinButton:displayValue-uncontrolled`,"SpinButton: `displayValue` is ignored while the value is uncontrolled; pass `value` (and update it in `onValueChange`).")},[w,A]);let nt=ee.useEventCallback(e=>{let t=e.target;F.current=t.value.length>0&&t.selectionStart===0&&t.selectionEnd===t.value.length,P(!0)}),rt=()=>{Ye(),P(!1)};s.useLayoutEffect(()=>{N&&F.current&&(F.current=!1,X.current?.select())},[N]);let it=e=>{if(!z||e.altKey||e.ctrlKey||e.metaKey)return;let t=fe??d(b*10,u(b));switch(e.key){case`ArrowUp`:case`ArrowDown`:case`PageUp`:case`PageDown`:{e.preventDefault();let n=e.key===`ArrowUp`||e.key===`ArrowDown`?b:t;W(e.key===`ArrowUp`||e.key===`PageUp`?n:-n);return}case`Home`:case`End`:{if(e.shiftKey)return;let t=e.key===`Home`?v:y;if(!Number.isFinite(t))return;e.preventDefault(),H(t);return}case`Enter`:Ye();return;case`Escape`:j!==null&&(e.preventDefault(),M(null));return}},at=v<0?`text`:u(b)>0||u(v)>0?`decimal`:`numeric`,$=e=>e.preventDefault();return(0,c.jsxs)(`div`,{ref:ye,"data-size":Z,"data-appearance":Q,className:t.cn(i.spinButtonClassNames.root,`relative inline-flex items-center`,o.inputHeightClasses[Z],o.inputTextClasses[Z],o.inputAppearanceClasses[Q],o.inputFocusWithin,tt&&`border-destructive`,S&&`cursor-not-allowed opacity-50`,_e),hidden:ve,...He,children:[(0,c.jsx)(`button`,{type:`button`,tabIndex:-1,onPointerDown:e=>Je(e,-1),onPointerUp:J,onPointerCancel:J,onPointerLeave:J,onClick:e=>Y(e,-1),onMouseDown:$,onContextMenu:e=>e.preventDefault(),disabled:!z||L!==null&&L<=v,"aria-label":me?.decrement??`Decrement`,className:t.cn(i.spinButtonClassNames.decrementButton,g,h[Z].button,Q===`outline`&&`border-e border-input`,Q===`filled-darker`&&`not-disabled:not-aria-disabled:hover:bg-subtle-pressed`,!S&&`disabled:opacity-50`),children:(0,c.jsx)(re.SubtractIcon,{size:h[Z].glyph})}),(0,c.jsx)(`input`,{ref:Xe,type:`text`,role:`spinbutton`,inputMode:Fe??at,autoComplete:Ne,autoFocus:Pe,enterKeyHint:Ie,placeholder:Ae,maxLength:je,spellCheck:Me,tabIndex:Le,readOnly:E,form:C,...$e,"aria-invalid":et,"aria-errormessage":De,"aria-details":Oe,"aria-valuenow":O??void 0,"aria-valuemin":Number.isFinite(v)?v:void 0,"aria-valuemax":Number.isFinite(y)?y:void 0,"aria-valuetext":ke??(B?w:void 0),value:j??(B&&!(N&&z)?w:O===null?``:String(O)),onChange:e=>M(e.target.value),onFocus:r.composeEventHandlers(Re,nt,{checkDefaultPrevented:!1}),onBlur:r.composeEventHandlers(ze,rt,{checkDefaultPrevented:!1}),onKeyDown:r.composeEventHandlers(Be,it),onKeyUp:Ve,disabled:S,className:t.cn(i.spinButtonClassNames.input,`h-full min-w-0 flex-auto border-none bg-transparent text-center text-foreground focus:outline-hidden disabled:cursor-not-allowed [appearance:textfield]`,h[Z].input,o.inputTextClasses[Z])}),(0,c.jsx)(`button`,{type:`button`,tabIndex:-1,onPointerDown:e=>Je(e,1),onPointerUp:J,onPointerCancel:J,onPointerLeave:J,onClick:e=>Y(e,1),onMouseDown:$,onContextMenu:e=>e.preventDefault(),disabled:!z||L!==null&&L>=y,"aria-label":me?.increment??`Increment`,className:t.cn(i.spinButtonClassNames.incrementButton,g,h[Z].button,Q===`outline`&&`border-s border-input`,Q===`filled-darker`&&`not-disabled:not-aria-disabled:hover:bg-subtle-pressed`,!S&&`disabled:opacity-50`),children:(0,c.jsx)(re.AddIcon,{size:h[Z].glyph})}),(0,c.jsx)(se.HiddenInput,{name:pe,form:C,disabled:S,type:`text`,value:O===null?``:String(O),required:l?Qe:void 0,onInvalid:()=>X.current?.focus()})]})};_.displayName=`SpinButton`,exports.SpinButton=_;
2
2
  //# sourceMappingURL=SpinButton.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"SpinButton.cjs","names":["cn","useControllable","React","useMergedRefs","useFieldControl","isInvalidLook","spinButtonClassNames","inputFocusWithin","SubtractIcon","composeEventHandlers","AddIcon","HiddenInput","require_runtime","require_cn","require_dev","require_composeEventHandlers","require_classNames","require_useControllable","require_useMergedRefs","require_useFieldControl","require_styles","require_icons","require_Input","require_useFormReset","require_HiddenInput","react","react_jsx_runtime","NUMBER_TEXT","PARTIAL_NUMBER_TEXT","parseNumberText","trimmed","text","decimalsOf","n","mantissa","exponent","fraction","roundTo","decimals","stepButtonClass","SpinButton","valueProp","ariaLabel","ariaLabelledBy","ariaDescribedBy","ariaInvalid","ariaRequired","ariaErrorMessage","ariaDetails","ariaValueText","rest","onChange","initialValue","defaultValue","value","setValue","onValueChange","next","draft","setDraft","prevValue","setPrevValue","draftNumber","current","draftInvalid","clamp","max","min","interactive","disabled","readOnly","commit","stepBy","precision","step","delta","commitDraft","inputRef","mergedInputRef","controlRef","fieldProps","ariaInvalidValue","invalidLook","form","input","handleKeyDown","big","largeStep","size","bound","defaultInputMode","keepFocus","className","labels","inputMode","onBlur","onKeyDown"],"sources":["../../../src/components/input/SpinButton.tsx"],"sourcesContent":["import * as React from 'react';\nimport { spinButtonClassNames } from '../../lib/classNames';\nimport { cn } from '../../lib/cn';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { warnDeprecated, warnOnce } from '../../lib/dev';\nimport { AddIcon, SubtractIcon } from '../../lib/icons';\nimport { inputFocusWithin, inputInvalidWithin } from '../../lib/styles';\nimport { useControllable } from '../../hooks/useControllable';\nimport { useFieldControl } from '../../hooks/useFieldControl';\nimport { useFormReset } from '../../hooks/useFormReset';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { HiddenInput } from '../internal/HiddenInput';\nimport { isInvalidLook } from './Input';\n\n/**\n * Props that SpinButton routes to its `<input role=\"spinbutton\">` (C-ROUTING): the id, the ARIA\n * naming/validation attributes, native text-input attributes and the focus/keyboard handlers.\n */\nexport type SpinButtonInputProps = Pick<\n React.InputHTMLAttributes<HTMLInputElement>,\n | 'id'\n | 'aria-label'\n | 'aria-labelledby'\n | 'aria-describedby'\n | 'aria-invalid'\n | 'aria-required'\n | 'aria-errormessage'\n | 'aria-details'\n | 'aria-valuetext'\n | 'required'\n | 'readOnly'\n | 'placeholder'\n | 'maxLength'\n | 'spellCheck'\n | 'autoComplete'\n | 'autoFocus'\n | 'inputMode'\n | 'enterKeyHint'\n | 'tabIndex'\n | 'onFocus'\n | 'onBlur'\n | 'onKeyDown'\n | 'onKeyUp'\n>;\n\n/**\n * Names of the SpinButton's step buttons, for localization. Each member is optional and falls back\n * to its English default.\n */\nexport interface SpinButtonLabels {\n /** Name of the button that steps up.\n * @default 'Increment'\n */\n increment?: string;\n /** Name of the button that steps down.\n * @default 'Decrement'\n */\n decrement?: string;\n}\n\n/** Properties for the SpinButton component. */\nexport interface SpinButtonProps\n extends\n Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'onChange' | 'defaultValue' | keyof SpinButtonInputProps\n >,\n SpinButtonInputProps {\n /** Controlled numeric value. */\n value?: number;\n /** Initial value for uncontrolled usage (also what a form reset restores).\n * @default 0\n */\n defaultValue?: number;\n /**\n * Called with the new value when it changes: a step (buttons, arrow keys, PageUp/PageDown,\n * Home/End) or a typed value committed on blur or Enter. Not called while the user is typing,\n * nor when the value stays the same.\n */\n onValueChange?: (value: number) => void;\n /**\n * Called with the new value when it changes.\n * @deprecated Use `onValueChange`. (`onChange` is reserved for native change events.)\n */\n onChange?: (value: number) => void;\n /** Minimum allowed value.\n * @default -Infinity\n */\n min?: number;\n /** Maximum allowed value.\n * @default Infinity\n */\n max?: number;\n /** Step of the increment/decrement buttons and of ArrowUp/ArrowDown.\n * @default 1\n */\n step?: number;\n /** Step of PageUp/PageDown.\n * @default step * 10\n */\n largeStep?: number;\n /** Whether the spin button is disabled and non-interactive. */\n disabled?: boolean;\n /** Form field name. With a name, the committed value is submitted with the form. */\n name?: string;\n /** Id of the form the spin button belongs to, when it is rendered outside that form. */\n form?: string;\n /**\n * Names of the −/+ step buttons (not tab stops, but in the accessibility tree), for\n * localization. Unset members keep their English defaults.\n */\n labels?: SpinButtonLabels;\n /** Ref to the root `<div>`. */\n ref?: React.Ref<HTMLDivElement>;\n /** Ref to the `<input role=\"spinbutton\">` (the focusable control). */\n controlRef?: React.Ref<HTMLInputElement>;\n}\n\n/** Decimal numbers as a user types them: `12`, `-3`, `1.5`, `.5`, `2.`, `1e3`. */\nconst NUMBER_TEXT = /^[+-]?(\\d+\\.?\\d*|\\.\\d+)(e[+-]?\\d+)?$/i;\n/** Text on the way to a number (`-`, `.`, `-.`): not a value yet, but not an error either. */\nconst PARTIAL_NUMBER_TEXT = /^[+-]?\\.?$/;\n\nfunction parseNumberText(text: string): number | null {\n const trimmed = text.trim();\n if (!NUMBER_TEXT.test(trimmed)) return null;\n const n = Number(trimmed);\n return Number.isFinite(n) ? n : null;\n}\n\n/** Number of decimals of `n` as written by JavaScript (`0.1` → 1, `1e-7` → 7). */\nfunction decimalsOf(n: number): number {\n if (!Number.isFinite(n)) return 0;\n const [mantissa, exponent] = String(n).toLowerCase().split('e');\n const fraction = mantissa.split('.')[1]?.length ?? 0;\n return Math.max(0, fraction - (exponent ? Number(exponent) : 0));\n}\n\nfunction roundTo(n: number, decimals: number): number {\n return Number(n.toFixed(Math.min(decimals, 20)));\n}\n\nconst stepButtonClass = cn(\n // Padding and background set here (C-NATIVE): an app-wide `button` rule cannot fill them.\n 'flex h-8 w-8 shrink-0 items-center justify-center border-input bg-transparent p-0 text-foreground',\n 'not-disabled:not-aria-disabled:hover:bg-subtle-hover not-disabled:not-aria-disabled:active:bg-subtle-pressed',\n 'disabled:pointer-events-none',\n);\n\n/**\n * A numeric input with decrement/increment buttons (APG spinbutton pattern).\n *\n * - **Keyboard** on the input: ArrowUp/ArrowDown step by `step`, PageUp/PageDown by `largeStep`\n * (default ten steps), Home/End jump to `min`/`max` when they are finite. The −/+ buttons are\n * not tab stops and never take focus, so focus stays on the spinbutton and screen readers\n * announce the new value.\n * - **Typing** edits a draft: the value is parsed, clamped and committed on blur or Enter\n * (Escape reverts). Text that is not a number is flagged with `aria-invalid` and reverted on\n * blur. Steps (keys and buttons) start from the typed number, and the −/+ buttons are disabled\n * when that number is at `min`/`max`. Steps are rounded to the precision of `step` and of the\n * value, so `0.1 + 0.2` is `0.3`.\n * - **Routing**: `id`, ARIA, native input attributes and focus/keyboard handlers go to the\n * `<input role=\"spinbutton\">`; `className`, `style`, `data-*`, other handlers and `ref` stay on\n * the root (`controlRef` reaches the input). Inside a `Field` it picks up the label, hint, error\n * and required state automatically. It has no built-in name: pass `aria-label`/`aria-labelledby`\n * or use a label (development warning otherwise).\n * - **Forms**: with `name` the committed value is submitted; `required` makes the input natively\n * required; a form reset restores `defaultValue`.\n * - The −/+ buttons are named \"Decrement\"/\"Increment\"; `labels` localizes the names.\n *\n * @example\n * <SpinButton aria-label=\"Quantity\" min={1} max={10} value={qty} onValueChange={setQty} />\n */\nexport const SpinButton = ({\n value: valueProp,\n defaultValue,\n onValueChange,\n onChange,\n min = -Infinity,\n max = Infinity,\n step = 1,\n largeStep,\n disabled,\n name,\n form,\n labels,\n className,\n hidden,\n ref,\n controlRef,\n // Routed to the input (C-ROUTING)\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-invalid': ariaInvalid,\n 'aria-required': ariaRequired,\n 'aria-errormessage': ariaErrorMessage,\n 'aria-details': ariaDetails,\n 'aria-valuetext': ariaValueText,\n required,\n readOnly,\n placeholder,\n maxLength,\n spellCheck,\n autoComplete = 'off',\n autoFocus,\n inputMode,\n enterKeyHint,\n tabIndex,\n onFocus,\n onBlur,\n onKeyDown,\n onKeyUp,\n ...rest\n}: SpinButtonProps) => {\n if (onChange !== undefined) warnDeprecated('SpinButton', 'onChange', 'onValueChange');\n const initialValue = defaultValue ?? 0;\n const [value, setValue] = useControllable(valueProp, initialValue, (next: number) => {\n onValueChange?.(next);\n onChange?.(next);\n });\n\n // The text is a draft while the user types (null = show the value).\n const [draft, setDraft] = React.useState<string | null>(null);\n // A value that changes from outside replaces the draft (adjust state during render, C-HOOKS).\n const [prevValue, setPrevValue] = React.useState(value);\n if (!Object.is(prevValue, value)) {\n setPrevValue(value);\n setDraft(null);\n }\n\n const draftNumber = draft === null ? null : parseNumberText(draft);\n // What stepping starts from: the typed draft when it is a number, else the value. The −/+\n // buttons are enabled by it too, so they agree with ArrowUp/ArrowDown while the user types.\n const current = draftNumber ?? value;\n const draftInvalid =\n draft !== null &&\n draftNumber === null &&\n draft.trim() !== '' &&\n !PARTIAL_NUMBER_TEXT.test(draft.trim());\n\n const clamp = (n: number) => Math.min(max, Math.max(min, n));\n const interactive = !disabled && !readOnly;\n\n const commit = (next: number) => {\n setDraft(null);\n setValue(clamp(next));\n };\n\n /** Steps from the typed draft when it is a number, else from the value. */\n const stepBy = (delta: number) => {\n const precision = Math.max(decimalsOf(step), decimalsOf(delta), decimalsOf(current));\n commit(roundTo(current + delta, precision));\n };\n\n const commitDraft = () => {\n if (draft === null) return;\n if (draftNumber === null) setDraft(null);\n else commit(draftNumber);\n };\n\n const inputRef = React.useRef<HTMLInputElement>(null);\n const mergedInputRef = useMergedRefs(inputRef, controlRef);\n\n const fieldProps = useFieldControl(\n {\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-invalid': ariaInvalid,\n 'aria-required': ariaRequired,\n required,\n },\n { nativeRequired: true },\n );\n // Typed text that is not a number is invalid whatever the consumer says; otherwise the\n // consumer's or the Field's `aria-invalid` is passed through as is (`grammar` included), and the\n // error look follows the shared rule of the text controls.\n const ariaInvalidValue = draftInvalid ? true : fieldProps['aria-invalid'];\n const invalidLook = isInvalidLook(false, ariaInvalidValue);\n\n useFormReset(\n inputRef,\n () => {\n setDraft(null);\n setValue(initialValue);\n },\n form,\n );\n\n // No built-in name any more (\"Value\" named every instance the same): report unnamed inputs.\n React.useEffect(() => {\n const input = inputRef.current;\n if (!input) return;\n const named =\n input.hasAttribute('aria-label') ||\n input.hasAttribute('aria-labelledby') ||\n (input.labels?.length ?? 0) > 0 ||\n input.hasAttribute('title');\n if (!named) {\n warnOnce(\n 'SpinButton:unnamed',\n 'SpinButton: the spinbutton has no accessible name. Pass `aria-label` or `aria-labelledby`, use a <label htmlFor>, or render it inside a Field.',\n );\n }\n });\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n // Read-only: the keys keep their native caret behaviour and never change the value.\n if (!interactive || e.altKey || e.ctrlKey || e.metaKey) return;\n // Ten steps, rounded: step * 10 carries float error (0.07 * 10 = 0.7000000000000001) that\n // stepBy would otherwise keep, since it rounds to the precision of the delta too.\n const big = largeStep ?? roundTo(step * 10, decimalsOf(step));\n switch (e.key) {\n case 'ArrowUp':\n case 'ArrowDown':\n case 'PageUp':\n case 'PageDown': {\n e.preventDefault();\n const size = e.key === 'ArrowUp' || e.key === 'ArrowDown' ? step : big;\n stepBy(e.key === 'ArrowUp' || e.key === 'PageUp' ? size : -size);\n return;\n }\n case 'Home':\n case 'End': {\n const bound = e.key === 'Home' ? min : max;\n if (!Number.isFinite(bound)) return; // no bound: Home/End move the caret\n e.preventDefault();\n commit(bound);\n return;\n }\n case 'Enter':\n // Not prevented: Enter still submits the surrounding form, with the committed value.\n commitDraft();\n return;\n case 'Escape':\n if (draft !== null) {\n e.preventDefault(); // enclosing layers (a Dialog) ignore this Escape\n setDraft(null);\n }\n return;\n default:\n }\n };\n\n const defaultInputMode: React.HTMLAttributes<HTMLInputElement>['inputMode'] =\n min < 0 ? 'text' : decimalsOf(step) > 0 || decimalsOf(min) > 0 ? 'decimal' : 'numeric';\n\n // The step buttons never take focus: a pointer press leaves it where it is (on the input while\n // editing), so a button that becomes disabled at a bound cannot drop focus to <body>.\n const keepFocus = (e: React.MouseEvent) => e.preventDefault();\n\n return (\n <div\n ref={ref}\n className={cn(\n spinButtonClassNames.root,\n 'relative inline-flex items-center rounded border border-input border-b-stroke-accessible bg-background',\n inputFocusWithin,\n invalidLook && inputInvalidWithin,\n disabled && 'cursor-not-allowed opacity-50',\n className,\n )}\n hidden={hidden}\n {...rest}\n >\n <button\n type=\"button\"\n tabIndex={-1}\n onMouseDown={keepFocus}\n onClick={() => stepBy(-step)}\n disabled={!interactive || current <= min}\n aria-label={labels?.decrement ?? 'Decrement'}\n className={cn(\n spinButtonClassNames.decrementButton,\n stepButtonClass,\n 'border-e',\n !disabled && 'disabled:opacity-50',\n )}\n >\n <SubtractIcon size={12} />\n </button>\n\n <input\n ref={mergedInputRef}\n type=\"text\"\n role=\"spinbutton\"\n inputMode={inputMode ?? defaultInputMode}\n autoComplete={autoComplete}\n autoFocus={autoFocus}\n enterKeyHint={enterKeyHint}\n placeholder={placeholder}\n maxLength={maxLength}\n spellCheck={spellCheck}\n tabIndex={tabIndex}\n readOnly={readOnly}\n form={form}\n {...fieldProps}\n aria-invalid={ariaInvalidValue}\n aria-errormessage={ariaErrorMessage}\n aria-details={ariaDetails}\n aria-valuenow={value}\n aria-valuemin={Number.isFinite(min) ? min : undefined}\n aria-valuemax={Number.isFinite(max) ? max : undefined}\n aria-valuetext={ariaValueText}\n value={draft ?? String(value)}\n onChange={(e) => setDraft(e.target.value)}\n onFocus={onFocus}\n onBlur={composeEventHandlers(onBlur, commitDraft, { checkDefaultPrevented: false })}\n onKeyDown={composeEventHandlers(onKeyDown, handleKeyDown)}\n onKeyUp={onKeyUp}\n disabled={disabled}\n className={cn(\n spinButtonClassNames.input,\n 'h-8 w-12 min-w-0 border-none bg-transparent text-center text-body-1 text-foreground focus:outline-hidden disabled:cursor-not-allowed [appearance:textfield]',\n )}\n />\n\n <button\n type=\"button\"\n tabIndex={-1}\n onMouseDown={keepFocus}\n onClick={() => stepBy(step)}\n disabled={!interactive || current >= max}\n aria-label={labels?.increment ?? 'Increment'}\n className={cn(\n spinButtonClassNames.incrementButton,\n stepButtonClass,\n 'border-s',\n !disabled && 'disabled:opacity-50',\n )}\n >\n <AddIcon size={12} />\n </button>\n\n <HiddenInput name={name} form={form} disabled={disabled} value={String(value)} />\n </div>\n );\n};\n\nSpinButton.displayName = 'SpinButton';\n"],"mappings":"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"}
1
+ {"version":3,"file":"SpinButton.cjs","names":["cn","useControllable","React","useEventCallback","useMergedRefs","useFieldContext","useFieldControl","isInvalidLook","useInputLook","spinButtonClassNames","inputHeightClasses","inputTextClasses","inputAppearanceClasses","inputFocusWithin","SubtractIcon","composeEventHandlers","AddIcon","HiddenInput","require_runtime","require_cn","require_dev","require_composeEventHandlers","require_classNames","require_useEventCallback","require_useControllable","require_useMergedRefs","require_useFieldControl","require_styles","require_icons","require_inputLook","require_Input","require_useFormReset","require_HiddenInput","react","react_jsx_runtime","NUMBER_TEXT","PARTIAL_NUMBER_TEXT","parseNumberText","trimmed","text","decimalsOf","n","mantissa","exponent","fraction","roundTo","decimals","SPIN_DELAY","SPIN_RAMP","spinDelay","elapsed","startSpin","timer","tick","step","delay","SPIN_SIZE","stepButtonClass","SpinButton","valueProp","baseProps","props","sizeProp","appearanceProp","ariaLabel","ariaLabelledBy","ariaDescribedBy","ariaInvalid","ariaRequired","ariaErrorMessage","ariaDetails","ariaValueText","rest","onChange","initialValue","defaultValue","allowEmpty","value","setValue","isControlled","onValueChange","next","draft","setDraft","prevValue","setPrevValue","focused","setFocused","reselectRef","draftNumber","current","draftInvalid","clamp","max","min","interactive","disabled","readOnly","showsDisplay","displayValue","places","precision","commit","rounded","stepFrom","base","from","delta","stepBy","holdRef","repeatedRef","stopHold","endHold","event","atBound","direction","holdStep","hold","prev","startHold","holder","clickStep","commitDraft","inputRef","mergedInputRef","controlRef","field","isRequired","required","fieldProps","ariaInvalidValue","invalidLook","form","input","handleFocus","handleBlur","handleKeyDown","big","largeStep","amount","bound","defaultInputMode","keepFocus","size","appearance","className","labels","inputMode","onFocus","onBlur","onKeyDown"],"sources":["../../../src/components/input/SpinButton.tsx"],"sourcesContent":["import * as React from 'react';\nimport { spinButtonClassNames } from '../../lib/classNames';\nimport { cn } from '../../lib/cn';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { warnDeprecated, warnOnce } from '../../lib/dev';\nimport { AddIcon, SubtractIcon } from '../../lib/icons';\nimport {\n inputAppearanceClasses,\n inputFocusWithin,\n inputHeightClasses,\n inputInvalidWithin,\n inputTextClasses,\n} from '../../lib/styles';\nimport type { CoreSize, InputAppearance } from '../../lib/types';\nimport { useControllable } from '../../hooks/useControllable';\nimport { useEventCallback } from '../../hooks/useEventCallback';\nimport { useFieldContext, useFieldControl } from '../../hooks/useFieldControl';\nimport { useFormReset } from '../../hooks/useFormReset';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { HiddenInput } from '../internal/HiddenInput';\nimport { isInvalidLook } from './Input';\nimport { useInputLook } from './inputLook';\n\n/**\n * Props that SpinButton routes to its `<input role=\"spinbutton\">` (C-ROUTING): the id, the ARIA\n * naming/validation attributes, native text-input attributes and the focus/keyboard handlers.\n */\nexport type SpinButtonInputProps = Pick<\n React.InputHTMLAttributes<HTMLInputElement>,\n | 'id'\n | 'aria-label'\n | 'aria-labelledby'\n | 'aria-describedby'\n | 'aria-invalid'\n | 'aria-required'\n | 'aria-errormessage'\n | 'aria-details'\n | 'aria-valuetext'\n | 'required'\n | 'readOnly'\n | 'placeholder'\n | 'maxLength'\n | 'spellCheck'\n | 'autoComplete'\n | 'autoFocus'\n | 'inputMode'\n | 'enterKeyHint'\n | 'tabIndex'\n | 'onFocus'\n | 'onBlur'\n | 'onKeyDown'\n | 'onKeyUp'\n>;\n\n/**\n * Names of the SpinButton's step buttons, for localization. Each member is optional and falls back\n * to its English default.\n */\nexport interface SpinButtonLabels {\n /** Name of the button that steps up.\n * @default 'Increment'\n */\n increment?: string;\n /** Name of the button that steps down.\n * @default 'Decrement'\n */\n decrement?: string;\n}\n\n/**\n * Properties every SpinButton mode shares (every member of {@link SpinButtonProps} and\n * {@link SpinButtonAllowEmptyProps} except the value members, which differ between them).\n */\nexport interface SpinButtonBaseProps\n extends\n Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'onChange' | 'defaultValue' | keyof SpinButtonInputProps\n >,\n SpinButtonInputProps {\n /** Minimum allowed value.\n * @default -Infinity\n */\n min?: number;\n /** Maximum allowed value.\n * @default Infinity\n */\n max?: number;\n /** Step of the increment/decrement buttons and of ArrowUp/ArrowDown.\n * @default 1\n */\n step?: number;\n /** Step of PageUp/PageDown.\n * @default step * 10\n */\n largeStep?: number;\n /**\n * Decimals every committed value is rounded to, typed or stepped (0–20); the number is not\n * padded (use `displayValue` for `1.00`). Default: steps round to the decimals of `step` and\n * the value, typed values are kept as typed.\n */\n precision?: number;\n /** Whether the spin button is disabled and non-interactive. */\n disabled?: boolean;\n /** Form field name. With a name, the committed value is submitted with the form. */\n name?: string;\n /** Id of the form the spin button belongs to, when it is rendered outside that form. */\n form?: string;\n /**\n * Names of the −/+ step buttons (not tab stops, but in the accessibility tree), for\n * localization. Unset members keep their English defaults.\n */\n labels?: SpinButtonLabels;\n /**\n * Text shown for the value while the field is not being edited, and its `aria-valuetext` (a\n * `$1.00` for 1). While the field has focus and can be edited it shows the plain number, so\n * typing edits the number. Applies only while `value` is controlled (update it with the\n * value); Fluent's `displayValue`.\n */\n displayValue?: string;\n /**\n * Size of the field: `small` (24px tall), `medium` (32px) or `large` (40px). Default: the\n * surrounding Field's `size`, else `WaveProvider inputDefaults.size`, else `'medium'`.\n *\n * SpinButton is 24, 32 or 40px tall like every field; its buttons are square-ish, 24, 32 or\n * 40px wide.\n */\n size?: CoreSize;\n /**\n * Look of the field: `outline` (a full border), `underline` (a bottom stroke only),\n * `filled-darker` or `filled-lighter` (a fill without a visible stroke: give the field a\n * visible label). Default: `WaveProvider inputDefaults.appearance`, else `'outline'`.\n *\n * The step buttons keep a 1px separator from the input only in `outline`; the other\n * appearances have none.\n */\n appearance?: InputAppearance;\n /** Ref to the root `<div>`. */\n ref?: React.Ref<HTMLDivElement>;\n /** Ref to the `<input role=\"spinbutton\">` (the focusable control). */\n controlRef?: React.Ref<HTMLInputElement>;\n}\n\n/** SpinButton props with a number value (the 0.7 shape). */\nexport interface SpinButtonProps extends SpinButtonBaseProps {\n /**\n * Allows an empty value (`null`). Takes the literal `true`: a `boolean` variable fits neither\n * member of the props, so branch the JSX or spread `{ allowEmpty: true, value }`.\n * @default false\n */\n allowEmpty?: false;\n /** Controlled numeric value. */\n value?: number;\n /** Initial value for uncontrolled usage (also what a form reset restores).\n * @default 0\n */\n defaultValue?: number;\n /**\n * Called with the new value when it changes: a step (buttons, arrow keys, PageUp/PageDown,\n * Home/End) or a typed value committed on blur or Enter. Not called while the user is typing,\n * nor when the value stays the same.\n */\n onValueChange?: (value: number) => void;\n /**\n * Called with the new value when it changes.\n * @deprecated Use `onValueChange`. (`onChange` is reserved for native change events.)\n */\n onChange?: (value: number) => void;\n}\n\n/** SpinButton props with `allowEmpty`: the value may be `null` (an empty field). */\nexport interface SpinButtonAllowEmptyProps extends SpinButtonBaseProps {\n /** The value may be `null`: clearing the text and committing it empties the field. */\n allowEmpty: true;\n /** Controlled value; `null` is an empty field. */\n value?: number | null;\n /** Initial value for uncontrolled usage (also what a form reset restores).\n * @default null\n */\n defaultValue?: number | null;\n /** Called with the new value when it changes, `null` when the field is emptied. */\n onValueChange?: (value: number | null) => void;\n /** Not available with `allowEmpty` (the deprecated alias of `onValueChange`). */\n onChange?: never;\n}\n\n/** Decimal numbers as a user types them: `12`, `-3`, `1.5`, `.5`, `2.`, `1e3`. */\nconst NUMBER_TEXT = /^[+-]?(\\d+\\.?\\d*|\\.\\d+)(e[+-]?\\d+)?$/i;\n/** Text on the way to a number (`-`, `.`, `-.`): not a value yet, but not an error either. */\nconst PARTIAL_NUMBER_TEXT = /^[+-]?\\.?$/;\n\nfunction parseNumberText(text: string): number | null {\n const trimmed = text.trim();\n if (!NUMBER_TEXT.test(trimmed)) return null;\n const n = Number(trimmed);\n return Number.isFinite(n) ? n : null;\n}\n\n/** Number of decimals of `n` as written by JavaScript (`0.1` → 1, `1e-7` → 7). */\nfunction decimalsOf(n: number): number {\n if (!Number.isFinite(n)) return 0;\n const [mantissa, exponent] = String(n).toLowerCase().split('e');\n const fraction = mantissa.split('.')[1]?.length ?? 0;\n return Math.max(0, fraction - (exponent ? Number(exponent) : 0));\n}\n\nfunction roundTo(n: number, decimals: number): number {\n return Number(n.toFixed(Math.min(decimals, 20)));\n}\n\n// Phase 4 D18, Fluent's timing (`DEFAULT_SPIN_DELAY_MS`, `MIN_SPIN_DELAY_MS`, `MAX_SPIN_TIME_MS`)\n// without its step on the press itself.\n/** Delay before a press becomes a hold, and the first delay between repeats (ms). */\nconst SPIN_DELAY = 300;\n/** The shortest delay between repeats, reached after SPIN_RAMP ms of holding. */\nconst SPIN_MIN_DELAY = 80;\n/** How long into a hold the delay between repeats takes to ease down to SPIN_MIN_DELAY (ms). */\nconst SPIN_RAMP = 1000;\n\n/** The delay after a step taken `elapsed` ms into a hold: 300ms easing linearly to 80ms. */\nfunction spinDelay(elapsed: number): number {\n const t = Math.min(1, elapsed / SPIN_RAMP);\n return Math.round(SPIN_DELAY + (SPIN_MIN_DELAY - SPIN_DELAY) * t);\n}\n\n/**\n * Runs the timers of a hold: calls `step` once SPIN_DELAY has passed, then again after each\n * `spinDelay` (steps at 300, 534, 717, 859, 970 and 1057ms, then every 80ms), until `step` returns\n * false or the returned function is called.\n */\nfunction startSpin(step: () => boolean): () => void {\n let elapsed = SPIN_DELAY;\n let timer = setTimeout(function tick() {\n if (!step()) return;\n const delay = spinDelay(elapsed);\n elapsed += delay;\n timer = setTimeout(tick, delay);\n }, SPIN_DELAY);\n return () => clearTimeout(timer);\n}\n\n/** Per-size widths of the step buttons and the input's flex basis, and the glyph size. */\nconst SPIN_SIZE: Readonly<Record<CoreSize, { button: string; input: string; glyph: number }>> = {\n small: { button: 'w-6', input: 'w-10', glyph: 12 },\n medium: { button: 'w-8', input: 'w-12', glyph: 12 },\n large: { button: 'w-10', input: 'w-14', glyph: 16 },\n};\n\nconst stepButtonClass = cn(\n // Padding and background set here (C-NATIVE): an app-wide `button` rule cannot fill them. The\n // size and, in `outline`, the separator are added per render (SPIN_SIZE, D13).\n 'flex h-full shrink-0 items-center justify-center bg-transparent p-0 text-foreground',\n 'not-disabled:not-aria-disabled:hover:bg-subtle-hover not-disabled:not-aria-disabled:active:bg-subtle-pressed',\n 'disabled:pointer-events-none',\n // A finger holding a button (D18): no double-tap zoom, text selection or iOS callout.\n 'touch-manipulation select-none [-webkit-touch-callout:none]',\n);\n\n/**\n * A numeric input with decrement/increment buttons (APG spinbutton pattern).\n *\n * - **Keyboard** on the input: ArrowUp/ArrowDown step by `step`, PageUp/PageDown by `largeStep`\n * (default ten steps), Home/End jump to `min`/`max` when they are finite (Shift+Home and\n * Shift+End keep their native text-selection instead). The −/+ buttons are not tab stops and\n * never take focus, so focus stays on the spinbutton and screen readers announce the new\n * value.\n * - **Pointer** on the −/+ buttons (mouse, touch or pen): a short press steps once, on release\n * (its click), never on the press alone, so a finger that starts a scroll on a button changes\n * nothing. Holding a button repeats the step: the first after 300ms, then faster, every 80ms\n * after about a second (Fluent's timing). A hold stops when the button is released, the\n * pointer leaves it or the press turns into a scroll, at the bound, when the window loses\n * focus, and when the spin button becomes disabled or read-only. Only the primary button of a\n * primary pointer holds: a second finger, a secondary button or a Ctrl+press with a mouse (the\n * secondary click on macOS) neither starts a hold nor ends one. A click without a press (a\n * screen reader, `element.click()`) steps once. The buttons open no context menu (a long press\n * on touch would), show no iOS callout, and allow no text selection or double-tap zoom.\n * - **Typing** edits a draft: the value is parsed, clamped and committed on blur or Enter\n * (Escape reverts). Text that is not a number is flagged with `aria-invalid` and reverted on\n * blur. Steps (keys and buttons) start from the typed number, and the −/+ buttons are disabled\n * when that number is at `min`/`max`. Steps are rounded to the precision of `step` and of the\n * value, so `0.1 + 0.2` is `0.3`; `precision` rounds every committed value, typed or stepped,\n * to a fixed number of decimals instead, without padding it (pad with `displayValue`).\n * - **Routing**: `id`, ARIA, native input attributes and focus/keyboard handlers go to the\n * `<input role=\"spinbutton\">`; `className`, `style`, `data-*`, other handlers and `ref` stay on\n * the root (`controlRef` reaches the input). Inside a `Field` it picks up the label, hint, error\n * and required state automatically. It has no built-in name: pass `aria-label`/`aria-labelledby`\n * or use a label (development warning otherwise).\n * - **Forms**: with `name` the committed value is submitted; `required` makes the input natively\n * required; a form reset restores `defaultValue`.\n * - **Empty values**: `allowEmpty` lets the value become `null` (an empty field). Clearing the\n * text and committing it (blur or Enter) sets the value to `null`, uncontrolled too; stepping\n * up or down from an empty value starts at 0 and clamps to the nearest bound. `required` (its\n * own, or a required Field's) still blocks submitting an empty value: the check runs on the\n * `HiddenInput` that carries the committed value, not on the shown text, so it keeps working\n * even when the shown text is not empty.\n * - **Display text**: `displayValue` shows formatted text (`$1.00`) for the value while the field\n * is not being edited, and as its `aria-valuetext`; while the field has focus and can be\n * edited it shows the plain number instead, so typing still edits the number (a consumer\n * `aria-valuetext` always wins). It applies only while `value` is controlled: an uncontrolled\n * spin button ignores it and warns once in development.\n * - **Size and appearance**: `size` resolves from its own prop, then the surrounding `Field`'s\n * `size`, then `WaveProvider inputDefaults.size`, else `'medium'`; `appearance` from its own\n * prop, then `WaveProvider inputDefaults.appearance`, else `'outline'`; both render as\n * `data-size` and `data-appearance` on the root. SpinButton takes every size the other fields\n * do, and unlike 0.7 its root is now exactly as tall as theirs, with its step buttons and\n * input taking up that height; sizing the root wider widens the input instead of leaving\n * empty space inside it. The step buttons keep a 1px separator from the input only in\n * `outline`; the other appearances have none.\n * - The −/+ buttons are named \"Decrement\"/\"Increment\"; `labels` localizes the names.\n *\n * @example\n * <SpinButton aria-label=\"Quantity\" min={1} max={10} value={qty} onValueChange={setQty} />\n */\nexport const SpinButton = (props: SpinButtonProps | SpinButtonAllowEmptyProps): React.ReactNode => {\n const {\n allowEmpty = false,\n value: valueProp,\n defaultValue,\n onValueChange,\n onChange,\n ...baseProps\n } = props as SpinButtonBaseProps & {\n allowEmpty?: boolean;\n value?: number | null;\n defaultValue?: number | null;\n onValueChange?: (value: number | null) => void;\n onChange?: (value: number) => void;\n };\n const {\n min = -Infinity,\n max = Infinity,\n step = 1,\n largeStep,\n precision,\n disabled,\n name,\n form,\n labels,\n displayValue,\n size: sizeProp,\n appearance: appearanceProp,\n className,\n hidden,\n ref,\n controlRef,\n // Routed to the input (C-ROUTING)\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-invalid': ariaInvalid,\n 'aria-required': ariaRequired,\n 'aria-errormessage': ariaErrorMessage,\n 'aria-details': ariaDetails,\n 'aria-valuetext': ariaValueText,\n required,\n readOnly,\n placeholder,\n maxLength,\n spellCheck,\n autoComplete = 'off',\n autoFocus,\n inputMode,\n enterKeyHint,\n tabIndex,\n onFocus,\n onBlur,\n onKeyDown,\n onKeyUp,\n ...rest\n } = baseProps;\n\n if (onChange !== undefined) warnDeprecated('SpinButton', 'onChange', 'onValueChange');\n const initialValue = defaultValue !== undefined ? defaultValue : allowEmpty ? null : 0;\n const [value, setValue, isControlled] = useControllable<number | null>(\n valueProp,\n initialValue,\n (next) => {\n onValueChange?.(next);\n if (next !== null) onChange?.(next);\n },\n );\n\n // The text is a draft while the user types (null = show the value).\n const [draft, setDraft] = React.useState<string | null>(null);\n // A value that changes from outside replaces the draft (adjust state during render, C-HOOKS).\n const [prevValue, setPrevValue] = React.useState(value);\n if (!Object.is(prevValue, value)) {\n setPrevValue(value);\n setDraft(null);\n }\n // Phase 4 D15: whether the input currently has focus, tracked from the composed onFocus/onBlur\n // below (after the consumer's), for the displayValue formula.\n const [focused, setFocused] = React.useState(false);\n // Whether the whole text was selected right before the focus that `focused` now reflects, so\n // the effect below can reselect the whole new text once the shown text switches.\n const reselectRef = React.useRef(false);\n\n const draftNumber = draft === null ? null : parseNumberText(draft);\n // What stepping starts from: the typed draft when it is a number, else the value. The −/+\n // buttons are enabled by it too, so they agree with ArrowUp/ArrowDown while the user types.\n const current = draftNumber ?? value;\n const draftInvalid =\n draft !== null &&\n draftNumber === null &&\n draft.trim() !== '' &&\n !PARTIAL_NUMBER_TEXT.test(draft.trim());\n\n const clamp = (n: number) => Math.min(max, Math.max(min, n));\n const interactive = !disabled && !readOnly;\n // Phase 4 D15: displayValue applies only while the value is controlled; while the field has\n // focus and can be edited, the plain number always shows instead (never displayValue).\n const showsDisplay = isControlled && displayValue !== undefined;\n // Phase 4 D19: a non-finite precision (NaN, Infinity) behaves as unset instead of reaching\n // roundTo, since Math.trunc(NaN) is NaN and Math.min/Math.max would otherwise carry it\n // through to `toFixed`, which treats a NaN argument as 0 (rounding to whole numbers).\n const places =\n precision === undefined || !Number.isFinite(precision)\n ? undefined\n : Math.min(20, Math.max(0, Math.trunc(precision)));\n\n const commit = (next: number) => {\n setDraft(null);\n const rounded = places === undefined ? next : roundTo(next, places);\n setValue(clamp(rounded));\n };\n\n /** The value one step of `delta` from `from` (0 while empty), rounded and clamped. */\n const stepFrom = (from: number | null, delta: number) => {\n const base = from ?? 0;\n const decimals = places ?? Math.max(decimalsOf(step), decimalsOf(delta), decimalsOf(base));\n return clamp(roundTo(base + delta, decimals));\n };\n\n /** Steps from the typed draft when it is a number, else from the value (0 while empty). */\n const stepBy = (delta: number) => {\n setDraft(null);\n setValue(stepFrom(current, delta));\n };\n\n // Phase 4 D18: a press on a step button becomes a hold after SPIN_DELAY and then repeats\n // (startSpin runs the timers); a shorter press steps on its click (WCAG 2.5.2).\n const holdRef = React.useRef<{\n /** The pointer holding the button: only its release, cancel or leave ends the hold. */\n pointerId: number;\n direction: 1 | -1;\n /** Whether the hold has stepped yet. */\n stepped: boolean;\n stop: () => void;\n } | null>(null);\n // Whether the press that ends in the next pointer click has repeated (reset by every press of\n // a primary pointer).\n const repeatedRef = React.useRef(false);\n\n const stopHold = React.useCallback(() => {\n holdRef.current?.stop();\n holdRef.current = null;\n }, []);\n\n /** Ends the hold on a pointerup, pointercancel or pointerleave of the pointer holding it. */\n const endHold = (event: React.PointerEvent<HTMLButtonElement>) => {\n if (event.pointerId === holdRef.current?.pointerId) stopHold();\n };\n\n /** Whether the value the user sees is at the bound a step in `direction` moves towards. */\n const atBound = (direction: 1 | -1) =>\n current !== null && (direction === 1 ? current >= max : current <= min);\n\n // One step of the hold, with the latest render's values; returns false to end the hold.\n const holdStep = useEventCallback((): boolean => {\n const hold = holdRef.current;\n if (!hold) return false;\n if (!interactive || atBound(hold.direction)) {\n stopHold();\n return false;\n }\n repeatedRef.current = true;\n const delta = hold.direction * step;\n if (hold.stepped) {\n // Later steps start from what useControllable's updater gets, never from this render's\n // closure: a value set earlier in the same task (several steps can run before React\n // renders), else the rendered one, so a controlled parent that ignored a step is stepped\n // from its own value again.\n setDraft(null);\n setValue((prev) => stepFrom(prev, delta));\n } else {\n // The first step starts where a click would: from the typed number, else the value.\n hold.stepped = true;\n stepBy(delta);\n }\n return true;\n });\n\n const startHold = (event: React.PointerEvent<HTMLButtonElement>, direction: 1 | -1) => {\n // Only a primary pointer presses a step button: a second finger (which gets no click) neither\n // starts a hold nor resets the flag of the press that holds one.\n if (!event.isPrimary) return;\n // Every press resets the flag, so a hold that ended without a click (at a bound, or a touch\n // long press) cannot swallow the click of a later press.\n repeatedRef.current = false;\n // A hold needs the primary button: not a secondary one, nor a Ctrl+press with a mouse (the\n // secondary click on macOS).\n if (event.button !== 0 || (event.pointerType === 'mouse' && event.ctrlKey) || !interactive) {\n return;\n }\n // Touch and pen capture the pointer implicitly on the element the press landed on (the glyph\n // when the finger lands on it); release it there, so pointerleave fires when the finger slides\n // off the button.\n const holder = event.target instanceof Element ? event.target : event.currentTarget;\n if (event.pointerType !== 'mouse' && holder.hasPointerCapture?.(event.pointerId)) {\n holder.releasePointerCapture(event.pointerId);\n }\n stopHold();\n holdRef.current = {\n pointerId: event.pointerId,\n direction,\n stepped: false,\n stop: startSpin(holdStep),\n };\n };\n\n const clickStep = (event: React.MouseEvent<HTMLButtonElement>, direction: 1 | -1) => {\n // A click without a press (detail 0: a screen reader, element.click()) steps once; a pointer\n // click steps unless its press already repeated.\n if (event.detail !== 0 && repeatedRef.current) {\n repeatedRef.current = false;\n return;\n }\n stepBy(direction * step);\n };\n\n // A hold stops once the control cannot step its way any more: disabled, read-only, or at the\n // bound it steps towards (where its button is disabled).\n React.useEffect(() => {\n const hold = holdRef.current;\n if (hold && (!interactive || atBound(hold.direction))) stopHold();\n });\n // It also stops when the window loses focus (a release outside it never reaches the button),\n // and on unmount.\n React.useEffect(() => {\n window.addEventListener('blur', stopHold);\n return () => {\n window.removeEventListener('blur', stopHold);\n stopHold();\n };\n }, [stopHold]);\n\n const commitDraft = () => {\n if (draft === null) return;\n if (allowEmpty && draft.trim() === '') {\n setDraft(null);\n setValue(null);\n return;\n }\n if (draftNumber === null) setDraft(null);\n else commit(draftNumber);\n };\n\n const inputRef = React.useRef<HTMLInputElement>(null);\n const mergedInputRef = useMergedRefs(inputRef, controlRef);\n\n const field = useFieldContext();\n const isRequired = required ?? field?.required ?? false;\n const fieldProps = useFieldControl(\n {\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-invalid': ariaInvalid,\n 'aria-required': ariaRequired,\n required,\n },\n { nativeRequired: true },\n );\n // Typed text that is not a number is invalid whatever the consumer says; otherwise the\n // consumer's or the Field's `aria-invalid` is passed through as is (`grammar` included), and the\n // error look follows the shared rule of the text controls.\n const ariaInvalidValue = draftInvalid ? true : fieldProps['aria-invalid'];\n const invalidLook = isInvalidLook(false, ariaInvalidValue);\n const { size, appearance } = useInputLook(sizeProp, appearanceProp);\n\n useFormReset(\n inputRef,\n () => {\n setDraft(null);\n setValue(initialValue);\n },\n form,\n );\n\n // No built-in name any more (\"Value\" named every instance the same): report unnamed inputs.\n React.useEffect(() => {\n const input = inputRef.current;\n if (!input) return;\n const named =\n input.hasAttribute('aria-label') ||\n input.hasAttribute('aria-labelledby') ||\n (input.labels?.length ?? 0) > 0 ||\n input.hasAttribute('title');\n if (!named) {\n warnOnce(\n 'SpinButton:unnamed',\n 'SpinButton: the spinbutton has no accessible name. Pass `aria-label` or `aria-labelledby`, use a <label htmlFor>, or render it inside a Field.',\n );\n }\n });\n\n // Phase 4 D15: an uncontrolled SpinButton ignores displayValue (it would make the shown text\n // lie as soon as the value changes without a matching prop update).\n React.useEffect(() => {\n if (displayValue !== undefined && !isControlled) {\n warnOnce(\n 'SpinButton:displayValue-uncontrolled',\n 'SpinButton: `displayValue` is ignored while the value is uncontrolled; pass `value` (and update it in `onValueChange`).',\n );\n }\n }, [displayValue, isControlled]);\n\n // A stable callback, so composing it with the consumer's onFocus reads no ref during render.\n const handleFocus = useEventCallback((e: React.FocusEvent<HTMLInputElement>) => {\n const input = e.target;\n reselectRef.current =\n input.value.length > 0 &&\n input.selectionStart === 0 &&\n input.selectionEnd === input.value.length;\n setFocused(true);\n });\n\n const handleBlur = () => {\n commitDraft();\n setFocused(false);\n };\n\n // Reselects the whole text once the shown text switches (blur shows displayValue, and a\n // programmatic focus that starts the field's editing mode shows the plain number) after a\n // focus that started with everything selected (Tab, or a consumer onFocus that calls\n // select()), so typing right after still replaces the whole value.\n React.useLayoutEffect(() => {\n if (!focused || !reselectRef.current) return;\n reselectRef.current = false;\n inputRef.current?.select();\n }, [focused]);\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n // Read-only: the keys keep their native caret behaviour and never change the value.\n if (!interactive || e.altKey || e.ctrlKey || e.metaKey) return;\n // Ten steps, rounded: step * 10 carries float error (0.07 * 10 = 0.7000000000000001) that\n // stepBy would otherwise keep, since it rounds to the precision of the delta too.\n const big = largeStep ?? roundTo(step * 10, decimalsOf(step));\n switch (e.key) {\n case 'ArrowUp':\n case 'ArrowDown':\n case 'PageUp':\n case 'PageDown': {\n e.preventDefault();\n const amount = e.key === 'ArrowUp' || e.key === 'ArrowDown' ? step : big;\n stepBy(e.key === 'ArrowUp' || e.key === 'PageUp' ? amount : -amount);\n return;\n }\n case 'Home':\n case 'End': {\n if (e.shiftKey) return; // Shift+Home/End select text (Fluent)\n const bound = e.key === 'Home' ? min : max;\n if (!Number.isFinite(bound)) return; // no bound: Home/End move the caret\n e.preventDefault();\n commit(bound);\n return;\n }\n case 'Enter':\n // Not prevented: Enter still submits the surrounding form, with the committed value.\n commitDraft();\n return;\n case 'Escape':\n if (draft !== null) {\n e.preventDefault(); // enclosing layers (a Dialog) ignore this Escape\n setDraft(null);\n }\n return;\n default:\n }\n };\n\n const defaultInputMode: React.HTMLAttributes<HTMLInputElement>['inputMode'] =\n min < 0 ? 'text' : decimalsOf(step) > 0 || decimalsOf(min) > 0 ? 'decimal' : 'numeric';\n\n // The step buttons never take focus: a pointer press leaves it where it is (on the input while\n // editing), so a button that becomes disabled at a bound cannot drop focus to <body>.\n const keepFocus = (e: React.MouseEvent) => e.preventDefault();\n\n return (\n <div\n ref={ref}\n data-size={size}\n data-appearance={appearance}\n className={cn(\n spinButtonClassNames.root,\n 'relative inline-flex items-center',\n inputHeightClasses[size],\n inputTextClasses[size],\n inputAppearanceClasses[appearance],\n inputFocusWithin,\n invalidLook && inputInvalidWithin,\n disabled && 'cursor-not-allowed opacity-50',\n className,\n )}\n hidden={hidden}\n {...rest}\n >\n <button\n type=\"button\"\n tabIndex={-1}\n onPointerDown={(event) => startHold(event, -1)}\n onPointerUp={endHold}\n onPointerCancel={endHold}\n onPointerLeave={endHold}\n onClick={(event) => clickStep(event, -1)}\n onMouseDown={keepFocus}\n onContextMenu={(event) => event.preventDefault()}\n disabled={!interactive || (current !== null && current <= min)}\n aria-label={labels?.decrement ?? 'Decrement'}\n className={cn(\n spinButtonClassNames.decrementButton,\n stepButtonClass,\n SPIN_SIZE[size].button,\n appearance === 'outline' && 'border-e border-input',\n appearance === 'filled-darker' &&\n 'not-disabled:not-aria-disabled:hover:bg-subtle-pressed',\n !disabled && 'disabled:opacity-50',\n )}\n >\n <SubtractIcon size={SPIN_SIZE[size].glyph} />\n </button>\n\n <input\n ref={mergedInputRef}\n type=\"text\"\n role=\"spinbutton\"\n inputMode={inputMode ?? defaultInputMode}\n autoComplete={autoComplete}\n autoFocus={autoFocus}\n enterKeyHint={enterKeyHint}\n placeholder={placeholder}\n maxLength={maxLength}\n spellCheck={spellCheck}\n tabIndex={tabIndex}\n readOnly={readOnly}\n form={form}\n {...fieldProps}\n aria-invalid={ariaInvalidValue}\n aria-errormessage={ariaErrorMessage}\n aria-details={ariaDetails}\n aria-valuenow={value ?? undefined}\n aria-valuemin={Number.isFinite(min) ? min : undefined}\n aria-valuemax={Number.isFinite(max) ? max : undefined}\n aria-valuetext={ariaValueText ?? (showsDisplay ? displayValue : undefined)}\n value={\n draft ??\n (showsDisplay && !(focused && interactive)\n ? displayValue\n : value === null\n ? ''\n : String(value))\n }\n onChange={(e) => setDraft(e.target.value)}\n onFocus={composeEventHandlers(onFocus, handleFocus, { checkDefaultPrevented: false })}\n onBlur={composeEventHandlers(onBlur, handleBlur, { checkDefaultPrevented: false })}\n onKeyDown={composeEventHandlers(onKeyDown, handleKeyDown)}\n onKeyUp={onKeyUp}\n disabled={disabled}\n className={cn(\n spinButtonClassNames.input,\n 'h-full min-w-0 flex-auto border-none bg-transparent text-center text-foreground focus:outline-hidden disabled:cursor-not-allowed [appearance:textfield]',\n SPIN_SIZE[size].input,\n inputTextClasses[size],\n )}\n />\n\n <button\n type=\"button\"\n tabIndex={-1}\n onPointerDown={(event) => startHold(event, 1)}\n onPointerUp={endHold}\n onPointerCancel={endHold}\n onPointerLeave={endHold}\n onClick={(event) => clickStep(event, 1)}\n onMouseDown={keepFocus}\n onContextMenu={(event) => event.preventDefault()}\n disabled={!interactive || (current !== null && current >= max)}\n aria-label={labels?.increment ?? 'Increment'}\n className={cn(\n spinButtonClassNames.incrementButton,\n stepButtonClass,\n SPIN_SIZE[size].button,\n appearance === 'outline' && 'border-s border-input',\n appearance === 'filled-darker' &&\n 'not-disabled:not-aria-disabled:hover:bg-subtle-pressed',\n !disabled && 'disabled:opacity-50',\n )}\n >\n <AddIcon size={SPIN_SIZE[size].glyph} />\n </button>\n\n <HiddenInput\n name={name}\n form={form}\n disabled={disabled}\n type=\"text\"\n value={value === null ? '' : String(value)}\n required={allowEmpty ? isRequired : undefined}\n onInvalid={() => inputRef.current?.focus()}\n />\n </div>\n );\n};\n\nSpinButton.displayName = 'SpinButton';\n"],"mappings":"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"}