@tremolo-ui/react 0.1.5 → 0.1.6

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.
@@ -3,11 +3,13 @@ import React, {
3
3
  ComponentPropsWithoutRef,
4
4
  createRef,
5
5
  CSSProperties,
6
+ forwardRef,
6
7
  ReactElement,
7
8
  ReactNode,
8
9
  RefObject,
9
10
  useCallback,
10
11
  useEffect,
12
+ useImperativeHandle,
11
13
  useMemo,
12
14
  useRef,
13
15
  useState,
@@ -35,13 +37,9 @@ import {
35
37
  import { KeyboardShortcuts } from './keyboardShortcuts'
36
38
 
37
39
  /**
38
- * Piano component
39
- *
40
- * TODO:
41
- * - add scale highlight
40
+ * [noteRange.first, noteRange.first + 1 ..., noteRange.last]
41
+ * @category Piano
42
42
  */
43
-
44
- /** @category Piano */
45
43
  export function getNoteRangeArray(noteRange: NoteRange) {
46
44
  return Array.from(
47
45
  { length: noteRange.last - noteRange.first + 1 },
@@ -51,10 +49,8 @@ export function getNoteRangeArray(noteRange: NoteRange) {
51
49
 
52
50
  /** @category Piano */
53
51
  export interface PianoProps {
54
- // required
55
52
  noteRange: NoteRange
56
53
 
57
- // optional
58
54
  glissando?: boolean
59
55
  midiMax?: number
60
56
  keyboardShortcuts?: KeyboardShortcuts
@@ -62,11 +58,10 @@ export interface PianoProps {
62
58
  whiteNoteWidth?: number
63
59
  blackNoteWidth?: number
64
60
  height?: number | string
65
-
66
61
  style?: CSSProperties
67
62
 
68
- onPlayNote?: (noteNumber: number) => void
69
- onStopNote?: (noteNumber: number) => void
63
+ onPlayNote?: (note: number, velocity?: number) => void
64
+ onStopNote?: (note: number) => void
70
65
 
71
66
  label?: (note: number, index: number) => ReactNode
72
67
 
@@ -78,236 +73,277 @@ export interface PianoProps {
78
73
 
79
74
  const blackPerWhiteWidth = defaultBlackKeyWidth / defaultWhiteKeyWidth // 0.65
80
75
 
76
+ /**
77
+ * @category Piano
78
+ */
79
+ export interface PianoMethods {
80
+ playNote: (note: number, velocity?: number) => void
81
+ stopNote: (note: number) => void
82
+ }
83
+
84
+ type Props = PianoProps &
85
+ Omit<ComponentPropsWithoutRef<'div'>, keyof PianoProps>
86
+
81
87
  /**
82
88
  * Customizable piano component.
83
89
  * @category Piano
84
90
  */
85
- export function Piano({
86
- noteRange,
87
- glissando = true,
88
- midiMax = 127,
89
- keyboardShortcuts,
90
- fill = false,
91
- height = fill ? '100%' : 160,
92
- whiteNoteWidth: _whiteNoteWidth = defaultWhiteKeyWidth,
93
- style,
94
- className,
95
- onPlayNote,
96
- onStopNote,
97
- label,
98
- children,
99
- onPointerDown,
100
- ...props
101
- }: PianoProps & Omit<ComponentPropsWithoutRef<'div'>, keyof PianoProps>) {
102
- // -- state and ref ---
103
- const [whiteNoteWidth, setWhiteNoteWidth] = useState(_whiteNoteWidth)
104
- const keyRefs = useRef<RefObject<KeyMethods | null>[]>([])
105
- for (let i = 0; i < noteRange.last - noteRange.first + 1; i++) {
106
- keyRefs.current[i] = createRef<KeyMethods>()
107
- }
108
- const pianoRef = useRef<HTMLDivElement>(null)
109
- const hitKeyIndex = useRef(-1)
91
+ export const Piano = forwardRef<PianoMethods, Props>(
92
+ (
93
+ {
94
+ noteRange,
95
+ glissando = true,
96
+ midiMax = 127,
97
+ keyboardShortcuts,
98
+ fill = false,
99
+ height = fill ? '100%' : 160,
100
+ whiteNoteWidth: _whiteNoteWidth = defaultWhiteKeyWidth,
101
+ style,
102
+ className,
103
+ onPlayNote,
104
+ onStopNote,
105
+ label,
106
+ children,
107
+ onPointerDown,
108
+ ...props
109
+ },
110
+ forwardedRef,
111
+ ) => {
112
+ // -- state and ref ---
113
+ const [whiteNoteWidth, setWhiteNoteWidth] = useState(_whiteNoteWidth)
114
+ const keyRefs = useRef<RefObject<KeyMethods | null>[]>([])
115
+ for (let i = 0; i < noteRange.last - noteRange.first + 1; i++) {
116
+ keyRefs.current[i] = createRef<KeyMethods>()
117
+ }
118
+ const pianoRef = useRef<HTMLDivElement>(null)
119
+ const hitKeyIndex = useRef(-1)
110
120
 
111
- // --- interpret props ---
112
- const noteRangeArray = getNoteRangeArray(noteRange)
113
- const whiteNotes = noteRangeArray.filter((v) => isWhiteKey(v))
114
- const blackNotes = noteRangeArray.filter((v) => isBlackKey(v))
115
- const whiteNoteCount = whiteNotes.length
116
- const padding = 1
117
- const staticWidth = (whiteNoteWidth + padding) * whiteNoteCount
118
- // const blackNoteShiftPercent = 0.3
121
+ // --- interpret props ---
122
+ const noteRangeArray = getNoteRangeArray(noteRange)
123
+ const whiteNotes = noteRangeArray.filter((v) => isWhiteKey(v))
124
+ const blackNotes = noteRangeArray.filter((v) => isBlackKey(v))
125
+ const whiteNoteCount = whiteNotes.length
126
+ const padding = 1
127
+ const staticWidth = (whiteNoteWidth + padding) * whiteNoteCount
128
+ // const blackNoteShiftPercent = 0.3
119
129
 
120
- const childrenWithProps = useMemo(() => {
121
- return (
122
- children &&
123
- React.Children.map(children, (child, index) => {
124
- if (React.isValidElement(child)) {
125
- const __width =
126
- child.type == WhiteKey ? whiteNoteWidth : whiteNoteWidth * 0.65
127
- const props = { ref: keyRefs.current[index], __width }
128
- return React.cloneElement(child, props)
130
+ const childrenWithProps = useMemo(() => {
131
+ return (
132
+ children &&
133
+ React.Children.map(children, (child, index) => {
134
+ if (React.isValidElement(child)) {
135
+ const __width =
136
+ child.type == WhiteKey ? whiteNoteWidth : whiteNoteWidth * 0.65
137
+ const props = { ref: keyRefs.current[index], __width }
138
+ return React.cloneElement(child, props)
139
+ }
140
+ return child
141
+ })
142
+ )
143
+ }, [children, whiteNoteWidth])
144
+
145
+ // --- internal functions ---
146
+ const notePosition = useCallback(
147
+ (note: number) => {
148
+ const pitchPositions: Record<NoteKey, number> = {
149
+ C: 0,
150
+ 'C#': 1,
151
+ D: 1,
152
+ 'D#': 2,
153
+ E: 2,
154
+ F: 3,
155
+ 'F#': 4,
156
+ G: 4,
157
+ 'G#': 5,
158
+ A: 5,
159
+ 'A#': 6,
160
+ B: 6,
129
161
  }
130
- return child
131
- })
132
- )
133
- }, [children, whiteNoteWidth])
134
162
 
135
- // --- internal functions ---
136
- const notePosition = useCallback(
137
- (note: number) => {
138
- const pitchPositions: Record<NoteKey, number> = {
139
- C: 0,
140
- 'C#': 1,
141
- D: 1,
142
- 'D#': 2,
143
- E: 2,
144
- F: 3,
145
- 'F#': 4,
146
- G: 4,
147
- 'G#': 5,
148
- A: 5,
149
- 'A#': 6,
150
- B: 6,
151
- }
163
+ const blackNoteWidth = whiteNoteWidth * blackPerWhiteWidth
164
+ const padding = 1
165
+ const targetNoteKey = noteKey(note)
166
+ const firstNoteKey = noteKey(noteRange.first)
167
+ const octave = Math.floor((note - noteRange.first) / 12)
168
+ const octaveOffset =
169
+ noteKeys.indexOf(firstNoteKey) > noteKeys.indexOf(targetNoteKey)
170
+ ? 1
171
+ : 0
172
+ const w = whiteNoteWidth + padding
173
+ const pos = pitchPositions[targetNoteKey] - pitchPositions[firstNoteKey]
174
+ const blackKeyOffset = isBlackKey(note) ? blackNoteWidth / 2 : 0
175
+ return pos * w + (octave + octaveOffset) * 7 * w - blackKeyOffset
176
+ },
177
+ [noteRange.first, whiteNoteWidth],
178
+ )
152
179
 
153
- const blackNoteWidth = whiteNoteWidth * blackPerWhiteWidth
154
- const padding = 1
155
- const targetNoteKey = noteKey(note)
156
- const firstNoteKey = noteKey(noteRange.first)
157
- const octave = Math.floor((note - noteRange.first) / 12)
158
- const octaveOffset =
159
- noteKeys.indexOf(firstNoteKey) > noteKeys.indexOf(targetNoteKey) ? 1 : 0
160
- const w = whiteNoteWidth + padding
161
- const pos = pitchPositions[targetNoteKey] - pitchPositions[firstNoteKey]
162
- const blackKeyOffset = isBlackKey(note) ? blackNoteWidth / 2 : 0
163
- return pos * w + (octave + octaveOffset) * 7 * w - blackKeyOffset
164
- },
165
- [noteRange.first, whiteNoteWidth],
166
- )
180
+ const getHitKeyIndex = useCallback(
181
+ (x: number, y: number) => {
182
+ if (!pianoRef.current) return -1
183
+ const containerHeight = pianoRef.current.clientHeight
184
+ const notes = [...blackNotes, ...whiteNotes]
185
+ for (let i = 0; i < notes.length; i++) {
186
+ const note = notes[i]
187
+ const pos = notePosition(note)
188
+ const w = isWhiteKey(note)
189
+ ? whiteNoteWidth
190
+ : whiteNoteWidth * blackPerWhiteWidth
191
+ const h = isWhiteKey(note) ? containerHeight : containerHeight * 0.6
192
+ if (pos <= x && x < pos + w && 0 <= y && y < h) {
193
+ return note
194
+ }
195
+ }
196
+ return -1
197
+ },
198
+ [blackNotes, notePosition, whiteNoteWidth, whiteNotes],
199
+ )
167
200
 
168
- const getHitKeyIndex = useCallback(
169
- (x: number, y: number) => {
170
- if (!pianoRef.current) return -1
171
- const containerHeight = pianoRef.current.clientHeight
172
- const notes = [...blackNotes, ...whiteNotes]
173
- for (let i = 0; i < notes.length; i++) {
174
- const note = notes[i]
175
- const pos = notePosition(note)
176
- const w = isWhiteKey(note)
177
- ? whiteNoteWidth
178
- : whiteNoteWidth * blackPerWhiteWidth
179
- const h = isWhiteKey(note) ? containerHeight : containerHeight * 0.6
180
- if (pos <= x && x < pos + w && 0 <= y && y < h) {
181
- return note
201
+ // TODO: 単一のポインターに対しては、useDragで対応可能だが、
202
+ // マルチタッチに対しては、TouchEventを使う必要がありそう
203
+ // 取り敢えず、シングルタッチだけ対応
204
+ const onDrag = useCallback(
205
+ (perX: number, perY: number) => {
206
+ if (!pianoRef.current) return
207
+ const x = perX * staticWidth
208
+ const y = perY * pianoRef.current.clientHeight
209
+ const note = getHitKeyIndex(x, y)
210
+ const index = noteRangeArray.indexOf(note)
211
+ if (index == -1) return
212
+ if (hitKeyIndex.current != index) {
213
+ keyRefs.current[hitKeyIndex.current]?.current?.stop()
214
+ keyRefs.current[index]?.current?.play()
215
+ hitKeyIndex.current = index
182
216
  }
183
- }
184
- return -1
185
- },
186
- [blackNotes, notePosition, whiteNoteWidth, whiteNotes],
187
- )
217
+ },
218
+ [getHitKeyIndex, noteRangeArray, staticWidth],
219
+ )
188
220
 
189
- // TODO: 単一のポインターに対しては、useDragで対応可能だが、
190
- // マルチタッチに対しては、TouchEventを使う必要がありそう
191
- // 取り敢えず、シングルタッチだけ対応
192
- const onDrag = useCallback(
193
- (perX: number, perY: number) => {
194
- if (!pianoRef.current) return
195
- const x = perX * staticWidth
196
- const y = perY * pianoRef.current.clientHeight
197
- const note = getHitKeyIndex(x, y)
198
- const index = noteRangeArray.indexOf(note)
199
- if (index == -1) return
200
- if (hitKeyIndex.current != index) {
221
+ const onDragEnd = useCallback(() => {
222
+ if (keyRefs.current[hitKeyIndex.current]?.current?.played()) {
201
223
  keyRefs.current[hitKeyIndex.current]?.current?.stop()
202
- keyRefs.current[index]?.current?.play()
203
- hitKeyIndex.current = index
204
224
  }
205
- },
206
- [getHitKeyIndex, noteRangeArray, staticWidth],
207
- )
225
+ hitKeyIndex.current = -1
226
+ }, [keyRefs])
208
227
 
209
- const onDragEnd = useCallback(() => {
210
- if (keyRefs.current[hitKeyIndex.current]?.current?.played()) {
211
- keyRefs.current[hitKeyIndex.current]?.current?.stop()
212
- }
213
- hitKeyIndex.current = -1
214
- }, [keyRefs])
228
+ // --- hooks ---
229
+ useEffect(() => {
230
+ if (fill && pianoRef.current) {
231
+ const parent = pianoRef.current.parentElement
232
+ if (!parent) throw new Error("doesn't have a parent element.")
233
+ const resizeObserver = new ResizeObserver(() => {
234
+ const w = pianoRef.current!.clientWidth
235
+ setWhiteNoteWidth(w / whiteNoteCount - padding)
236
+ })
237
+ resizeObserver.observe(parent)
238
+ return () => {
239
+ resizeObserver.unobserve(parent)
240
+ }
241
+ } else {
242
+ setWhiteNoteWidth(_whiteNoteWidth)
243
+ }
244
+ }, [fill, _whiteNoteWidth, whiteNoteCount])
215
245
 
216
- // --- hooks ---
217
- useEffect(() => {
218
- if (fill && pianoRef.current) {
219
- const parent = pianoRef.current.parentElement
220
- if (!parent) throw new Error("doesn't have a parent element.")
221
- const resizeObserver = new ResizeObserver(() => {
222
- const w = pianoRef.current!.clientWidth
223
- setWhiteNoteWidth(w / whiteNoteCount - padding)
246
+ const [touchMoveRefCallback, pointerDownHandler] =
247
+ useDragWithElement<HTMLDivElement>({
248
+ baseElementRef: pianoRef,
249
+ onDrag: onDrag,
250
+ onDragEnd: onDragEnd,
224
251
  })
225
- resizeObserver.observe(parent)
226
- return () => {
227
- resizeObserver.unobserve(parent)
228
- }
229
- } else {
230
- setWhiteNoteWidth(_whiteNoteWidth)
231
- }
232
- }, [fill, _whiteNoteWidth, whiteNoteCount])
233
252
 
234
- const [touchMoveRefCallback, pointerDownHandler] =
235
- useDragWithElement<HTMLDivElement>({
236
- baseElementRef: pianoRef,
237
- onDrag: onDrag,
238
- onDragEnd: onDragEnd,
253
+ useEventListener(globalThis.window, 'keydown', (e) => {
254
+ if (e.repeat) return
255
+ if (!keyboardShortcuts) return
256
+ const index = keyboardShortcuts.keys.indexOf(e.key)
257
+ if (index != -1) {
258
+ if (!keyRefs.current[index]?.current?.played()) {
259
+ keyRefs.current[index]?.current?.play()
260
+ }
261
+ }
239
262
  })
240
263
 
241
- useEventListener(globalThis.window, 'keydown', (e) => {
242
- if (e.repeat) return
243
- if (!keyboardShortcuts) return
244
- const index = keyboardShortcuts.keys.indexOf(e.key)
245
- if (index != -1) {
246
- if (!keyRefs.current[index]?.current?.played()) {
247
- keyRefs.current[index]?.current?.play()
264
+ useEventListener(globalThis.window, 'keyup', (e) => {
265
+ if (e.repeat) return
266
+ if (!keyboardShortcuts) return
267
+ const index = keyboardShortcuts.keys.indexOf(e.key)
268
+ if (index != -1) {
269
+ keyRefs.current[index]?.current?.stop()
248
270
  }
249
- }
250
- })
271
+ })
251
272
 
252
- useEventListener(globalThis.window, 'keyup', (e) => {
253
- if (e.repeat) return
254
- if (!keyboardShortcuts) return
255
- const index = keyboardShortcuts.keys.indexOf(e.key)
256
- if (index != -1) {
257
- keyRefs.current[index]?.current?.stop()
258
- }
259
- })
273
+ useImperativeHandle(forwardedRef, () => {
274
+ return {
275
+ playNote(note, velocity) {
276
+ const index = noteRangeArray.indexOf(note)
277
+ if (index != -1) {
278
+ if (!keyRefs.current[index]?.current?.played()) {
279
+ keyRefs.current[index]?.current?.play(velocity)
280
+ }
281
+ } else {
282
+ onPlayNote?.(note, velocity)
283
+ }
284
+ },
285
+ stopNote(note: number) {
286
+ const index = noteRangeArray.indexOf(note)
287
+ if (index != -1) {
288
+ keyRefs.current[index]?.current?.stop()
289
+ } else {
290
+ onStopNote?.(note)
291
+ }
292
+ },
293
+ }
294
+ }, [noteRangeArray, onPlayNote, onStopNote])
260
295
 
261
- return (
262
- <PianoProvider
263
- notePosition={notePosition}
264
- noteRange={noteRange}
265
- glissando={glissando}
266
- midiMax={midiMax}
267
- fill={fill}
268
- onPlayNote={onPlayNote}
269
- onStopNote={onStopNote}
270
- label={label}
271
- >
272
- <div
273
- ref={(div) => {
274
- pianoRef.current = div
275
- touchMoveRefCallback(div)
276
- }}
277
- className={clsx('tremolo-piano', className)}
278
- style={{
279
- width: fill ? '100%' : staticWidth,
280
- height: height,
281
- ...style,
282
- }}
283
- onPointerDown={(event) => {
284
- pointerDownHandler(event)
285
- onPointerDown?.(event)
286
- }}
287
- {...props}
296
+ return (
297
+ <PianoProvider
298
+ notePosition={notePosition}
299
+ noteRange={noteRange}
300
+ glissando={glissando}
301
+ midiMax={midiMax}
302
+ fill={fill}
303
+ onPlayNote={onPlayNote}
304
+ onStopNote={onStopNote}
305
+ label={label}
288
306
  >
289
- {childrenWithProps ||
290
- noteRangeArray.map((note, index) =>
291
- isWhiteKey(note) ? (
292
- <WhiteKey
293
- ref={keyRefs.current[index]}
294
- key={note}
295
- noteNumber={note}
296
- __width={whiteNoteWidth}
297
- />
298
- ) : (
299
- <BlackKey
300
- ref={keyRefs.current[index]}
301
- key={note}
302
- noteNumber={note}
303
- __width={whiteNoteWidth * blackPerWhiteWidth}
304
- />
305
- ),
306
- )}
307
- </div>
308
- </PianoProvider>
309
- )
310
- }
307
+ <div
308
+ ref={(div) => {
309
+ pianoRef.current = div
310
+ touchMoveRefCallback(div)
311
+ }}
312
+ className={clsx('tremolo-piano', className)}
313
+ style={{
314
+ width: fill ? '100%' : staticWidth,
315
+ height: height,
316
+ ...style,
317
+ }}
318
+ onPointerDown={(event) => {
319
+ pointerDownHandler(event)
320
+ onPointerDown?.(event)
321
+ }}
322
+ {...props}
323
+ >
324
+ {childrenWithProps ||
325
+ noteRangeArray.map((note, index) =>
326
+ isWhiteKey(note) ? (
327
+ <WhiteKey
328
+ ref={keyRefs.current[index]}
329
+ key={note}
330
+ noteNumber={note}
331
+ __width={whiteNoteWidth}
332
+ />
333
+ ) : (
334
+ <BlackKey
335
+ ref={keyRefs.current[index]}
336
+ key={note}
337
+ noteNumber={note}
338
+ __width={whiteNoteWidth * blackPerWhiteWidth}
339
+ />
340
+ ),
341
+ )}
342
+ </div>
343
+ </PianoProvider>
344
+ )
345
+ },
346
+ )
311
347
 
312
348
  export { type KeyboardShortcuts, SHORTCUTS } from './keyboardShortcuts'
313
349
  export { WhiteKey, BlackKey, type KeyProps, type KeyMethods } from './key'
@@ -40,7 +40,7 @@ export interface KeyProps {
40
40
  * @category Piano
41
41
  */
42
42
  export interface KeyMethods {
43
- play: () => void
43
+ play: (velocity?: number) => void
44
44
  stop: () => void
45
45
  played: () => boolean
46
46
  }
@@ -92,10 +92,10 @@ const KeyImpl = forwardRef<KeyMethods, ImplProps>(
92
92
 
93
93
  useImperativeHandle(ref, () => {
94
94
  return {
95
- play() {
95
+ play(velocity) {
96
96
  if (disabled) return
97
97
  setPlayed(true)
98
- if (onPlayNote) onPlayNote(noteNumber)
98
+ if (onPlayNote) onPlayNote(noteNumber, velocity)
99
99
  },
100
100
  stop() {
101
101
  setPlayed(false)
@@ -64,8 +64,8 @@ export function SliderTrack({
64
64
  : {
65
65
  ...colors,
66
66
  background: xor(vertical, reverse)
67
- ? `linear-gradient(to ${direction}, var(--inactive, #eee) ${__percent}%, var(--active, #7998ec) ${__percent}%)`
68
- : `linear-gradient(to ${direction}, var(--active, #7998ec) ${__percent}%, var(--inactive, #eee) ${__percent}%)`,
67
+ ? `linear-gradient(to ${direction}, var(--inactive) ${__percent}%, var(--active) ${__percent}%)`
68
+ : `linear-gradient(to ${direction}, var(--active) ${__percent}%, var(--inactive) ${__percent}%)`,
69
69
  borderRadius: styleHelper(thickness!, '/', 2),
70
70
  width: !vertical ? length : thickness,
71
71
  height: vertical ? length : thickness,
@@ -112,7 +112,7 @@ export const Slider = forwardRef<SliderMethods, Props>(
112
112
  onKeyDown,
113
113
  ...props
114
114
  }: Props,
115
- ref,
115
+ forwardedRef,
116
116
  ) => {
117
117
  // -- state and ref ---
118
118
  const trackElementRef = useRef<HTMLDivElement>(null)
@@ -245,7 +245,7 @@ export const Slider = forwardRef<SliderMethods, Props>(
245
245
  [wheel, vertical, readonly, onChange, updateValueByEvent],
246
246
  )
247
247
 
248
- useImperativeHandle(ref, () => {
248
+ useImperativeHandle(forwardedRef, () => {
249
249
  return {
250
250
  focus() {
251
251
  thumbRef.current?.focus()
@@ -106,7 +106,7 @@ export const XYPad = forwardRef<XYPadMethods, Props>(
106
106
  children,
107
107
  ...props
108
108
  }: Props,
109
- ref,
109
+ forwardedRef,
110
110
  ) => {
111
111
  const x = useMemo(() => {
112
112
  return { ...defaultValueOptions, ..._x }
@@ -276,7 +276,7 @@ export const XYPad = forwardRef<XYPadMethods, Props>(
276
276
  [x, y, onChange, readonly, updateValueByEvent],
277
277
  )
278
278
 
279
- useImperativeHandle(ref, () => {
279
+ useImperativeHandle(forwardedRef, () => {
280
280
  return {
281
281
  focus() {
282
282
  thumbRef.current?.focus()
@@ -1,5 +1,8 @@
1
1
  import { DependencyList, useCallback, useEffect, useRef } from 'react'
2
2
 
3
+ /**
4
+ * @category hooks
5
+ */
3
6
  export function useAnimationFrame(
4
7
  callback = () => {},
5
8
  deps: DependencyList = [],
@@ -1,8 +1,8 @@
1
1
  import { useCallback, useInsertionEffect, useRef } from 'react'
2
2
 
3
3
  /**
4
- * This hook is user-land implementation of the experimental `useEffectEvent` hook.
5
- * React docs: https://react.dev/learn/separating-events-from-effects#declaring-an-effect-event
4
+ * Internal
5
+ * @private
6
6
  */
7
7
  export function useCallbackRef<Args extends unknown[], Return>(
8
8
  callback: ((...args: Args) => Return) | undefined,
@@ -3,7 +3,7 @@ import { useRef, useCallback } from 'react'
3
3
  import { useEventListener } from './useEventListener'
4
4
  import { useRefCallbackEvent } from './useRefCallbackEvent'
5
5
 
6
- export interface UseDragProps {
6
+ interface UseDragProps {
7
7
  threshold?: number
8
8
 
9
9
  onDrag: (x: number, y: number, deltaX: number, deltaY: number) => void
@@ -12,6 +12,7 @@ export interface UseDragProps {
12
12
  }
13
13
 
14
14
  /**
15
+ * @category hooks
15
16
  * @returns [refCallback, pointerDownHandler]
16
17
  */
17
18
  export function useDrag<T extends Element>({
@@ -5,7 +5,7 @@ import { normalizeValue } from '@tremolo-ui/functions'
5
5
  import { useEventListener } from './useEventListener'
6
6
  import { useRefCallbackEvent } from './useRefCallbackEvent'
7
7
 
8
- export interface UseDragWithElementProps<T extends Element> {
8
+ interface UseDragWithElementProps<T extends Element> {
9
9
  baseElementRef: RefObject<T | null>
10
10
  onDrag: (normalizedX: number, normalizedY: number) => void
11
11
  onDragStart?: (normalizedX: number, normalizedY: number) => void
@@ -13,6 +13,7 @@ export interface UseDragWithElementProps<T extends Element> {
13
13
  }
14
14
 
15
15
  /**
16
+ * @category hooks
16
17
  * @returns [refCallback, pointerDownHandler]
17
18
  */
18
19
  export function useDragWithElement<T extends Element>({