@wallarm-org/design-system 0.69.0-rc-feature-WDS-143.2 → 0.69.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 (52) hide show
  1. package/dist/components/Attribute/index.d.ts +0 -8
  2. package/dist/components/Attribute/index.js +1 -9
  3. package/dist/components/Calendar/Calendar.js +4 -14
  4. package/dist/components/Calendar/CalendarContext.d.ts +2 -19
  5. package/dist/components/Calendar/CalendarHeader.js +1 -2
  6. package/dist/components/Calendar/CalendarInputHeader.js +27 -27
  7. package/dist/components/Slider/Slider.d.ts +34 -0
  8. package/dist/components/Slider/Slider.figma.d.ts +1 -0
  9. package/dist/components/Slider/Slider.figma.js +123 -0
  10. package/dist/components/Slider/Slider.js +85 -0
  11. package/dist/components/Slider/SliderContext.d.ts +31 -0
  12. package/dist/components/Slider/SliderContext.js +9 -0
  13. package/dist/components/Slider/SliderControl.d.ts +9 -0
  14. package/dist/components/Slider/SliderControl.js +35 -0
  15. package/dist/components/Slider/SliderInput.d.ts +26 -0
  16. package/dist/components/Slider/SliderInput.js +69 -0
  17. package/dist/components/Slider/SliderMarks.d.ts +15 -0
  18. package/dist/components/Slider/SliderMarks.js +58 -0
  19. package/dist/components/Slider/SliderThumb.d.ts +19 -0
  20. package/dist/components/Slider/SliderThumb.js +58 -0
  21. package/dist/components/Slider/SliderValue.d.ts +14 -0
  22. package/dist/components/Slider/SliderValue.js +21 -0
  23. package/dist/components/Slider/classes.d.ts +52 -0
  24. package/dist/components/Slider/classes.js +14 -0
  25. package/dist/components/Slider/index.d.ts +8 -0
  26. package/dist/components/Slider/index.js +8 -0
  27. package/dist/components/Slider/types.d.ts +11 -0
  28. package/dist/components/Slider/types.js +0 -0
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.js +2 -1
  31. package/dist/metadata/components.json +2439 -3630
  32. package/package.json +1 -1
  33. package/dist/components/Attribute/AttributeEdit.d.ts +0 -28
  34. package/dist/components/Attribute/AttributeEdit.js +0 -151
  35. package/dist/components/Attribute/AttributeEditContext.d.ts +0 -22
  36. package/dist/components/Attribute/AttributeEditContext.js +0 -9
  37. package/dist/components/Attribute/AttributeEditControl.d.ts +0 -6
  38. package/dist/components/Attribute/AttributeEditControl.js +0 -69
  39. package/dist/components/Attribute/AttributeEditError.d.ts +0 -6
  40. package/dist/components/Attribute/AttributeEditError.js +0 -32
  41. package/dist/components/Attribute/AttributeEditInput.d.ts +0 -4
  42. package/dist/components/Attribute/AttributeEditInput.js +0 -19
  43. package/dist/components/Attribute/AttributeEditNumber.d.ts +0 -11
  44. package/dist/components/Attribute/AttributeEditNumber.js +0 -17
  45. package/dist/components/Attribute/AttributeEditPreview.d.ts +0 -18
  46. package/dist/components/Attribute/AttributeEditPreview.js +0 -111
  47. package/dist/components/Attribute/AttributeEditTextarea.d.ts +0 -4
  48. package/dist/components/Attribute/AttributeEditTextarea.js +0 -17
  49. package/dist/components/Calendar/dateValue.d.ts +0 -18
  50. package/dist/components/Calendar/dateValue.js +0 -7
  51. package/dist/components/Calendar/useCalendarTime.d.ts +0 -25
  52. package/dist/components/Calendar/useCalendarTime.js +0 -38
@@ -3,14 +3,6 @@ export { AttributeActions, type AttributeActionsProps } from './AttributeActions
3
3
  export { AttributeActionsContent, type AttributeActionsContentProps, } from './AttributeActionsContent';
4
4
  export { AttributeActionsItem, type AttributeActionsItemProps } from './AttributeActionsItem';
5
5
  export { AttributeActionsTarget, type AttributeActionsTargetProps } from './AttributeActionsTarget';
