@tremolo-ui/react 0.4.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 (90) 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 +2272 -1650
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +1461 -475
  12. package/dist/index.d.cts.map +1 -1
  13. package/dist/index.d.ts +1461 -475
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +2258 -1648
  16. package/dist/index.js.map +1 -1
  17. package/package.json +32 -31
  18. package/src/components/AnimationCanvas/index.tsx +162 -194
  19. package/src/components/Knob/ActiveLine.tsx +9 -8
  20. package/src/components/Knob/InactiveLine.tsx +11 -14
  21. package/src/components/Knob/SVGRoot.tsx +10 -17
  22. package/src/components/Knob/Thumb.tsx +33 -11
  23. package/src/components/Knob/context.tsx +73 -89
  24. package/src/components/Knob/index.tsx +227 -105
  25. package/src/components/NumberInput/DecrementStepper.tsx +21 -50
  26. package/src/components/NumberInput/IncrementStepper.tsx +22 -50
  27. package/src/components/NumberInput/InputField.tsx +206 -0
  28. package/src/components/NumberInput/Stepper.tsx +150 -21
  29. package/src/components/NumberInput/StepperButton.tsx +95 -0
  30. package/src/components/NumberInput/context.tsx +104 -149
  31. package/src/components/NumberInput/index.tsx +461 -110
  32. package/src/components/Piano/index.tsx +413 -263
  33. package/src/components/Piano/keyboard-shortcuts.ts +61 -0
  34. package/src/components/PointsEditor/Background.tsx +19 -4
  35. package/src/components/PointsEditor/Container.tsx +92 -22
  36. package/src/components/PointsEditor/Point.tsx +293 -53
  37. package/src/components/PointsEditor/SelectionBox.tsx +54 -0
  38. package/src/components/PointsEditor/context.tsx +97 -75
  39. package/src/components/PointsEditor/index.tsx +497 -62
  40. package/src/components/Slider/Marks.tsx +84 -0
  41. package/src/components/Slider/MarksOption.tsx +115 -0
  42. package/src/components/Slider/Thumb.tsx +99 -54
  43. package/src/components/Slider/Track.tsx +73 -62
  44. package/src/components/Slider/context.tsx +40 -53
  45. package/src/components/Slider/index.tsx +237 -190
  46. package/src/components/Slider/type.ts +27 -9
  47. package/src/components/XYPad/Area.tsx +52 -33
  48. package/src/components/XYPad/Thumb.tsx +121 -60
  49. package/src/components/XYPad/context.tsx +49 -0
  50. package/src/components/XYPad/index.tsx +338 -246
  51. package/src/components/_util/Placement.tsx +68 -0
  52. package/src/components/_util/VisuallyHiddenRangeInput.tsx +24 -0
  53. package/src/components/_util/css-length.ts +13 -0
  54. package/src/{components/_util/composeRefs.tsx → compose-refs/index.tsx} +33 -5
  55. package/src/hooks/{useCallbackRef.ts → _internal/useCallbackRef.ts} +3 -1
  56. package/src/hooks/_internal/useCheckSteps.ts +170 -0
  57. package/src/hooks/useAnimationFrame.ts +17 -7
  58. package/src/hooks/useDrag.ts +39 -9
  59. package/src/hooks/useDragValue.ts +194 -0
  60. package/src/hooks/useEventListener.ts +11 -7
  61. package/src/hooks/useLongPress.ts +42 -8
  62. package/src/hooks/useMIDIAccess.ts +82 -11
  63. package/src/hooks/useMIDIInput.ts +45 -13
  64. package/src/hooks/useMIDIMessage.ts +23 -5
  65. package/src/hooks/useWheel.ts +29 -9
  66. package/src/index.ts +50 -27
  67. package/src/input-event.ts +32 -0
  68. package/dist/index.css +0 -536
  69. package/src/Globals.d.ts +0 -3
  70. package/src/components/AnimationCanvas/canvas.ts +0 -46
  71. package/src/components/Knob/index.css +0 -77
  72. package/src/components/NumberInput/InternalInput.tsx +0 -216
  73. package/src/components/NumberInput/index.css +0 -185
  74. package/src/components/NumberInput/type.ts +0 -58
  75. package/src/components/Piano/KeyLabel.tsx +0 -51
  76. package/src/components/Piano/context.tsx +0 -73
  77. package/src/components/Piano/index.css +0 -90
  78. package/src/components/Piano/key.tsx +0 -205
  79. package/src/components/Piano/keyboardShortcuts.ts +0 -31
  80. package/src/components/PointsEditor/index.css +0 -30
  81. package/src/components/Slider/Scale.tsx +0 -52
  82. package/src/components/Slider/ScaleOption.tsx +0 -103
  83. package/src/components/Slider/index.css +0 -112
  84. package/src/components/XYPad/index.css +0 -48
  85. package/src/components/_util/index.ts +0 -28
  86. package/src/components/_util/type.ts +0 -1
  87. package/src/hooks/useDragWithElement.ts +0 -100
  88. package/src/hooks/usePianoDrag.ts +0 -75
  89. package/src/hooks/useRefCallbackEvent.ts +0 -34
  90. package/src/styles/global.css +0 -19
