@tremolo-ui/react 0.4.0 → 0.5.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 (62) hide show
  1. package/dist/index.cjs +1125 -1437
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.css +58 -93
  4. package/dist/index.d.cts +561 -243
  5. package/dist/index.d.cts.map +1 -1
  6. package/dist/index.d.ts +561 -243
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1125 -1438
  9. package/dist/index.js.map +1 -1
  10. package/package.json +9 -10
  11. package/src/components/AnimationCanvas/index.tsx +84 -155
  12. package/src/components/Knob/ActiveLine.tsx +6 -6
  13. package/src/components/Knob/InactiveLine.tsx +9 -11
  14. package/src/components/Knob/SVGRoot.tsx +9 -18
  15. package/src/components/Knob/context.tsx +57 -89
  16. package/src/components/Knob/index.css +6 -2
  17. package/src/components/Knob/index.tsx +90 -61
  18. package/src/components/NumberInput/DecrementStepper.tsx +17 -50
  19. package/src/components/NumberInput/IncrementStepper.tsx +17 -50
  20. package/src/components/NumberInput/InputField.tsx +123 -0
  21. package/src/components/NumberInput/Stepper.tsx +90 -12
  22. package/src/components/NumberInput/context.tsx +74 -146
  23. package/src/components/NumberInput/index.css +38 -102
  24. package/src/components/NumberInput/index.tsx +252 -98
  25. package/src/components/NumberInput/stepperButton.tsx +90 -0
  26. package/src/components/Piano/index.tsx +272 -288
  27. package/src/components/Piano/keyboardShortcuts.ts +34 -4
  28. package/src/components/PointsEditor/Background.tsx +8 -2
  29. package/src/components/PointsEditor/Container.tsx +19 -9
  30. package/src/components/PointsEditor/Point.tsx +104 -24
  31. package/src/components/PointsEditor/context.tsx +28 -74
  32. package/src/components/PointsEditor/index.css +5 -0
  33. package/src/components/PointsEditor/index.tsx +77 -43
  34. package/src/components/Slider/{Scale.tsx → Marks.tsx} +8 -8
  35. package/src/components/Slider/{ScaleOption.tsx → MarksOption.tsx} +16 -18
  36. package/src/components/Slider/Thumb.tsx +55 -68
  37. package/src/components/Slider/Track.tsx +15 -15
  38. package/src/components/Slider/context.tsx +36 -51
  39. package/src/components/Slider/index.css +18 -5
  40. package/src/components/Slider/index.tsx +127 -146
  41. package/src/components/Slider/type.ts +4 -4
  42. package/src/components/XYPad/Area.tsx +16 -7
  43. package/src/components/XYPad/Thumb.tsx +62 -74
  44. package/src/components/XYPad/context.tsx +46 -0
  45. package/src/components/XYPad/index.css +12 -0
  46. package/src/components/XYPad/index.tsx +190 -230
  47. package/src/hooks/useDrag.ts +1 -1
  48. package/src/hooks/useDragValue.ts +149 -0
  49. package/src/hooks/useEventListener.ts +1 -1
  50. package/src/hooks/useWheel.ts +29 -9
  51. package/src/index.ts +15 -9
  52. package/src/components/AnimationCanvas/canvas.ts +0 -46
  53. package/src/components/NumberInput/InternalInput.tsx +0 -216
  54. package/src/components/NumberInput/type.ts +0 -58
  55. package/src/components/Piano/KeyLabel.tsx +0 -51
  56. package/src/components/Piano/context.tsx +0 -73
  57. package/src/components/Piano/key.tsx +0 -205
  58. package/src/components/_util/type.ts +0 -1
  59. package/src/hooks/useDragWithElement.ts +0 -100
  60. package/src/hooks/usePianoDrag.ts +0 -75
  61. package/src/hooks/useRefCallbackEvent.ts +0 -34
  62. /package/src/hooks/{useCallbackRef.ts → _internal/useCallbackRef.ts} +0 -0