6
- export { AttributeEdit, type AttributeEditProps } from './AttributeEdit';
7
- export { type AttributeEditActivationMode, type AttributeEditContextValue, type AttributeEditStatus, type AttributeEditSubmitMode, useAttributeEdit, } from './AttributeEditContext';
8
- export { AttributeEditControl, type AttributeEditControlProps } from './AttributeEditControl';
9
- export { AttributeEditError, type AttributeEditErrorProps } from './AttributeEditError';
10
- export { AttributeEditInput, type AttributeEditInputProps } from './AttributeEditInput';
11
- export { AttributeEditNumber, type AttributeEditNumberProps } from './AttributeEditNumber';
12
- export { AttributeEditPreview, type AttributeEditPreviewProps } from './AttributeEditPreview';
13
- export { AttributeEditTextarea, type AttributeEditTextareaProps } from './AttributeEditTextarea';
14
6
  export { useAttributeEmpty } from './AttributeEmptyContext';
15
7
  export { AttributeEmptyDescription, type AttributeEmptyDescriptionProps, } from './AttributeEmptyDescription';
16
8
  export { AttributeLabel, type AttributeLabelProps } from './AttributeLabel';
@@ -3,18 +3,10 @@ import { AttributeActions } from "./AttributeActions.js";
3
3
  import { AttributeActionsContent } from "./AttributeActionsContent.js";
4
4
  import { AttributeActionsItem } from "./AttributeActionsItem.js";
5
5
  import { AttributeActionsTarget } from "./AttributeActionsTarget.js";
6
- import { AttributeEdit } from "./AttributeEdit.js";
7
- import { useAttributeEdit } from "./AttributeEditContext.js";
8
- import { AttributeEditControl } from "./AttributeEditControl.js";
9
- import { AttributeEditError } from "./AttributeEditError.js";
10
- import { AttributeEditInput } from "./AttributeEditInput.js";
11
- import { AttributeEditNumber } from "./AttributeEditNumber.js";
12
- import { AttributeEditPreview } from "./AttributeEditPreview.js";
13
- import { AttributeEditTextarea } from "./AttributeEditTextarea.js";
14
6
  import { useAttributeEmpty } from "./AttributeEmptyContext.js";
15
7
  import { AttributeEmptyDescription } from "./AttributeEmptyDescription.js";
16
8
  import { AttributeLabel } from "./AttributeLabel.js";
17
9
  import { AttributeLabelDescription } from "./AttributeLabelDescription.js";
18
10
  import { AttributeLabelInfo } from "./AttributeLabelInfo.js";
19
11
  import { AttributeValue } from "./AttributeValue.js";
20
- export { Attribute, AttributeActions, AttributeActionsContent, AttributeActionsItem, AttributeActionsTarget, AttributeEdit, AttributeEditControl, AttributeEditError, AttributeEditInput, AttributeEditNumber, AttributeEditPreview, AttributeEditTextarea, AttributeEmptyDescription, AttributeLabel, AttributeLabelDescription, AttributeLabelInfo, AttributeValue, useAttributeEdit, useAttributeEmpty };
12
+ export { Attribute, AttributeActions, AttributeActionsContent, AttributeActionsItem, AttributeActionsTarget, AttributeEmptyDescription, AttributeLabel, AttributeLabelDescription, AttributeLabelInfo, AttributeValue, useAttributeEmpty };
@@ -3,8 +3,7 @@ import { useCallback } from "react";
3
3
  import { DatePicker } from "@ark-ui/react";
4
4
  import { CalendarDate, endOfMonth, getLocalTimeZone, startOfMonth, today } from "@internationalized/date";
5
5
  import { CalendarProvider } from "./CalendarContext.js";
