@tremolo-ui/react 0.5.0 → 0.6.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 (76) hide show
  1. package/dist/compose-refs.cjs +72 -0
  2. package/dist/compose-refs.cjs.map +1 -0
  3. package/dist/compose-refs.d.cts +43 -0
  4. package/dist/compose-refs.d.cts.map +1 -0
  5. package/dist/compose-refs.d.ts +43 -0
  6. package/dist/compose-refs.d.ts.map +1 -0
  7. package/dist/compose-refs.js +70 -0
  8. package/dist/compose-refs.js.map +1 -0
  9. package/dist/index.cjs +1468 -534
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +1103 -435
  12. package/dist/index.d.cts.map +1 -1
  13. package/dist/index.d.ts +1103 -435
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +1457 -534
  16. package/dist/index.js.map +1 -1
  17. package/package.json +28 -26
  18. package/src/components/AnimationCanvas/index.tsx +115 -76
  19. package/src/components/Knob/ActiveLine.tsx +7 -6
  20. package/src/components/Knob/InactiveLine.tsx +9 -10
  21. package/src/components/Knob/SVGRoot.tsx +5 -3
  22. package/src/components/Knob/Thumb.tsx +33 -11
  23. package/src/components/Knob/context.tsx +17 -1
  24. package/src/components/Knob/index.tsx +148 -55
  25. package/src/components/NumberInput/DecrementStepper.tsx +8 -4
  26. package/src/components/NumberInput/IncrementStepper.tsx +9 -4
  27. package/src/components/NumberInput/InputField.tsx +188 -105
  28. package/src/components/NumberInput/Stepper.tsx +123 -72
  29. package/src/components/NumberInput/{stepperButton.tsx → StepperButton.tsx} +27 -22
  30. package/src/components/NumberInput/context.tsx +35 -8
  31. package/src/components/NumberInput/index.tsx +272 -75
  32. package/src/components/Piano/index.tsx +354 -188
  33. package/src/components/PointsEditor/Background.tsx +11 -2
  34. package/src/components/PointsEditor/Container.tsx +87 -27
  35. package/src/components/PointsEditor/Point.tsx +239 -79
  36. package/src/components/PointsEditor/SelectionBox.tsx +54 -0
  37. package/src/components/PointsEditor/context.tsx +78 -10
  38. package/src/components/PointsEditor/index.tsx +440 -39
  39. package/src/components/Slider/Marks.tsx +48 -16
  40. package/src/components/Slider/MarksOption.tsx +57 -43
  41. package/src/components/Slider/Thumb.tsx +108 -50
  42. package/src/components/Slider/Track.tsx +69 -58
  43. package/src/components/Slider/context.tsx +4 -2
  44. package/src/components/Slider/index.tsx +131 -65
  45. package/src/components/Slider/type.ts +26 -8
  46. package/src/components/XYPad/Area.tsx +45 -35
  47. package/src/components/XYPad/Thumb.tsx +135 -62
  48. package/src/components/XYPad/context.tsx +4 -1
  49. package/src/components/XYPad/index.tsx +180 -48
  50. package/src/components/_util/Placement.tsx +68 -0
  51. package/src/components/_util/VisuallyHiddenRangeInput.tsx +24 -0
  52. package/src/components/_util/css-length.ts +13 -0
  53. package/src/{components/_util/composeRefs.tsx → compose-refs/index.tsx} +33 -5
  54. package/src/hooks/_internal/useCallbackRef.ts +3 -1
  55. package/src/hooks/_internal/useCheckSteps.ts +170 -0
  56. package/src/hooks/useAnimationFrame.ts +17 -7
  57. package/src/hooks/useDrag.ts +38 -8
  58. package/src/hooks/useDragValue.ts +54 -9
  59. package/src/hooks/useEventListener.ts +10 -6
  60. package/src/hooks/useLongPress.ts +42 -8
  61. package/src/hooks/useMIDIAccess.ts +82 -11
  62. package/src/hooks/useMIDIInput.ts +45 -13
  63. package/src/hooks/useMIDIMessage.ts +23 -5
  64. package/src/index.ts +38 -21
  65. package/src/input-event.ts +32 -0
  66. package/dist/index.css +0 -501
  67. package/src/Globals.d.ts +0 -3
  68. package/src/components/Knob/index.css +0 -81
  69. package/src/components/NumberInput/index.css +0 -121
  70. package/src/components/Piano/index.css +0 -90
  71. package/src/components/PointsEditor/index.css +0 -35
  72. package/src/components/Slider/index.css +0 -125
  73. package/src/components/XYPad/index.css +0 -60
  74. package/src/components/_util/index.ts +0 -28
  75. package/src/styles/global.css +0 -19
  76. /package/src/components/Piano/{keyboardShortcuts.ts → keyboard-shortcuts.ts} +0 -0