@@ -1,6 +1,16 @@
1
1
  .tremolo-xy-pad {
2
+ /* Half the thumb size. The thumb overhangs the edges of the area, so the
3
+ pad reserves room for it. Override this when using a different size. */
4
+ --thumb-size: 22px;
5
+
2
6
  display: inline-block;
7
+ margin: calc(var(--thumb-size) / 2);
3
8
  cursor: pointer;
9
+ -webkit-tap-highlight-color: transparent;
10
+
11
+ &[aria-readonly='true'] {
12
+ cursor: not-allowed;
13
+ }
4
14
  }
5
15
 
6
16
  .tremolo-xy-pad-area {
@@ -19,6 +29,8 @@
19
29
  .tremolo-xy-pad-thumb {
20
30
  --color: oklch(59.5% 0.17548 266.373);
21
31
 
32
+ width: var(--thumb-size);
33
+ height: var(--thumb-size);
22
34
  border-radius: 50%;
23
35
  outline: none;
24
36
  background-color: var(--color);
@@ -1,8 +1,8 @@
1
1
  import clsx from 'clsx'
2
- import React, {
2
+ import {
3
3
  ComponentPropsWithoutRef,
4
4
  forwardRef,
5
- ReactElement,
5
+ ReactNode,
6
6
  Ref,
7
7
  useCallback,
8
8
  useImperativeHandle,
@@ -10,31 +10,23 @@ import React, {
10
10
  useRef,
11
11
  } from 'react'
12
12
 
13
+ import type { AxisOptions } from '@tremolo-ui/dom'
13
14
  import {
14
- clamp,
15
- normalizeValue,
16
- rawValue,
17
- stepValue,
15
+ applyDelta,
16
+ linearScale,
18
17
  toFixed,
19
18
  InputEventOption,
20
- styleHelper,
19
+ type Scale,
21
20
  } from '@tremolo-ui/functions'
22
21
 
23
- import { useDragWithElement } from '../../hooks/useDragWithElement'
22
+ import { useDragValue } from '../../hooks/useDragValue'
24
23
  import { useWheel } from '../../hooks/useWheel'
25
24
  import { addUserSelectNone, Cursor, removeUserSelectNone } from '../_util'
26
- import { composeRefs } from '../_util/composeRefs'
27
-
28
- import { Area, XYPadAreaProps } from './Area'
29
- import { Thumb, XYPadThumbMethods, XYPadThumbProps } from './Thumb'
25
+ import { useComposedRefs } from '../_util/composeRefs'
30
26
 
31
- const defaultValueOptions = {
32
- skew: 1,
33
- step: 1,
34
- reverse: false,
35
- wheel: ['raw', 1] as InputEventOption,
36
- keyboard: ['raw', 1] as InputEventOption,
37
- }
27
+ import { Area } from './Area'
28
+ import { toXY, XY, XYInput, XYPadProvider } from './context'
29
+ import { Thumb, XYPadThumbMethods } from './Thumb'
38
30
 
39
31
  const defaultExternalStyles: XYPadProps['externalStyles'] = {
40
32
  userSelectNone: true,
@@ -43,30 +35,38 @@ const defaultExternalStyles: XYPadProps['externalStyles'] = {
43
35
 
44
36
  /**
45
37
  * Two-dimensional slider component.
38
+ *
39
+ * The per-axis settings mirror `Slider`, given as `[x, y]` tuples. A plain
40
+ * value applies to both axes.
46
41
  */
47
- export interface ValueOptions {
48
- // required
49
- value: number
50
- min: number
51
- max: number
42
+ export interface XYPadProps {
43
+ value: XY<number>
44
+ min: XYInput<number>
45
+ max: XYInput<number>
46
+
47
+ step?: XYInput<number>
48
+ /**
49
+ * How the value of each axis is distributed across the travel.
50
+ *
51
+ * Pick one of the scales from `@tremolo-ui/functions`: `linearScale`,
52
+ * `exponentialScale`, `curveScale(n)`, `symmetricSkewScale(n)`, or
53
+ * `skewScale(n)` for a value that has to match a JUCE parameter.
54
+ *
55
+ * @default linearScale
56
+ */
57
+ scale?: XYInput<Scale>
58
+ reverse?: XYInput<boolean>
52
59
 
53
- // optional
54
- step?: number
55
- skew?: number
56
- reverse?: boolean
57
60
  /**
58
- * wheel control option
61
+ * wheel control option. Shift selects the x axis.
62
+ * If null, no event will be triggered
59
63
  */
60
64
  wheel?: InputEventOption | null
61
65
  /**
62
66
  * keyboard control option
67
+ * If null, no event will be triggered
63
68
  */
64
69
  keyboard?: InputEventOption | null
65
- }
66
-
67
- export interface XYPadProps {
68
- x: ValueOptions
69
- y: ValueOptions
70
70
 
71
71
  externalStyles?: {
72
72
  userSelectNone?: boolean
@@ -76,11 +76,22 @@ export interface XYPadProps {
76
76
  disabled?: boolean
77
77
  readonly?: boolean
78
78
 
79
- onChange?: (valueX: number, valueY: number) => void
80
- onDragStart?: (valueX: number, valueY: number) => void
81
- onDragEnd?: (valueX: number, valueY: number) => void
82
- /** \<XYPadThumb /> | \<XYPadArea /> */
83
- children?: ReactElement | ReactElement[]
79
+ onChange?: (value: XY<number>) => void
80
+ onDragStart?: (value: XY<number>) => void
81
+ onDragEnd?: (value: XY<number>) => void
82
+
83
+ /**
84
+ * The pad renders exactly what you compose here; there is no default
85
+ * markup to fall back to.
86
+ *
87
+ * @example
88
+ * <XYPad.Root value={[x, y]} min={0} max={100} onChange={setValue}>
89
+ * <XYPad.Area>
90
+ * <XYPad.Thumb />
91
+ * </XYPad.Area>
92
+ * </XYPad.Root>
93
+ */
94
+ children: ReactNode
84
95
  }
85
96
 
86
97
  export interface XYPadMethods {
@@ -92,11 +103,23 @@ export interface XYPadMethods {
92
103
  type Props = XYPadProps &
93
104
  Omit<ComponentPropsWithoutRef<'div'>, keyof XYPadProps>
94
105
 
106
+ /**
107
+ * The wheel only acts while the focus is inside, so that scrolling a page past
108
+ * the control does not change its value.
109
+ */
110
+ const WHEEL_OPTIONS = { requireFocus: true }
111
+
95
112
  export const Root = forwardRef<XYPadMethods, Props>(
96
113
  (
97
114
  {
98
- x: _x,
99
- y: _y,
115
+ value,
116
+ min: _min,
117
+ max: _max,
118
+ step: _step = 1,
119
+ scale: _scale = linearScale,
120
+ reverse: _reverse = false,
121
+ wheel = ['raw', 1],
122
+ keyboard = ['raw', 1],
100
123
  className,
101
124
  style,
102
125
  externalStyles: _externalStyles,
@@ -114,178 +137,133 @@ export const Root = forwardRef<XYPadMethods, Props>(
114
137
  }: Props,
115
138
  forwardedRef,
116
139
  ) => {
117
- const x = useMemo(() => {
118
- return { ...defaultValueOptions, ..._x }
119
- }, [_x])
120
- const y = useMemo(() => {
121
- return { ...defaultValueOptions, ..._y }
122
- }, [_y])
123
-
124
140
  // -- state and ref ---
125
141
  const rootRef = useRef<HTMLDivElement>(null)
126
- const areaElementRef = useRef<HTMLDivElement>(null)
142
+ const areaRef = useRef<HTMLDivElement>(null)
127
143
  const thumbRef = useRef<XYPadThumbMethods>(null)
128
144
 
129
145
  // --- interpret props ---
130
146
  const externalStyles = { ...defaultExternalStyles, ..._externalStyles }
131
147
 
132
- const nx = normalizeValue(x.value, x.min, x.max, x.skew)
133
- const ny = normalizeValue(y.value, y.min, y.max, y.skew)
134
- const percentX = toFixed((x.reverse ? 1 - nx : nx) * 100)
135
- const percentY = toFixed((y.reverse ? 1 - ny : ny) * 100)
136
-
137
- let areaProps: XYPadAreaProps = {}
138
- let thumbProps: XYPadThumbProps = {}
139
- if (children != undefined) {
140
- React.Children.forEach(children, (child) => {
141
- if (React.isValidElement(child)) {
142
- if (child.type == Thumb) {
143
- thumbProps = child.props as XYPadThumbProps
144
- } else if (child.type == Area) {
145
- areaProps = child.props as XYPadAreaProps
146
- } else {
147
- throw new Error('only <XYPadThumb> or <XYPadArea>')
148
- }
149
- } else {
150
- throw new Error('children is an invalid element.')
151
- }
152
- })
153
- }
148
+ const min = useMemo(() => toXY(_min), [_min])
149
+ const max = useMemo(() => toXY(_max), [_max])
150
+ const step = useMemo(() => toXY(_step), [_step])
151
+ const scale = useMemo(() => toXY(_scale), [_scale])
152
+ const reverse = useMemo(() => toXY(_reverse), [_reverse])
153
+
154
+ const percent = useMemo((): XY<number> => {
155
+ const normalized = [0, 1].map((i) =>
156
+ scale[i].normalize(value[i], min[i], max[i]),
157
+ )
158
+ return [0, 1].map((i) =>
159
+ toFixed((reverse[i] ? 1 - normalized[i] : normalized[i]) * 100),
160
+ ) as XY<number>
161
+ }, [value, min, max, scale, reverse])
154
162
 
155
163
  // --- internal functions ---
156
- const onDrag = useCallback(
157
- (nx: number, ny: number) => {
158
- if (!onChange || readonly) return
159
- const vx = rawValue(x.reverse ? 1 - nx : nx, x.min, x.max, x.skew)
160
- const vy = rawValue(y.reverse ? 1 - ny : ny, y.min, y.max, y.skew)
161
- const newX = clamp(stepValue(vx, x.step), x.min, x.max)
162
- const newY = clamp(stepValue(vy, y.step), y.min, y.max)
163
- onChange(newX, newY)
164
- },
165
- [
166
- onChange,
167
- readonly,
168
- x.max,
169
- x.min,
170
- x.reverse,
171
- x.skew,
172
- x.step,
173
- y.max,
174
- y.min,
175
- y.reverse,
176
- y.skew,
177
- y.step,
178
- ],
164
+ // `AxisOptions` extends `ValueRange`, so the same pair also describes the
165
+ // scaling for `applyDelta`. Its `reverse` only concerns the drag, where
166
+ // positions follow the screen; the key and wheel handlers below flip the
167
+ // direction themselves.
168
+ const axis = useMemo(
169
+ (): XY<AxisOptions> =>
170
+ [0, 1].map((i) => ({
171
+ min: min[i],
172
+ max: max[i],
173
+ step: step[i],
174
+ scale: scale[i],
175
+ reverse: reverse[i],
176
+ })) as XY<AxisOptions>,
177
+ [min, max, step, scale, reverse],
179
178
  )
180
179
 
181
- const updateValueByEvent = useCallback(
182
- (
183
- eventType: InputEventOption[0],
184
- target: Required<ValueOptions>,
185
- x: number,
186
- ) => {
187
- let v
188
- if (eventType == 'normalized') {
189
- const n = normalizeValue(
190
- target.value,
191
- target.min,
192
- target.max,
193
- target.skew,
194
- )
195
- v = rawValue(n + x, target.min, target.max, target.skew)
196
- } else {
197
- v = target.value + x
198
- }
199
- return clamp(stepValue(v, target.step), target.min, target.max)
200
- },
201
- [],
180
+ const withAxis = useCallback(
181
+ (axis: 0 | 1, next: number): XY<number> =>
182
+ axis == 0 ? [next, value[1]] : [value[0], next],
183
+ [value],
184
+ )
185
+
186
+ /** @param i 0 = x, 1 = y */
187
+ const nudge = useCallback(
188
+ (i: 0 | 1, direction: number, option: InputEventOption): XY<number> =>
189
+ withAxis(i, applyDelta(value[i], direction, option, axis[i])),
190
+ [value, axis, withAxis],
202
191
  )
203
192
 
204
193
  const handleKeyDown = useCallback(
205
194
  (event: React.KeyboardEvent<HTMLDivElement>) => {
206
- if (!onChange || readonly) return
195
+ if (!onChange || readonly || !keyboard) return
207
196
  const key = event.key
208
197
  if (['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown'].includes(key)) {
209
- const isHorizontal = key == 'ArrowRight' || key == 'ArrowLeft'
210
- const target = isHorizontal ? x : y
211
- if (!target.keyboard) return
198
+ const i: 0 | 1 = key == 'ArrowRight' || key == 'ArrowLeft' ? 0 : 1
212
199
  event.preventDefault()
213
- let delta = target.keyboard[1]
214
- if (key == 'ArrowLeft' || key == 'ArrowUp') delta *= -1
215
- if (target.reverse) delta *= -1
216
- const newValue = updateValueByEvent(target.keyboard[0], target, delta)
217
- if (isHorizontal) {
218
- onChange(newValue, y.value)
219
- } else {
220
- onChange(x.value, newValue)
221
- }
200
+ let direction = 1
201
+ if (key == 'ArrowLeft' || key == 'ArrowUp') direction *= -1
202
+ if (reverse[i]) direction *= -1
203
+ onChange(nudge(i, direction, keyboard))
222
204
  }
223
205
  },
224
- [onChange, readonly, x, y, updateValueByEvent],
206
+ [onChange, readonly, keyboard, reverse, nudge],
225
207
  )
226
208
 
227
209
  // --- hooks ---
228
- const { refCallback: dragRefCallback } = useDragWithElement<HTMLDivElement>(
229
- {
230
- baseElementRef: areaElementRef,
231
- cursor: readonly ? undefined : externalStyles.cursor,
232
- updateOnPointerDown: true,
233
- onDrag: onDrag,
234
- onDragStart: (nx, ny) => {
235
- if (readonly) return
236
-
237
- if (externalStyles.userSelectNone) addUserSelectNone()
238
-
239
- thumbRef.current?.focus()
240
-
241
- const valueX = clamp(
242
- stepValue(rawValue(nx, x.min, x.max, x.skew), x.step),
243
- x.min,
244
- x.max,
245
- )
246
- const valueY = clamp(
247
- stepValue(rawValue(ny, y.min, y.max, y.skew), y.step),
248
- y.min,
249
- y.max,
250
- )
251
210
 
252
- onDragStart?.(valueX, valueY)
253
- },
254
- onDragEnd: (nx, ny) => {
255
- if (readonly) return
256
-
257
- if (externalStyles.userSelectNone) removeUserSelectNone()
258
-
259
- const valueX = clamp(
260
- stepValue(rawValue(nx, x.min, x.max, x.skew), x.step),
261
- x.min,
262
- x.max,
263
- )
264
- const valueY = clamp(
265
- stepValue(rawValue(ny, y.min, y.max, y.skew), y.step),
266
- y.min,
267
- y.max,
268
- )
269
- onDragEnd?.(valueX, valueY)
270
- },
211
+ const { refCallback: dragRefCallback } = useDragValue<HTMLDivElement>({
212
+ axis,
213
+ baseElementRef: areaRef,
214
+ updateOnPointerDown: true,
215
+ cursor: readonly ? undefined : externalStyles.cursor,
216
+ onChange: (v) => {
217
+ if (readonly) return
218
+ onChange?.(v)
271
219
  },
272
- )
220
+ onDragStart: (v) => {
221
+ if (readonly) return
222
+ if (externalStyles.userSelectNone) addUserSelectNone()
223
+ thumbRef.current?.focus()
224
+ onDragStart?.(v)
225
+ },
226
+ onDragEnd: (v) => {
227
+ if (readonly) return
228
+ if (externalStyles.userSelectNone) removeUserSelectNone()
229
+ onDragEnd?.(v)
230
+ },
231
+ })
273
232
 
274
233
  const wheelRefCallback = useWheel<HTMLDivElement>((event) => {
275
- if (!onChange || readonly) return
276
- const target = event.shiftKey ? x : y
277
- if (!target.wheel) return
234
+ if (!onChange || readonly || !wheel) return
235
+ const i: 0 | 1 = event.shiftKey ? 0 : 1
278
236
  event.preventDefault()
279
- let delta = target.wheel[1]
280
- if (event.deltaY < 0) delta *= -1
281
- if (target.reverse) delta *= -1
282
- const newValue = updateValueByEvent(target.wheel[0], target, delta)
283
- if (event.shiftKey) {
284
- onChange(newValue, y.value)
285
- } else {
286
- onChange(x.value, newValue)
287
- }
288
- })
237
+ let direction = 1
238
+ if (event.deltaY < 0) direction *= -1
239
+ if (reverse[i]) direction *= -1
240
+ onChange(nudge(i, direction, wheel))
241
+ }, WHEEL_OPTIONS)
242
+
243
+ // Composed once, so React attaches the refs a single time instead of
244
+ // detaching and re-attaching on every render.
245
+ const rootRefCallback = useComposedRefs<HTMLDivElement>(
246
+ rootRef,
247
+ dragRefCallback,
248
+ wheelRefCallback,
249
+ )
250
+
251
+ const context = useMemo(
252
+ () => ({
253
+ value,
254
+ min,
255
+ max,
256
+ step,
257
+ scale,
258
+ reverse,
259
+ disabled,
260
+ readonly,
261
+ percent,
262
+ areaRef,
263
+ thumbRef,
264
+ }),
265
+ [value, min, max, step, scale, reverse, disabled, readonly, percent],
266
+ )
289
267
 
290
268
  useImperativeHandle(forwardedRef, () => {
291
269
  return {
@@ -300,52 +278,33 @@ export const Root = forwardRef<XYPadMethods, Props>(
300
278
  }, [])
301
279
 
302
280
  return (
303
- // eslint-disable-next-line jsx-a11y/no-static-element-interactions
304
- <div
305
- className={clsx('tremolo-xy-pad', className)}
306
- ref={composeRefs(rootRef, wheelRefCallback, dragRefCallback)}
307
- tabIndex={-1}
308
- aria-disabled={disabled}
309
- aria-readonly={readonly}
310
- style={{
311
- margin: `calc(${styleHelper(thumbProps?.size ?? 22)} / 2)`, // half thumb size
312
- cursor: readonly ? 'not-allowed' : 'pointer',
313
- WebkitTapHighlightColor: 'transparent',
314
- ...style,
315
- }}
316
- onPointerDown={onPointerDown}
317
- onKeyDown={(event) => {
318
- handleKeyDown(event)
319
- onKeyDown?.(event)
320
- }}
321
- onFocus={(event) => {
322
- thumbRef.current?.focus()
323
- onFocus?.(event)
324
- }}
325
- onBlur={(event) => {
326
- thumbRef.current?.blur()
327
- onBlur?.(event)
328
- }}
329
- {...props}
330
- >
331
- <div className="tremolo-xy-pad-area-wrapper" ref={areaElementRef}>
332
- <Area
333
- __thumb={
334
- <Thumb
335
- ref={thumbRef}
336
- __disabled={disabled}
337
- __readonly={readonly}
338
- __css={{
339
- top: `${percentY}%`,
340
- left: `${percentX}%`,
341
- }}
342
- {...thumbProps}
343
- />
344
- }
345
- {...areaProps}
346
- />
281
+ <XYPadProvider value={context}>
282
+ {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
283
+ <div
284
+ className={clsx('tremolo-xy-pad', className)}
285
+ ref={rootRefCallback}
286
+ tabIndex={-1}
287
+ aria-disabled={disabled}
288
+ aria-readonly={readonly}
289
+ style={style}
290
+ onPointerDown={onPointerDown}
291
+ onKeyDown={(event) => {
292
+ handleKeyDown(event)
293
+ onKeyDown?.(event)
294
+ }}
295
+ onFocus={(event) => {
296
+ thumbRef.current?.focus()
297
+ onFocus?.(event)
298
+ }}
299
+ onBlur={(event) => {
300
+ thumbRef.current?.blur()
301
+ onBlur?.(event)
302
+ }}
303
+ {...props}
304
+ >
305
+ {children}
347
306
  </div>
348
- </div>
307
+ </XYPadProvider>
349
308
  )
350
309
  },
351
310
  )
@@ -361,3 +320,4 @@ export const XYPad = {
361
320
 
362
321
  export { type XYPadThumbProps, type XYPadThumbMethods } from './Thumb'
363
322
  export { type XYPadAreaProps } from './Area'
323
+ export { useXYPadContext, type XY, type XYInput } from './context'
@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'
2
2
 
3
3
  import { createDrag } from '@tremolo-ui/dom'
4
4
 
5
- import { useCallbackRef } from './useCallbackRef'
5
+ import { useCallbackRef } from './_internal/useCallbackRef'
6
6
 
7
7
  interface UseDragProps {
8
8
  /**