6
- import { toDateValue } from "./dateValue.js";
7
- import { useCalendarTime } from "./useCalendarTime.js";
6
+ const toDateValue = (date)=>date;
8
7
  const getThisMonthRange = ()=>{
9
8
  const now = today(getLocalTimeZone());
10
9
  return [
@@ -116,13 +115,6 @@ const DEFAULT_RANGE_PRESETS = [
116
115
  const Calendar = ({ type = 'single', showTime = false, showKeyboardHints = true, value, defaultValue, onChange, minDate, maxDate, isDateUnavailable, disallowDisabledDatesInRange = false, open, defaultOpen, onOpenChange, closeOnSelect = true, readonly = false, focusedValue, children })=>{
117
116
  const isRange = 'range' === type;
118
117
  const numOfMonths = isRange ? 2 : 1;
119
- const { timeRef, commitValue } = useCalendarTime({
120
- showTime,
121
- isRange,
122
- value,
123
- defaultValue,
124
- onChange
125
- });
126
118
  const hasUnavailableDateInRange = useCallback((start, end)=>{
127
119
  if (!isDateUnavailable) return false;
128
120
  let current = new CalendarDate(start.year, start.month, start.day);
@@ -151,9 +143,9 @@ const Calendar = ({ type = 'single', showTime = false, showKeyboardHints = true,
151
143
  const [start, end] = newValue;
152
144
  if (start && end && hasUnavailableDateInRange(start, end)) return;
153
145
  }
154
- commitValue(newValue);
146
+ onChange?.(newValue);
155
147
  }, [
156
- commitValue,
148
+ onChange,
157
149
  disallowDisabledDatesInRange,
158
150
  isRange,
159
151
  hasUnavailableDateInRange
@@ -169,9 +161,7 @@ const Calendar = ({ type = 'single', showTime = false, showKeyboardHints = true,
169
161
  isRange,
170
162
  showKeyboardHints,
171
163
  showTime,
172
- readonly,
173
- timeRef,
174
- commitValue
164
+ readonly
175
165
  };
176
166
  return /*#__PURE__*/ jsx(DatePicker.Root, {
177
167
  selectionMode: isRange ? 'range' : 'single',
@@ -1,5 +1,5 @@
1
- import { type ReactNode, type RefObject } from 'react';
2
- import type { CalendarType, DateValue } from './types';
1
+ import { type ReactNode } from 'react';
2
+ import type { CalendarType } from './types';
3
3
  export interface CalendarContextValue {
4
4
  /** Calendar type: 'single' for one month, 'range' for two months (date range selection) */
5
5
  type: CalendarType;
@@ -11,23 +11,6 @@ export interface CalendarContextValue {
11
11
  showTime: boolean;
12
12
  /** Whether calendar is readonly (display only, no selection) */
13
13
  readonly: boolean;
14
- /**
15
- * Last-known time-of-day, owned by `Calendar`. In `showTime` mode the grid
16
- * only produces date-only values; this ref carries the hour/minute so they
17
- * survive a day change and so `Calendar`'s `onChange` can emit a full
18
- * `CalendarDateTime`. Undefined outside a `Calendar`.
19
- */
20
- timeRef?: RefObject<{
21
- hour: number;
22
- minute: number;
23
- }>;
24
- /**
25
- * Push a value straight to the consumer `onChange`, bypassing Ark's machine.
26
- * Ark's DatePicker dedupes its value by date (year/month/day) and silently
27
- * drops a same-date, time-only change, so the `showTime` header uses this to
28
- * commit a time edit. Undefined outside a `Calendar`.
29
- */
30
- commitValue?: (value: DateValue[]) => void;
31
14
  }
32
15
  export interface CalendarProviderProps {
33
16
  children: ReactNode;
@@ -12,7 +12,6 @@ import { Tooltip } from "../Tooltip/index.js";
12
12
  import { TooltipContent } from "../Tooltip/TooltipContent.js";
13
13
  import { TooltipTrigger } from "../Tooltip/TooltipTrigger.js";
14
14
  import { useCalendarContext } from "./CalendarContext.js";
15
- import { toDateValue } from "./dateValue.js";
16
15
  const CalendarHeader = ({ showArrows = true, showJumpToToday = false, visibleRangeText: visibleRangeTextProp })=>{
17
16
  const { isRange, readonly } = useCalendarContext();
18
17
  const effectiveShowArrows = showArrows && !readonly;
@@ -61,7 +60,7 @@ const CalendarHeader = ({ showArrows = true, showJumpToToday = false, visibleRan
61
60
  size: "small",
62
61
  "aria-label": "Jump to today",
63
62
  onClick: ()=>{
64
- api.setFocusedValue(toDateValue(todayDate));
63
+ api.setFocusedValue(todayDate);
65
64
  },
66
65
  children: /*#__PURE__*/ jsx(JumpToTodayIcon, {
67
66
  size: "md"
@@ -1,12 +1,28 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { useCallback, useReducer } from "react";
2
+ import { useCallback, useState } from "react";
3
3
  import { DatePicker } from "@ark-ui/react";
4
+ import { CalendarDateTime } from "@internationalized/date";
4
5
  import { cn } from "../../utils/cn.js";
5
6
  import { DateInput } from "../DateInput/index.js";
6
7
  import { useCalendarContext } from "./CalendarContext.js";
7
- import { sameDate, toArkDateValue, toReactAriaDateValue, withTime } from "./dateValue.js";
8
- const SingleDateInputInner = ({ api, readonly, showTime, timeRef, commitValue })=>{
9
- const [, bumpTime] = useReducer((n)=>n + 1, 0);
8
+ const toReactAriaDateValue = (date)=>{
9
+ if (!date) return null;
10
+ return date;
11
+ };
12
+ const toArkDateValue = (date)=>{
13
+ if (!date) return;
14
+ return date;
15
+ };
16
+ const withTime = (value, time)=>{
17
+ if (!value) return null;
18
+ if ('hour' in value) return value;
19
+ return new CalendarDateTime(value.year, value.month, value.day, time.hour, time.minute);
20
+ };
21
+ const SingleDateInputInner = ({ api, readonly, showTime })=>{
22
+ const [time, setTime] = useState({
23
+ hour: 0,
24
+ minute: 0
25
+ });
10
26
  const handleChange = useCallback((newValue)=>{
11
27
  const arkValue = toArkDateValue(newValue);
12
28
  if (arkValue) api.setValue([
@@ -17,34 +33,20 @@ const SingleDateInputInner = ({ api, readonly, showTime, timeRef, commitValue })
17
33
  api
18
34
  ]);
19
35
  const handleDateTimeChange = useCallback((newValue)=>{
20
- if (newValue && 'hour' in newValue && timeRef) timeRef.current = {
36
+ if (newValue && 'hour' in newValue) setTime({
21
37
  hour: newValue.hour,
22
38
  minute: newValue.minute
23
- };
24
- if (newValue && sameDate(api.value[0], newValue)) {
25
- const arkValue = toArkDateValue(newValue);
26
- if (arkValue) commitValue?.([
27
- arkValue
28
- ]);
29
- bumpTime();
30
- } else handleChange(newValue);
39
+ });
40
+ handleChange(newValue);
31
41
  }, [
32
- api,
33
- handleChange,
34
- timeRef,
35
- commitValue
42
+ handleChange
36
43
  ]);
37
44
  const inputValue = toReactAriaDateValue(api.value[0]);
38
- const time = timeRef?.current ?? {
39
- hour: 0,
40
- minute: 0
41
- };
42
- const dateTimeValue = inputValue ? toReactAriaDateValue(withTime(inputValue, time)) : null;
43
45
  return /*#__PURE__*/ jsx("div", {
44
46
  className: "flex flex-1",
45
47
  onKeyDown: (e)=>e.stopPropagation(),
46
48
  children: showTime ? /*#__PURE__*/ jsx(DateInput, {
47
- value: dateTimeValue,
49
+ value: withTime(inputValue, time),
48
50
  onChange: handleDateTimeChange,
49
51
  readOnly: readonly,
50
52
  granularity: "minute",
@@ -119,7 +121,7 @@ const RangeDateInputInner = ({ api, readonly })=>{
119
121
  });
120
122
  };
121
123
  const CalendarInputHeader = ({ className })=>{
122
- const { isRange, readonly, showTime, timeRef, commitValue } = useCalendarContext();
124
+ const { isRange, readonly, showTime } = useCalendarContext();
123
125
  return /*#__PURE__*/ jsx("div", {
124
126
  className: cn('flex flex-1 items-center', 'pt-20 pb-4 px-20', className),
125
127
  children: /*#__PURE__*/ jsx(DatePicker.Context, {
@@ -129,9 +131,7 @@ const CalendarInputHeader = ({ className })=>{
129
131
  }) : /*#__PURE__*/ jsx(SingleDateInputInner, {
130
132
  api: api,
131
133
  readonly: readonly,
132
- showTime: showTime,
133
- timeRef: timeRef,
134
- commitValue: commitValue
134
+ showTime: showTime
135
135
  })
136
136
  })
137
137
  });
@@ -0,0 +1,34 @@
1
+ import { type FC, type ReactNode, type Ref } from 'react';
2
+ import { Slider as ArkSlider } from '@ark-ui/react/slider';
3
+ import { type TestableProps } from '../../utils/testId';
4
+ /** Ark/Zag machine-config props forwarded to the headless `Slider.Root`. */
5
+ type SliderRootConfig = Pick<ArkSlider.RootProps, 'value' | 'defaultValue' | 'min' | 'max' | 'step' | 'disabled' | 'readOnly' | 'getAriaValueText' | 'name' | 'form' | 'minStepsBetweenThumbs' | 'thumbCollisionBehavior'>;
6
+ export interface SliderProps extends SliderRootConfig, TestableProps {
7
+ /** Error state → maps to Ark `invalid` (repaints the handle; the message is the Field's job). */
8
+ error?: boolean;
9
+ className?: string;
10
+ children?: ReactNode;
11
+ /** Fires live during drag with the current value array (`[n]` single, `[low, high]` range). */
12
+ onValueChange?: (value: number[]) => void;
13
+ /** Fires once on drag release with the committed value array. */
14
+ onValueChangeEnd?: (value: number[]) => void;
15
+ /** Ref to the Ark Slider root element. */
16
+ ref?: Ref<HTMLDivElement>;
17
+ }
18
+ /**
19
+ * Slider — pick an approximate, bounded value (or range) by dragging a handle along
20
+ * a track. Built on `@ark-ui/react/slider`.
21
+ *
22
+ * Compose the parts: `SliderControl` (renders the track + range) holds one
23
+ * `SliderThumb` per thumb (the real `role="slider"` node — consumer `data-*` /
24
+ * analytics / `ref` land there) and an optional `SliderMarks`. `SliderInput` and
25
+ * `SliderValue` are optional readouts. Single vs range is driven by the length of
26
+ * `value` / `defaultValue` — render one `SliderThumb` per entry. Thumbs never cross
27
+ * (`thumbCollisionBehavior` defaults to `'none'`); set `minStepsBetweenThumbs` for a gap.
28
+ *
29
+ * It reads `Field` context like `Input`/`Textarea`, so wrapping it in `<Field>` wires
30
+ * label / invalid / disabled. When an exact figure matters, pair with `NumberInput`;
31
+ * for 2–7 fixed options use `SegmentedControl`. See `docs/slider-design-spec.md`.
32
+ */
33
+ export declare const Slider: FC<SliderProps>;
34
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,123 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import code_connect from "@figma/code-connect";
3
+ import { Field, FieldLabel } from "../Field/index.js";
4
+ import { Slider } from "./Slider.js";
5
+ import { SliderControl } from "./SliderControl.js";
6
+ import { SliderInput } from "./SliderInput.js";
7
+ import { SliderMarks } from "./SliderMarks.js";
8
+ import { SliderThumb } from "./SliderThumb.js";
9
+ const SLIDER_URL = 'https://www.figma.com/design/VKb5gW46uSGw0rqrhZsbXT/WADS-Components?node-id=11354-181';
10
+ const SLIDER_INPUT_URL = 'https://www.figma.com/design/VKb5gW46uSGw0rqrhZsbXT/WADS-Components?node-id=11358-1807';
11
+ code_connect.connect(Slider, SLIDER_URL, {
12
+ example: ()=>/*#__PURE__*/ jsx(Slider, {
13
+ "aria-label": "Value",
14
+ defaultValue: [
15
+ 50
16
+ ],
17
+ children: /*#__PURE__*/ jsx(SliderControl, {
18
+ children: /*#__PURE__*/ jsx(SliderThumb, {
19
+ "aria-label": "Value"
20
+ })
21
+ })
22
+ })
23
+ });
24
+ code_connect.connect(Slider, SLIDER_URL, {
25
+ variant: {
26
+ Double: 'On'
27
+ },
28
+ example: ()=>/*#__PURE__*/ jsx(Slider, {
29
+ defaultValue: [
30
+ 20,
31
+ 80
32
+ ],
33
+ children: /*#__PURE__*/ jsxs(SliderControl, {
34
+ children: [
35
+ /*#__PURE__*/ jsx(SliderThumb, {
36
+ index: 0
37
+ }),
38
+ /*#__PURE__*/ jsx(SliderThumb, {
39
+ index: 1
40
+ })
41
+ ]
42
+ })
43
+ })
44
+ });
45
+ code_connect.connect(Slider, SLIDER_URL, {
46
+ variant: {
47
+ Ticks: 'On'
48
+ },
49
+ example: ()=>/*#__PURE__*/ jsx(Slider, {
50
+ "aria-label": "Value",
51
+ step: 25,
52
+ defaultValue: [
53
+ 50
54
+ ],
55
+ children: /*#__PURE__*/ jsxs(SliderControl, {
56
+ children: [
57
+ /*#__PURE__*/ jsx(SliderThumb, {
58
+ "aria-label": "Value"
59
+ }),
60
+ /*#__PURE__*/ jsx(SliderMarks, {
61
+ marks: [
62
+ {
63
+ value: 0,
64
+ label: '0'
65
+ },
66
+ {
67
+ value: 25,
68
+ label: '25'
69
+ },
70
+ {
71
+ value: 50,
72
+ label: '50'
73
+ },
74
+ {
75
+ value: 75,
76
+ label: '75'
77
+ },
78
+ {
79
+ value: 100,
80
+ label: '100'
81
+ }
82
+ ]
83
+ })
84
+ ]
85
+ })
86
+ })
87
+ });
88
+ code_connect.connect(Slider, SLIDER_URL, {
89
+ variant: {
90
+ Input: 'On'
91
+ },
92
+ example: ()=>/*#__PURE__*/ jsxs(Slider, {
93
+ "aria-label": "Value",
94
+ defaultValue: [
95
+ 50
96
+ ],
97
+ children: [
98
+ /*#__PURE__*/ jsx(SliderControl, {
99
+ children: /*#__PURE__*/ jsx(SliderThumb, {
100
+ "aria-label": "Value"
101
+ })
102
+ }),
103
+ /*#__PURE__*/ jsx(SliderInput, {})
104
+ ]
105
+ })
106
+ });
107
+ code_connect.connect(Slider, SLIDER_INPUT_URL, {
108
+ example: ()=>/*#__PURE__*/ jsxs(Field, {
109
+ children: [
110
+ /*#__PURE__*/ jsx(FieldLabel, {
111
+ children: "Risk threshold"
112
+ }),
113
+ /*#__PURE__*/ jsx(Slider, {
114
+ defaultValue: [
115
+ 50
116
+ ],
117
+ children: /*#__PURE__*/ jsx(SliderControl, {
118
+ children: /*#__PURE__*/ jsx(SliderThumb, {})
119
+ })
120
+ })
121
+ ]
122
+ })
123
+ });
@@ -0,0 +1,85 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useCallback, useMemo, useState } from "react";
3
+ import { useFieldContext } from "@ark-ui/react/field";
4
+ import { Slider } from "@ark-ui/react/slider";
5
+ import { cn } from "../../utils/cn.js";
6
+ import { TestIdProvider } from "../../utils/testId.js";
7
+ import { sliderVariants } from "./classes.js";
8
+ import { SliderRootContextProvider } from "./SliderContext.js";
9
+ const sameMarks = (a, b)=>a.length === b.length && a.every((mark, i)=>mark.value === b[i]?.value && mark.label === b[i]?.label);
10
+ const Slider_Slider = ({ value, defaultValue = [
11
+ 50
12
+ ], min, max, step, disabled, readOnly, getAriaValueText, name, form, minStepsBetweenThumbs, thumbCollisionBehavior, error = false, className, children, onValueChange, onValueChangeEnd, ref, 'data-testid': testId })=>{
13
+ const field = useFieldContext();
14
+ const invalid = error || Boolean(field?.invalid);
15
+ const isDisabled = disabled ?? field?.disabled ?? false;
16
+ const isReadOnly = readOnly ?? field?.readOnly ?? false;
17
+ const thumbCount = (value ?? defaultValue).length;
18
+ const isRange = thumbCount > 1;
19
+ const [marks, setMarks] = useState([]);
20
+ const registerMarks = useCallback((next)=>{
21
+ setMarks((prev)=>sameMarks(prev, next) ? prev : next);
22
+ }, []);
23
+ const resolvedGetAriaValueText = useMemo(()=>getAriaValueText ?? (({ value: v })=>marks.find((mark)=>mark.value === v)?.label ?? String(v)), [
24
+ getAriaValueText,
25
+ marks
26
+ ]);
27
+ const fieldLabelId = field && !isRange ? field.ids.label : void 0;
28
+ const handleValueChange = useCallback((details)=>onValueChange?.(details.value), [
29
+ onValueChange
30
+ ]);
31
+ const handleValueChangeEnd = useCallback((details)=>onValueChangeEnd?.(details.value), [
32
+ onValueChangeEnd
33
+ ]);
34
+ const contextValue = useMemo(()=>({
35
+ isRange,
36
+ thumbCount,
37
+ invalid,
38
+ disabled: isDisabled,
39
+ readOnly: isReadOnly,
40
+ ariaDescribedby: field?.ariaDescribedby,
41
+ fieldLabelId,
42
+ marks,
43
+ registerMarks
44
+ }), [
45
+ isRange,
46
+ thumbCount,
47
+ invalid,
48
+ isDisabled,
49
+ isReadOnly,
50
+ field?.ariaDescribedby,
51
+ fieldLabelId,
52
+ marks,
53
+ registerMarks
54
+ ]);
55
+ return /*#__PURE__*/ jsx(Slider.Root, {
56
+ ref: ref,
57
+ value: value,
58
+ defaultValue: defaultValue,
59
+ min: min,
60
+ max: max,
61
+ step: step,
62
+ disabled: isDisabled,
63
+ readOnly: isReadOnly,
64
+ invalid: invalid,
65
+ getAriaValueText: resolvedGetAriaValueText,
66
+ name: name,
67
+ form: form,
68
+ minStepsBetweenThumbs: minStepsBetweenThumbs,
69
+ thumbCollisionBehavior: thumbCollisionBehavior,
70
+ onValueChange: onValueChange ? handleValueChange : void 0,
71
+ onValueChangeEnd: onValueChangeEnd ? handleValueChangeEnd : void 0,
72
+ "data-slot": "slider",
73
+ "data-testid": testId,
74
+ className: cn(sliderVariants(), className),
75
+ children: /*#__PURE__*/ jsx(SliderRootContextProvider, {
76
+ value: contextValue,
77
+ children: /*#__PURE__*/ jsx(TestIdProvider, {
78
+ value: testId,
79
+ children: children
80
+ })
81
+ })
82
+ });
83
+ };
84
+ Slider_Slider.displayName = 'Slider';
85
+ export { Slider_Slider as Slider };
@@ -0,0 +1,31 @@
1
+ import type { SliderMark } from './types';
2
+ export interface SliderRootContextValue {
3
+ /** True when the value has 2+ entries (range) — drives slot-naming + label defaults. */
4
+ isRange: boolean;
5
+ /** Expected thumb count (`value`/`defaultValue` length) — for the dev thumb-count guard. */
6
+ thumbCount: number;
7
+ /** Resolved invalid state (own `error` || Field `invalid`) — for sub-part error styling. */
8
+ invalid: boolean;
9
+ /** Resolved disabled state (own `disabled` ?? Field `disabled`). */
10
+ disabled: boolean;
11
+ /**
12
+ * Resolved read-only state (own `readOnly` ?? Field `readOnly`). Ark's thumb guards
13
+ * drag/keyboard on this, but its `setThumbValue` does NOT — so parts that write
14
+ * imperatively (`SliderInput`, `SliderMarks` click-to-jump) must honour it themselves.
15
+ */
16
+ readOnly: boolean;
17
+ /** Field help/error id to announce on the thumb (`aria-describedby`). */
18
+ ariaDescribedby?: string;
19
+ /** Field label id — fallback accessible name for a single thumb with no explicit label. */
20
+ fieldLabelId?: string;
21
+ /**
22
+ * Marks published by `<SliderMarks>`, mirrored into root state so `getAriaValueText`
23
+ * (and `SliderValue` / the thumb tooltip) resolve ordinal labels. Empty when there
24
+ * are no marks.
25
+ */
26
+ marks: SliderMark[];
27
+ /** Called by `<SliderMarks>` to publish its marks to the root (no-op if unchanged). */
28
+ registerMarks: (marks: SliderMark[]) => void;
29
+ }
30
+ export declare const SliderRootContextProvider: import("react").Provider<SliderRootContextValue | null>;
31
+ export declare const useSliderRootContext: () => SliderRootContextValue;
@@ -0,0 +1,9 @@
1
+ import { createContext, useContext } from "react";
2
+ const SliderRootContext = /*#__PURE__*/ createContext(null);
3
+ const SliderRootContextProvider = SliderRootContext.Provider;
4
+ const useSliderRootContext = ()=>{
5
+ const context = useContext(SliderRootContext);
6
+ if (!context) throw new Error('Slider sub-components must be rendered inside <Slider>.');
7
+ return context;
8
+ };
9
+ export { SliderRootContextProvider, useSliderRootContext };
@@ -0,0 +1,9 @@
1
+ import { type FC, type ReactNode, type Ref } from 'react';
2
+ export interface SliderControlProps {
3
+ children?: ReactNode;
4
+ className?: string;
5
+ /** Ref to the Ark Control element (the track region). */
6
+ ref?: Ref<HTMLDivElement>;
7
+ }
8
+ /** The interactive track region — renders the track + range, then the thumb(s) / marks. */
9
+ export declare const SliderControl: FC<SliderControlProps>;
@@ -0,0 +1,35 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Children, isValidElement } from "react";
3
+ import { Slider } from "@ark-ui/react/slider";
4
+ import { cn } from "../../utils/cn.js";
5
+ import { useTestId } from "../../utils/testId.js";
6
+ import { sliderControlClassNames, sliderRangeClassNames, sliderTrackClassNames } from "./classes.js";
7
+ import { useSliderRootContext } from "./SliderContext.js";
8
+ import { SliderThumb } from "./SliderThumb.js";
9
+ const SliderControl = ({ children, className, ref })=>{
10
+ const { thumbCount } = useSliderRootContext();
11
+ const testId = useTestId('control');
12
+ if ('production' !== process.env.NODE_ENV) {
13
+ const renderedThumbs = Children.toArray(children).filter((child)=>/*#__PURE__*/ isValidElement(child) && child.type === SliderThumb).length;
14
+ if (renderedThumbs > 0 && renderedThumbs !== thumbCount) console.warn(`[Slider] Rendered ${renderedThumbs} <SliderThumb> but the value has ${thumbCount} entr${1 === thumbCount ? 'y' : 'ies'}. Render exactly one <SliderThumb index> per value entry.`);
15
+ }
16
+ return /*#__PURE__*/ jsxs(Slider.Control, {
17
+ ref: ref,
18
+ "data-slot": "slider-control",
19
+ "data-testid": testId,
20
+ className: cn(sliderControlClassNames, className),
21
+ children: [
22
+ /*#__PURE__*/ jsx(Slider.Track, {
23
+ "data-slot": "slider-track",
24
+ className: sliderTrackClassNames,
25
+ children: /*#__PURE__*/ jsx(Slider.Range, {
26
+ "data-slot": "slider-range",
27
+ className: sliderRangeClassNames
28
+ })
29
+ }),
30
+ children
31
+ ]
32
+ });
33
+ };
34
+ SliderControl.displayName = 'SliderControl';
35
+ export { SliderControl };
@@ -0,0 +1,26 @@
1
+ import { type FC, type InputHTMLAttributes, type Ref } from 'react';
2
+ export interface SliderInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'defaultValue'> {
3
+ /** Which thumb this box is bound to (0 = single/low, 1 = high). */
4
+ index?: number;
5
+ /** Ref to the real `<input>` node (analytics / focus management). */
6
+ ref?: Ref<HTMLInputElement>;
7
+ }
8
+ /**
9
+ * Inline numbers-only `Input` two-way bound to the thumb at `index`: a plain box (no
10
+ * stepper, fixed width — the DS `Input`, matching Figma `_text-box`). The slider machine
11
+ * owns clamp/snap/no-cross, so the box needs no bounds of its own.
12
+ *
13
+ * Editing uses a local draft buffer: while focused, the box shows exactly what the user
14
+ * typed (so an empty field, a leading `-`, a trailing `.`, or a value that would snap/clamp
15
+ * survive the keystroke) and commits to `setThumbValue` on blur / Enter — then drops the
16
+ * draft so the box re-syncs to the machine's clamped + snapped value. Binding `value`
17
+ * straight to the snapped machine value instead would fight every intermediate keystroke.
18
+ *
19
+ * It is the real interactive node, so arbitrary consumer `data-*` / `aria-*` / `id` / `ref`
20
+ * (canonically `data-analytics-id` / `data-analytics-props`) forward straight to the
21
+ * `<input>` (see `docs/metrics/contract.md`).
22
+ *
23
+ * Explicit `useId` default so two range boxes never collide on the wrapping field's control
24
+ * id (and `FieldLabel`'s `htmlFor` keeps naming the thumb, not a box); a consumer `id` wins.
25
+ */
26
+ export declare const SliderInput: FC<SliderInputProps>;
@@ -0,0 +1,69 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useCallback, useId, useState } from "react";
3
+ import { useSliderContext } from "@ark-ui/react/slider";
4
+ import { cn } from "../../utils/cn.js";
5
+ import { useTestId } from "../../utils/testId.js";
6
+ import { Input } from "../Input/index.js";
7
+ import { sliderInputBoxClassNames } from "./classes.js";
8
+ import { useSliderRootContext } from "./SliderContext.js";
9
+ const SliderInput = ({ index = 0, 'aria-label': ariaLabel, id: idProp, className, ref, onChange: onChangeProp, onBlur: onBlurProp, onKeyDown: onKeyDownProp, ...rest })=>{
10
+ const { isRange, disabled, invalid, readOnly } = useSliderRootContext();
11
+ const api = useSliderContext();
12
+ const autoId = useId();
13
+ const id = idProp ?? autoId;
14
+ const testId = useTestId(isRange ? `input-${index}` : 'input');
15
+ const defaultLabel = isRange ? 0 === index ? 'Minimum value' : 'Maximum value' : 'Value';
16
+ const machineText = String(api.value[index] ?? '');
17
+ const [draft, setDraft] = useState(null);
18
+ const value = draft ?? machineText;
19
+ const commit = useCallback(()=>{
20
+ if (null === draft) return;
21
+ const next = Number(draft);
22
+ if ('' !== draft && !Number.isNaN(next)) api.setThumbValue(index, next);
23
+ setDraft(null);
24
+ }, [
25
+ api,
26
+ draft,
27
+ index
28
+ ]);
29
+ const handleChange = useCallback((event)=>{
30
+ onChangeProp?.(event);
31
+ if (readOnly) return;
32
+ setDraft(event.target.value);
33
+ }, [
34
+ onChangeProp,
35
+ readOnly
36
+ ]);
37
+ const handleBlur = useCallback((event)=>{
38
+ onBlurProp?.(event);
39
+ commit();
40
+ }, [
41
+ onBlurProp,
42
+ commit
43
+ ]);
44
+ const handleKeyDown = useCallback((event)=>{
45
+ onKeyDownProp?.(event);
46
+ if ('Enter' === event.key) commit();
47
+ }, [
48
+ onKeyDownProp,
49
+ commit
50
+ ]);
51
+ return /*#__PURE__*/ jsx(Input, {
52
+ ...rest,
53
+ ref: ref,
54
+ inputMode: "numeric",
55
+ id: id,
56
+ value: value,
57
+ disabled: disabled,
58
+ readOnly: readOnly,
59
+ error: invalid,
60
+ "aria-label": ariaLabel ?? defaultLabel,
61
+ "data-testid": testId,
62
+ className: cn(sliderInputBoxClassNames, className),
63
+ onChange: handleChange,
64
+ onBlur: handleBlur,
65
+ onKeyDown: handleKeyDown
66
+ });
67
+ };
68
+ SliderInput.displayName = 'SliderInput';
69
+ export { SliderInput };