@@ -0,0 +1,115 @@
1
+ import { ComponentPropsWithoutRef, CSSProperties, useCallback } from 'react'
2
+
3
+ import { toFixed, xor } from '@tremolo-ui/functions'
4
+
5
+ import { cssLength } from '../_util/css-length'
6
+ import { useCheckPlacement } from '../_util/Placement'
7
+
8
+ import { useSliderContext } from './context'
9
+
10
+ export interface SliderMarksOptionProps {
11
+ /** Where the mark sits, on the same scale as the thumb. */
12
+ value: number
13
+
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`. */
25
+ thickness?: number | string
26
+ /** Length of the mark. Sets `--length`. */
27
+ length?: number | string
28
+ /** Space between the mark and the label. Sets `--gap`. */
29
+ gap?: number | string
30
+ /** Width of the label. Sets `--label-width`. */
31
+ labelWidth?: number | string
32
+ /** Classes for the mark and the label inside the option. */
33
+ classes?: {
34
+ mark?: string
35
+ label?: string
36
+ }
37
+ /** Styles for the mark and the label inside the option. */
38
+ styles?: {
39
+ mark?: CSSProperties
40
+ label?: CSSProperties
41
+ }
42
+ }
43
+
44
+ export function MarksOption({
45
+ value,
46
+ mark = true,
47
+ label,
48
+ thickness,
49
+ length,
50
+ gap,
51
+ labelWidth,
52
+ classes,
53
+ styles,
54
+ className,
55
+ style,
56
+ ...props
57
+ }: SliderMarksOptionProps &
58
+ Omit<ComponentPropsWithoutRef<'div'>, keyof SliderMarksOptionProps>) {
59
+ useCheckPlacement('Slider.MarksOption', 'Slider.Marks')
60
+
61
+ const min = useSliderContext((s) => s.min)
62
+ const max = useSliderContext((s) => s.max)
63
+ const scale = useSliderContext((s) => s.scale)
64
+ const vertical = useSliderContext((s) => s.vertical)
65
+ const reverse = useSliderContext((s) => s.reverse)
66
+
67
+ const calcPercent = useCallback(
68
+ (value: number) => {
69
+ // The marks have to sit on the same curve the thumb runs along.
70
+ const percent = scale.normalize(value, min, max) * 100
71
+ return toFixed(xor(vertical, reverse) ? 100 - percent : percent)
72
+ },
73
+ [vertical, reverse, max, min, scale],
74
+ )
75
+
76
+ return (
77
+ <div
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'}
99
+ {...props}
100
+ >
101
+ {mark && (
102
+ <div
103
+ className={classes?.mark}
104
+ style={styles?.mark}
105
+ data-orientation={vertical ? 'vertical' : 'horizontal'}
106
+ ></div>
107
+ )}
108
+ {label !== null && (
109
+ <div className={classes?.label} style={styles?.label}>
110
+ {label ?? value}
111
+ </div>
112
+ )}
113
+ </div>
114
+ )
115
+ }
@@ -1,5 +1,6 @@
1
- import clsx from 'clsx'
2
1
  import {
2
+ AriaAttributes,
3
+ ComponentPropsWithoutRef,
3
4
  CSSProperties,
4
5
  forwardRef,
5
6
  ReactNode,
@@ -7,21 +8,31 @@ import {
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
- size?: number | string
14
- width?: number | string
15
- height?: number | string
16
-
17
+ /**
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.
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
-
23
- /** @internal */
24
- __percent?: number
32
+ 'aria-label'?: AriaAttributes['aria-label']
33
+ 'aria-labelledby'?: AriaAttributes['aria-labelledby']
34
+ 'aria-describedby'?: AriaAttributes['aria-describedby']
35
+ 'aria-valuetext'?: AriaAttributes['aria-valuetext']
25
36
  }
26
37
 
27
38
  export interface SliderThumbMethods {
@@ -29,65 +40,99 @@ export interface SliderThumbMethods {
29
40
  blur: () => void
30
41
  }
31
42
 
32
- export const defaultThumbSize = 22
43
+ type Props = SliderThumbProps &
44
+ Omit<ComponentPropsWithoutRef<'div'>, keyof SliderThumbProps>
33
45
 
34
- export const Thumb = forwardRef<SliderThumbMethods, SliderThumbProps>(
35
- (
46
+ export const Thumb = /* @__PURE__ */ forwardRef<SliderThumbMethods, Props>(
47
+ function Thumb(
36
48
  {
37
- size,
38
- width = defaultThumbSize,
39
- height = defaultThumbSize,
40
49
  color,
41
50
  children,
42
51
  className,
43
52
  style,
44
- __percent,
45
- }: SliderThumbProps,
46
- ref,
47
- ) => {
48
- const wrapperRef = useRef<HTMLDivElement>(null)
49
- const vertical = useSliderContext((s) => s.vertical)
50
- const disabled = useSliderContext((s) => s.disabled)
51
- const readonly = useSliderContext((s) => s.readonly)
53
+ 'aria-label': ariaLabel,
54
+ 'aria-labelledby': ariaLabelledby,
55
+ 'aria-describedby': ariaDescribedby,
56
+ 'aria-valuetext': ariaValuetext,
57
+ ...props
58
+ },
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')
77
+
78
+ const methods = () => ({
79
+ focus() {
80
+ if (!disabled) inputRef.current?.focus()
81
+ },
82
+ blur() {
83
+ inputRef.current?.blur()
84
+ },
85
+ })
52
86
 
53
- useImperativeHandle(ref, () => {
54
- return {
55
- focus() {
56
- wrapperRef.current?.focus()
57
- },
58
- blur() {
59
- wrapperRef.current?.blur()
60
- },
61
- }
62
- }, [])
87
+ useImperativeHandle(forwardedRef, methods, [disabled])
88
+ // Root focuses the thumb when a drag starts, wherever the user placed it.
89
+ useImperativeHandle(thumbRef, methods, [disabled])
63
90
 
64
91
  return (
65
92
  <div
66
- className="tremolo-slider-thumb-wrapper"
93
+ className={className}
94
+ data-disabled={disabled ? '' : undefined}
95
+ data-readonly={readonly ? '' : undefined}
96
+ {...props}
67
97
  style={{
68
- top: vertical ? `${__percent}%` : '50%',
69
- left: !vertical ? `${__percent}%` : '50%',
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%',
70
112
  }}
71
113
  >
72
- {children ? (
73
- children
74
- ) : (
75
- // default slider thumb
76
- <div
77
- ref={wrapperRef}
78
- className={clsx('tremolo-slider-thumb', className)}
79
- // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
80
- tabIndex={0}
81
- aria-disabled={disabled}
82
- aria-readonly={readonly}
83
- style={{
84
- ...{ '--color': color },
85
- width: size ?? width,
86
- height: size ?? height,
87
- ...style,
88
- }}
89
- ></div>
90
- )}
114
+ <VisuallyHiddenRangeInput
115
+ ref={inputRef}
116
+ value={value}
117
+ min={min}
118
+ max={max}
119
+ step={step}
120
+ disabled={disabled}
121
+ aria-readonly={readonly}
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)
133
+ }}
134
+ />
135
+ {children}
91
136
  </div>
92
137
  )
93
138
  },
@@ -1,79 +1,90 @@
1
- import clsx from 'clsx'
2
- import { ComponentPropsWithoutRef, CSSProperties, ReactElement } 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 { useSliderContext } from './context'
10
+ import { useComposedRefs } from '../../compose-refs'
11
+ import { cssLength } from '../_util/css-length'
12
+ import { Placement } from '../_util/Placement'
7
13
 
8
- export const defaultLength = 140
9
- export const defaultThickness = 10
14
+ import { useSliderContext } from './context'
10
15
 
11
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
+ */
12
21
  length?: number | string
22
+ /** How thick the track is across that axis. Sets `--thickness`. */
13
23
  thickness?: number | string
14
24
 
25
+ /** Colour of the part from `min` to the value. Sets `--active`. */
15
26
  active?: string
27
+ /** Colour of the rest of the track. Sets `--inactive`. */
16
28
  inactive?: string
17
29
 
18
- defaultStyle?: boolean
19
-
20
30
  className?: string
21
31
  style?: CSSProperties
22
- children?: ReactElement
23
-
24
- /** @internal */
25
- __thumb?: ReactElement
26
- /** @internal */
27
- __percent?: number
32
+ /** `<Slider.Thumb />` goes here. */
33
+ children?: ReactNode
28
34
  }
29
35
 
30
- export function Track({
31
- length = defaultLength,
32
- thickness = defaultThickness,
33
- active,
34
- inactive,
35
- children,
36
- className,
37
- style,
38
- defaultStyle = true,
39
- __thumb,
40
- __percent = 0,
41
- ...props
42
- }: SliderTrackProps &
43
- Omit<ComponentPropsWithoutRef<'div'>, keyof SliderTrackProps>) {
44
- const vertical = useSliderContext((s) => s.vertical)
45
- const reverse = useSliderContext((s) => s.reverse)
46
- const disabled = useSliderContext((s) => s.disabled)
36
+ type Props = SliderTrackProps &
37
+ Omit<ComponentPropsWithoutRef<'div'>, keyof SliderTrackProps>
47
38
 
48
- const direction = vertical ? 'bottom' : 'right'
49
- const colors = {
50
- '--active': active,
51
- '--inactive': inactive,
52
- }
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()
53
55
 
54
- return (
55
- <div
56
- className={clsx('tremolo-slider-track', className)}
57
- aria-disabled={disabled}
58
- data-vertical={vertical}
59
- style={
60
- !defaultStyle
61
- ? style
62
- : {
63
- ...colors,
64
- background: xor(vertical, reverse)
65
- ? `linear-gradient(to ${direction}, var(--inactive) ${__percent}%, var(--active) ${__percent}%)`
66
- : `linear-gradient(to ${direction}, var(--active) ${__percent}%, var(--inactive) ${__percent}%)`,
67
- borderRadius: styleHelper(thickness!, '/', 2),
68
- width: !vertical ? length : thickness,
69
- height: vertical ? length : thickness,
70
- ...style,
71
- }
72
- }
73
- {...props}
74
- >
75
- {children}
76
- {__thumb}
77
- </div>
78
- )
79
- }
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)
59
+
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
+ )
@@ -1,64 +1,51 @@
1
- import { createContext, useContext, useEffect, useRef } from 'react'
2
- import { createStore, useStore } from 'zustand'
1
+ import { createContext, RefObject, useContext } from 'react'
3
2
 
4
- type State = {
3
+ import type { Scale } from '@tremolo-ui/functions'
4
+
5
+ import type { SliderThumbMethods } from './Thumb'
6
+
7
+ export type SliderContextValue = {
8
+ value: number
5
9
  min: number
6
10
  max: number
7
11
  step: number
8
- skew: number
12
+ /** How the value is distributed across the travel. */
13
+ scale: Scale
9
14
  vertical: boolean
10
15
  reverse: boolean
11
16
  disabled: boolean
12
17
  readonly: boolean
18
+ onChange?: (value: number) => void
19
+ /**
20
+ * Position of the thumb, 0-100, already accounting for the display direction
21
+ * implied by `vertical` and `reverse`.
22
+ */
23
+ percent: number
24
+
25
+ /** `Track` registers its element here; `Root` normalizes the pointer against it. */
26
+ trackRef: RefObject<HTMLDivElement | null>
27
+ /** `Thumb` registers itself here; `Root` focuses it when a drag starts. */
28
+ thumbRef: RefObject<SliderThumbMethods | null>
13
29
  }
14
30
 
15
- type SliderStore = ReturnType<typeof createSliderStore>
16
-
17
- const createSliderStore = (initProps?: Partial<State>) => {
18
- const DEFAULT_PROPS: State = {
19
- min: 0,
20
- max: 0,
21
- step: 0,
22
- skew: 0,
23
- vertical: false,
24
- reverse: false,
25
- disabled: false,
26
- readonly: false,
27
- }
28
-
29
- return createStore<State>()(() => ({
30
- ...DEFAULT_PROPS,
31
- ...initProps,
32
- }))
33
- }
34
-
35
- const SliderContext = createContext<SliderStore | null>(null)
36
-
37
- type SliderProviderProps = React.PropsWithChildren<Partial<State>>
38
-
39
- export function SliderProvider({ children, ...props }: SliderProviderProps) {
40
- const storeRef = useRef<SliderStore>(null)
41
- if (!storeRef.current) {
42
- storeRef.current = createSliderStore(props)
43
- }
44
-
45
- useEffect(() => {
46
- if (storeRef.current) {
47
- storeRef.current.setState(props)
48
- } else {
49
- storeRef.current = createSliderStore(props)
50
- }
51
- }, [props])
52
-
53
- return (
54
- <SliderContext.Provider value={storeRef.current}>
55
- {children}
56
- </SliderContext.Provider>
57
- )
58
- }
59
-
60
- export function useSliderContext<T>(selector: (state: State) => T): T {
61
- const store = useContext(SliderContext)
62
- if (!store) throw new Error('Missing SliderContext.Provider in the tree')
63
- return useStore(store, selector)
31
+ const SliderContext = /* @__PURE__ */ createContext<SliderContextValue | null>(
32
+ null,
33
+ )
34
+
35
+ export const SliderProvider = SliderContext.Provider
36
+
37
+ /**
38
+ * Everything here is derived during render, so there is no state to keep in
39
+ * sync: `value` comes from the props of `Root` and the rest follows from it.
40
+ */
41
+ export function useSliderContext(): SliderContextValue
42
+ export function useSliderContext<T>(
43
+ selector: (state: SliderContextValue) => T,
44
+ ): T
45
+ export function useSliderContext<T>(
46
+ selector?: (state: SliderContextValue) => T,
47
+ ) {
48
+ const context = useContext(SliderContext)
49
+ if (!context) throw new Error('Missing SliderContext.Provider in the tree')
50
+ return selector ? selector(context) : context
64
51
  }