@@ -1,26 +1,45 @@
1
- import clsx from 'clsx'
2
- import { ComponentPropsWithoutRef, ReactNode } from 'react'
1
+ import { ComponentPropsWithoutRef, CSSProperties, ReactNode } from 'react'
3
2
 
4
3
  import { xor } from '@tremolo-ui/functions'
5
4
 
5
+ import { cssLength } from '../_util/css-length'
6
+ import { Placement } from '../_util/Placement'
7
+
6
8
  import { useSliderContext } from './context'
7
9
  import { MarksOption } from './MarksOption'
8
10
  import { generateOptionsList, MarksOptions } from './type'
9
11
 
10
- export interface MarksProps {
12
+ export interface SliderMarksProps {
13
+ /**
14
+ * Space between the marks and the track. Sets `--gap`; the theme's own
15
+ * spacing stands when this is omitted.
16
+ */
11
17
  gap?: number | string
18
+ /**
19
+ * Build the marks instead of writing `Slider.MarksOption` out: a number puts
20
+ * one every that many, `'step'` one every `step`, and `{ per, mark, label }`
21
+ * also turns the mark or the label off for all of them. `children` is
22
+ * ignored while it is set.
23
+ *
24
+ * `'step'` makes one per step, which is a great many for a fine `step`.
25
+ */
12
26
  options?: MarksOptions
27
+ /**
28
+ * `<Slider.MarksOption />` goes here, one per mark. Ignored while `options`
29
+ * is set.
30
+ */
13
31
  children?: ReactNode
14
32
  }
15
33
 
16
34
  export function Marks({
17
- gap = 6,
35
+ gap,
18
36
  options,
19
37
  children,
20
38
  className,
21
39
  style,
22
40
  ...props
23
- }: MarksProps & Omit<ComponentPropsWithoutRef<'div'>, keyof MarksProps>) {
41
+ }: SliderMarksProps &
42
+ Omit<ComponentPropsWithoutRef<'div'>, keyof SliderMarksProps>) {
24
43
  const min = useSliderContext((s) => s.min)
25
44
  const max = useSliderContext((s) => s.max)
26
45
  const step = useSliderContext((s) => s.step)
@@ -34,19 +53,32 @@ export function Marks({
34
53
 
35
54
  return (
36
55
  <div
37
- className={clsx('tremolo-slider-marks', className)}
38
- style={{
39
- marginLeft: vertical ? gap : undefined,
40
- marginTop: !vertical ? gap : undefined,
41
- ...style,
42
- }}
56
+ className={className}
57
+ data-orientation={vertical ? 'vertical' : 'horizontal'}
58
+ style={
59
+ {
60
+ '--gap': cssLength(gap),
61
+ // Each option inside is placed against this box.
62
+ position: 'relative',
63
+ ...style,
64
+ } as CSSProperties
65
+ }
43
66
  {...props}
44
67
  >
45
- {options
46
- ? optionsList.map(({ value, type }, index) => {
47
- return <MarksOption key={index} value={value} type={type} />
48
- })
49
- : children}
68
+ <Placement name="Slider.Marks">
69
+ {options
70
+ ? optionsList.map(({ value, mark, label }, index) => {
71
+ return (
72
+ <MarksOption
73
+ key={index}
74
+ value={value}
75
+ mark={mark}
76
+ label={label ? undefined : null}
77
+ />
78
+ )
79
+ })
80
+ : children}
81
+ </Placement>
50
82
  </div>
51
83
  )
52
84
  }
@@ -1,30 +1,40 @@
1
- import clsx from 'clsx'
2
1
  import { ComponentPropsWithoutRef, CSSProperties, useCallback } from 'react'
3
2
 
4
3
  import { toFixed, xor } from '@tremolo-ui/functions'
5
4
 
5
+ import { cssLength } from '../_util/css-length'
6
+ import { useCheckPlacement } from '../_util/Placement'
7
+
6
8
  import { useSliderContext } from './context'
7
- import { MarksType } from './type'
8
9
 
9
- export interface MarksOptionProps {
10
- // required
10
+ export interface SliderMarksOptionProps {
11
+ /** Where the mark sits, on the same scale as the thumb. */
11
12
  value: number
12
13
 
13
- // optional
14
- type?: MarksType
15
- /** Display text instead of value. */
16
- label?: string
17
- /** mark thickness */
14
+ /**
15
+ * Draw the mark itself. Turn it off to show the label alone.
16
+ * @default true
17
+ */
18
+ mark?: boolean
19
+ /**
20
+ * Text shown next to the mark. Leave it out to show the value; `null` leaves
21
+ * the label out, and an empty string draws an empty one.
22
+ */
23
+ label?: number | string | null
24
+ /** Thickness of the mark. Sets `--thickness`. */
18
25
  thickness?: number | string
19
- /** mark length */
26
+ /** Length of the mark. Sets `--length`. */
20
27
  length?: number | string
21
- /** Gap between mark and label. */
28
+ /** Space between the mark and the label. Sets `--gap`. */
22
29
  gap?: number | string
30
+ /** Width of the label. Sets `--label-width`. */
23
31
  labelWidth?: number | string
32
+ /** Classes for the mark and the label inside the option. */
24
33
  classes?: {
25
34
  mark?: string
26
35
  label?: string
27
36
  }
37
+ /** Styles for the mark and the label inside the option. */
28
38
  styles?: {
29
39
  mark?: CSSProperties
30
40
  label?: CSSProperties
@@ -33,19 +43,21 @@ export interface MarksOptionProps {
33
43
 
34
44
  export function MarksOption({
35
45
  value,
36
- type = 'mark-number',
46
+ mark = true,
37
47
  label,
38
- thickness = 1,
39
- length = '0.5rem',
40
- gap = 2,
48
+ thickness,
49
+ length,
50
+ gap,
41
51
  labelWidth,
42
52
  classes,
43
53
  styles,
44
54
  className,
45
55
  style,
46
56
  ...props
47
- }: MarksOptionProps &
48
- Omit<ComponentPropsWithoutRef<'div'>, keyof MarksOptionProps>) {
57
+ }: SliderMarksOptionProps &
58
+ Omit<ComponentPropsWithoutRef<'div'>, keyof SliderMarksOptionProps>) {
59
+ useCheckPlacement('Slider.MarksOption', 'Slider.Marks')
60
+
49
61
  const min = useSliderContext((s) => s.min)
50
62
  const max = useSliderContext((s) => s.max)
51
63
  const scale = useSliderContext((s) => s.scale)
@@ -63,37 +75,39 @@ export function MarksOption({
63
75
 
64
76
  return (
65
77
  <div
66
- className={clsx('tremolo-slider-marks-option', className)}
67
- style={{
68
- left: !vertical ? `${calcPercent(value)}%` : undefined,
69
- top: vertical ? `${calcPercent(value)}%` : undefined,
70
- ...style,
71
- }}
72
- data-vertical={vertical}
78
+ className={className}
79
+ style={
80
+ {
81
+ // The mark and the label read these, so they are set once here.
82
+ '--thickness': cssLength(thickness),
83
+ '--length': cssLength(length),
84
+ '--gap': cssLength(gap),
85
+ '--label-width': cssLength(labelWidth),
86
+ // The mechanics of the position below, as on the thumb.
87
+ position: 'absolute',
88
+ translate: vertical
89
+ ? 'var(--translate, 0 -50%)'
90
+ : 'var(--translate, -50% 0)',
91
+ zIndex: 10,
92
+ ...style,
93
+ // Where the mark belongs on the track: the value, not a style.
94
+ left: !vertical ? `${calcPercent(value)}%` : undefined,
95
+ top: vertical ? `${calcPercent(value)}%` : undefined,
96
+ } as CSSProperties
97
+ }
98
+ data-orientation={vertical ? 'vertical' : 'horizontal'}
73
99
  {...props}
74
100
  >
75
- {type != 'number' && (
101
+ {mark && (
76
102
  <div
77
- className={clsx('tremolo-slider-marks-option-mark', classes?.mark)}
78
- style={{
79
- width: !vertical ? thickness : length,
80
- height: vertical ? thickness : length,
81
- marginBottom: !vertical ? gap : undefined,
82
- marginRight: vertical ? gap : undefined,
83
- ...styles?.mark,
84
- }}
85
- data-vertical={vertical}
103
+ className={classes?.mark}
104
+ style={styles?.mark}
105
+ data-orientation={vertical ? 'vertical' : 'horizontal'}
86
106
  ></div>
87
107
  )}
88
- {type != 'mark' && (
89
- <div
90
- className={clsx('tremolo-slider-marks-option-label', classes?.label)}
91
- style={{
92
- width: labelWidth,
93
- ...styles?.label,
94
- }}
95
- >
96
- {label || value}
108
+ {label !== null && (
109
+ <div className={classes?.label} style={styles?.label}>
110
+ {label ?? value}
97
111
  </div>
98
112
  )}
99
113
  </div>
@@ -1,25 +1,38 @@
1
- import clsx from 'clsx'
2
1
  import {
2
+ AriaAttributes,
3
+ ComponentPropsWithoutRef,
3
4
  CSSProperties,
5
+ forwardRef,
4
6
  ReactNode,
5
- Ref,
6
7
  useImperativeHandle,
7
8
  useRef,
8
9
  } from 'react'
9
10
 
11
+ import { useCheckPlacement } from '../_util/Placement'
12
+ import { VisuallyHiddenRangeInput } from '../_util/VisuallyHiddenRangeInput'
13
+
10
14
  import { useSliderContext } from './context'
11
15
 
12
16
  export interface SliderThumbProps {
13
17
  /**
14
- * Size comes from the `--thumb-size` CSS variable on `Slider.Root`, so that
15
- * the root can reserve the matching amount of space around the track.
18
+ * Sets `--color`, for the theme to colour the thumb with. The size is not
19
+ * set here but by `--thumb-size` on `Slider.Root`, so that the root can
20
+ * leave the matching room around the track.
16
21
  */
17
22
  color?: string
18
23
 
19
24
  className?: string
20
25
  style?: CSSProperties
26
+ /**
27
+ * Rendered inside the thumb. The thumb is one element either way, so what
28
+ * is passed here is decoration on top of it rather than a replacement for
29
+ * it — `className` and `style` are how its own appearance is changed.
30
+ */
21
31
  children?: ReactNode
22
- ref?: Ref<SliderThumbMethods>
32
+ 'aria-label'?: AriaAttributes['aria-label']
33
+ 'aria-labelledby'?: AriaAttributes['aria-labelledby']
34
+ 'aria-describedby'?: AriaAttributes['aria-describedby']
35
+ 'aria-valuetext'?: AriaAttributes['aria-valuetext']
23
36
  }
24
37
 
25
38
  export interface SliderThumbMethods {
@@ -27,55 +40,100 @@ export interface SliderThumbMethods {
27
40
  blur: () => void
28
41
  }
29
42
 
30
- export function Thumb({
31
- color,
32
- children,
33
- className,
34
- style,
35
- ref,
36
- }: SliderThumbProps) {
37
- const elementRef = useRef<HTMLDivElement>(null)
38
- const { vertical, disabled, readonly, percent, thumbRef } = useSliderContext()
43
+ type Props = SliderThumbProps &
44
+ Omit<ComponentPropsWithoutRef<'div'>, keyof SliderThumbProps>
39
45
 
40
- const methods = () => ({
41
- focus() {
42
- elementRef.current?.focus()
46
+ export const Thumb = /* @__PURE__ */ forwardRef<SliderThumbMethods, Props>(
47
+ function Thumb(
48
+ {
49
+ color,
50
+ children,
51
+ className,
52
+ style,
53
+ 'aria-label': ariaLabel,
54
+ 'aria-labelledby': ariaLabelledby,
55
+ 'aria-describedby': ariaDescribedby,
56
+ 'aria-valuetext': ariaValuetext,
57
+ ...props
43
58
  },
44
- blur() {
45
- elementRef.current?.blur()
46
- },
47
- })
59
+ forwardedRef,
60
+ ) {
61
+ const inputRef = useRef<HTMLInputElement>(null)
62
+ const {
63
+ value,
64
+ min,
65
+ max,
66
+ step,
67
+ vertical,
68
+ disabled,
69
+ readonly,
70
+ onChange,
71
+ percent,
72
+ thumbRef,
73
+ } = useSliderContext()
74
+
75
+ // The thumb is positioned against the track.
76
+ useCheckPlacement('Slider.Thumb', 'Slider.Track')
48
77
 
49
- useImperativeHandle(ref, methods, [])
50
- // Root focuses the thumb when a drag starts, wherever the user placed it.
78
+ const methods = () => ({
79
+ focus() {
80
+ if (!disabled) inputRef.current?.focus()
81
+ },
82
+ blur() {
83
+ inputRef.current?.blur()
84
+ },
85
+ })
51
86
 
52
- useImperativeHandle(thumbRef, methods, [])
87
+ useImperativeHandle(forwardedRef, methods, [disabled])
88
+ // Root focuses the thumb when a drag starts, wherever the user placed it.
89
+ useImperativeHandle(thumbRef, methods, [disabled])
53
90
 
54
- return (
55
- <div
56
- className="tremolo-slider-thumb-wrapper"
57
- style={{
58
- top: vertical ? `${percent}%` : '50%',
59
- left: !vertical ? `${percent}%` : '50%',
60
- }}
61
- >
62
- {children ? (
63
- children
64
- ) : (
65
- // default slider thumb
66
- <div
67
- ref={elementRef}
68
- className={clsx('tremolo-slider-thumb', className)}
69
- // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
70
- tabIndex={0}
71
- aria-disabled={disabled}
91
+ return (
92
+ <div
93
+ className={className}
94
+ data-disabled={disabled ? '' : undefined}
95
+ data-readonly={readonly ? '' : undefined}
96
+ {...props}
97
+ style={{
98
+ ...{ '--color': color },
99
+ // Not a style but the mechanics of the position below: the thumb is
100
+ // placed by a percentage of the track, which needs it out of flow
101
+ // and measured from its own centre. `--translate` is there for a
102
+ // thumb that should hang off its edge instead.
103
+ position: 'absolute',
104
+ translate: 'var(--translate, -50% -50%)',
105
+ zIndex: 100,
106
+ ...style,
107
+ // Where the thumb sits is the component's decision, not a style: a
108
+ // `left` from the caller would take it off the track, so it is
109
+ // written after theirs.
110
+ top: vertical ? `${percent}%` : '50%',
111
+ left: !vertical ? `${percent}%` : '50%',
112
+ }}
113
+ >
114
+ <VisuallyHiddenRangeInput
115
+ ref={inputRef}
116
+ value={value}
117
+ min={min}
118
+ max={max}
119
+ step={step}
120
+ disabled={disabled}
72
121
  aria-readonly={readonly}
73
- style={{
74
- ...{ '--color': color },
75
- ...style,
122
+ aria-orientation={vertical ? 'vertical' : 'horizontal'}
123
+ aria-label={ariaLabel}
124
+ aria-labelledby={ariaLabelledby}
125
+ aria-describedby={ariaDescribedby}
126
+ aria-valuetext={ariaValuetext}
127
+ onChange={(event) => {
128
+ if (readonly) {
129
+ event.currentTarget.value = String(value)
130
+ return
131
+ }
132
+ onChange?.(event.currentTarget.valueAsNumber)
76
133
  }}
77
- ></div>
78
- )}
79
- </div>
80
- )
81
- }
134
+ />
135
+ {children}
136
+ </div>
137
+ )
138
+ },
139
+ )
@@ -1,79 +1,90 @@
1
- import clsx from 'clsx'
2
- import { ComponentPropsWithoutRef, CSSProperties, ReactNode, Ref } from 'react'
1
+ import {
2
+ ComponentPropsWithoutRef,
3
+ CSSProperties,
4
+ forwardRef,
5
+ ReactNode,
6
+ } from 'react'
3
7
 
4
- import { styleHelper, xor } from '@tremolo-ui/functions'
8
+ import { xor } from '@tremolo-ui/functions'
5
9
 
6
- import { useComposedRefs } from '../_util/composeRefs'
10
+ import { useComposedRefs } from '../../compose-refs'
11
+ import { cssLength } from '../_util/css-length'
12
+ import { Placement } from '../_util/Placement'
7
13
 
8
14
  import { useSliderContext } from './context'
9
15
 
10
- export const defaultLength = 140
11
- export const defaultThickness = 10
12
-
13
16
  export interface SliderTrackProps {
17
+ /**
18
+ * How long the track is along the axis the slider runs. Sets `--length`;
19
+ * the size the theme gives it stands when this is omitted.
20
+ */
14
21
  length?: number | string
22
+ /** How thick the track is across that axis. Sets `--thickness`. */
15
23
  thickness?: number | string
16
24
 
25
+ /** Colour of the part from `min` to the value. Sets `--active`. */
17
26
  active?: string
27
+ /** Colour of the rest of the track. Sets `--inactive`. */
18
28
  inactive?: string
19
29
 
20
- defaultStyle?: boolean
21
-
22
30
  className?: string
23
31
  style?: CSSProperties
24
32
  /** `<Slider.Thumb />` goes here. */
25
33
  children?: ReactNode
26
- ref?: Ref<HTMLDivElement>
27
34
  }
28
35
 
29
- export function Track({
30
- length = defaultLength,
31
- thickness = defaultThickness,
32
- active,
33
- inactive,
34
- children,
35
- className,
36
- style,
37
- defaultStyle = true,
38
- ref,
39
- ...props
40
- }: SliderTrackProps &
41
- Omit<ComponentPropsWithoutRef<'div'>, keyof SliderTrackProps>) {
42
- const { vertical, reverse, disabled, percent, trackRef } = useSliderContext()
36
+ type Props = SliderTrackProps &
37
+ Omit<ComponentPropsWithoutRef<'div'>, keyof SliderTrackProps>
43
38
 
44
- // The track is what the pointer position is normalized against, so the
45
- // context ref is composed with any ref the caller passed.
46
- const composedRef = useComposedRefs<HTMLDivElement>(ref, trackRef)
39
+ export const Track = /* @__PURE__ */ forwardRef<HTMLDivElement, Props>(
40
+ function Track(
41
+ {
42
+ length,
43
+ thickness,
44
+ active,
45
+ inactive,
46
+ children,
47
+ className,
48
+ style,
49
+ ...props
50
+ },
51
+ forwardedRef,
52
+ ) {
53
+ const { vertical, reverse, disabled, percent, trackRef } =
54
+ useSliderContext()
47
55
 
48
- const direction = vertical ? 'bottom' : 'right'
49
- const colors = {
50
- '--active': active,
51
- '--inactive': inactive,
52
- }
56
+ // The track is what the pointer position is normalized against, so the
57
+ // context ref is composed with any ref the caller passed.
58
+ const composedRef = useComposedRefs<HTMLDivElement>(forwardedRef, trackRef)
53
59
 
54
- return (
55
- <div
56
- ref={composedRef}
57
- className={clsx('tremolo-slider-track', className)}
58
- aria-disabled={disabled}
59
- data-vertical={vertical}
60
- style={
61
- !defaultStyle
62
- ? style
63
- : {
64
- ...colors,
65
- background: xor(vertical, reverse)
66
- ? `linear-gradient(to ${direction}, var(--inactive) ${percent}%, var(--active) ${percent}%)`
67
- : `linear-gradient(to ${direction}, var(--active) ${percent}%, var(--inactive) ${percent}%)`,
68
- borderRadius: styleHelper(thickness!, '/', 2),
69
- width: !vertical ? length : thickness,
70
- height: vertical ? length : thickness,
71
- ...style,
72
- }
73
- }
74
- {...props}
75
- >
76
- {children}
77
- </div>
78
- )
79
- }
60
+ return (
61
+ <div
62
+ ref={composedRef}
63
+ className={className}
64
+ data-disabled={disabled ? '' : undefined}
65
+ data-orientation={vertical ? 'vertical' : 'horizontal'}
66
+ // Which end the value grows from. `percent` is already the position on
67
+ // screen, so this only says which side of it is the filled one.
68
+ data-flipped={xor(vertical, reverse) ? '' : undefined}
69
+ style={
70
+ {
71
+ '--active': active,
72
+ '--inactive': inactive,
73
+ '--length': cssLength(length),
74
+ '--thickness': cssLength(thickness),
75
+ // Where the value sits, for the theme to paint the fill with. The
76
+ // component draws nothing itself: this is the one number CSS cannot
77
+ // work out on its own.
78
+ '--percent': `${percent}%`,
79
+ // The thumb inside is placed against this box.
80
+ position: 'relative',
81
+ ...style,
82
+ } as CSSProperties
83
+ }
84
+ {...props}
85
+ >
86
+ <Placement name="Slider.Track">{children}</Placement>
87
+ </div>
88
+ )
89
+ },
90
+ )
@@ -15,7 +15,7 @@ export type SliderContextValue = {
15
15
  reverse: boolean
16
16
  disabled: boolean
17
17
  readonly: boolean
18
-
18
+ onChange?: (value: number) => void
19
19
  /**
20
20
  * Position of the thumb, 0-100, already accounting for the display direction
21
21
  * implied by `vertical` and `reverse`.
@@ -28,7 +28,9 @@ export type SliderContextValue = {
28
28
  thumbRef: RefObject<SliderThumbMethods | null>
29
29
  }
30
30
 
31
- const SliderContext = createContext<SliderContextValue | null>(null)
31
+ const SliderContext = /* @__PURE__ */ createContext<SliderContextValue | null>(
32
+ null,
33
+ )
32
34
 
33
35
  export const SliderProvider = SliderContext.Provider
34
36