@tremolo-ui/react 0.2.0 → 0.2.1

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 (64) hide show
  1. package/dist/index.cjs +1203 -1036
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.css +176 -159
  4. package/dist/index.d.cts +241 -246
  5. package/dist/index.d.cts.map +1 -1
  6. package/dist/index.d.ts +241 -246
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1187 -1021
  9. package/dist/index.js.map +1 -1
  10. package/package.json +17 -19
  11. package/src/components/AnimationCanvas/index.tsx +0 -7
  12. package/src/components/DragObserver/index.tsx +9 -7
  13. package/src/components/Knob/ActiveLine.tsx +29 -0
  14. package/src/components/Knob/InactiveLine.tsx +53 -0
  15. package/src/components/Knob/SVGRoot.tsx +39 -0
  16. package/src/components/Knob/Thumb.tsx +64 -0
  17. package/src/components/Knob/context.tsx +120 -0
  18. package/src/components/Knob/index.css +5 -1
  19. package/src/components/Knob/index.tsx +110 -164
  20. package/src/components/NumberInput/DecrementStepper.tsx +0 -2
  21. package/src/components/NumberInput/IncrementStepper.tsx +0 -2
  22. package/src/components/NumberInput/InternalInput.tsx +3 -1
  23. package/src/components/NumberInput/Stepper.tsx +0 -2
  24. package/src/components/NumberInput/context.tsx +0 -1
  25. package/src/components/NumberInput/index.tsx +6 -8
  26. package/src/components/Piano/KeyLabel.tsx +0 -2
  27. package/src/components/Piano/context.tsx +0 -2
  28. package/src/components/Piano/index.tsx +7 -11
  29. package/src/components/Piano/key.tsx +0 -10
  30. package/src/components/Piano/keyboardShortcuts.ts +0 -2
  31. package/src/components/PointsEditor/Background.tsx +17 -0
  32. package/src/components/PointsEditor/Container.tsx +26 -0
  33. package/src/components/PointsEditor/Point.tsx +137 -0
  34. package/src/components/PointsEditor/context.tsx +91 -0
  35. package/src/components/PointsEditor/index.css +30 -0
  36. package/src/components/PointsEditor/index.tsx +129 -0
  37. package/src/components/Slider/Scale.tsx +0 -2
  38. package/src/components/Slider/ScaleOption.tsx +0 -2
  39. package/src/components/Slider/Thumb.tsx +0 -4
  40. package/src/components/Slider/Track.tsx +0 -2
  41. package/src/components/Slider/context.tsx +0 -1
  42. package/src/components/Slider/index.tsx +42 -14
  43. package/src/components/WheelObserver/index.tsx +17 -19
  44. package/src/components/XYPad/Area.tsx +0 -2
  45. package/src/components/XYPad/Thumb.tsx +0 -3
  46. package/src/components/XYPad/index.tsx +52 -25
  47. package/src/components/_util/composeRefs.tsx +63 -0
  48. package/src/components/_util/index.ts +23 -6
  49. package/src/components/_util/type.ts +1 -0
  50. package/src/hooks/useAnimationFrame.ts +0 -3
  51. package/src/hooks/useCallbackRef.ts +2 -2
  52. package/src/hooks/useDrag.ts +12 -11
  53. package/src/hooks/useDragWithElement.ts +12 -17
  54. package/src/hooks/useEventListener.ts +6 -9
  55. package/src/hooks/useInterval.ts +0 -3
  56. package/src/hooks/useLongPress.ts +0 -3
  57. package/src/hooks/useMIDIAccess.ts +0 -1
  58. package/src/hooks/useMIDIInput.ts +0 -1
  59. package/src/hooks/useMIDIMessage.ts +0 -1
  60. package/src/hooks/usePianoDrag.ts +75 -0
  61. package/src/index.ts +8 -5
  62. package/src/styles/global.css +17 -1
  63. package/dist/index.css.map +0 -1
  64. package/src/components/AnimationKnob/index.tsx +0 -314
package/dist/index.js CHANGED
@@ -3,7 +3,6 @@ import React, { createContext, createRef, forwardRef, useCallback, useContext, u
3
3
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
4
  import { clamp, decimalPart, isBlackKey, isWhiteKey, normalizeValue, noteKey, noteKeys, radian, rawValue, stepValue, styleHelper, toFixed, xor } from "@tremolo-ui/functions";
5
5
  import { createStore, useStore } from "zustand";
6
-
7
6
  //#region src/components/AnimationCanvas/canvas.ts
8
7
  const drawingState = [
9
8
  "strokeStyle",
@@ -33,10 +32,9 @@ function setDprConfig(canvas, context, width, height, dpr) {
33
32
  canvas.style.width = `${width}px`;
34
33
  canvas.style.height = `${height}px`;
35
34
  }
36
-
37
35
  //#endregion
38
36
  //#region src/components/AnimationCanvas/index.tsx
39
- function AnimationCanvas({ draw, init, animate = true, options, width: _width = 100, height: _height = 100, relativeSize, reduceFlickering = true, className, onContextMenu = (event) => event.preventDefault(),...props }) {
37
+ function AnimationCanvas({ draw, init, animate = true, options, width: _width = 100, height: _height = 100, relativeSize, reduceFlickering = true, className, onContextMenu = (event) => event.preventDefault(), ...props }) {
40
38
  const canvasRef = useRef(null);
41
39
  const memoCanvasRef = useRef(null);
42
40
  const reqIdRef = useRef(-1);
@@ -140,7 +138,6 @@ function AnimationCanvas({ draw, init, animate = true, options, width: _width =
140
138
  ref: memoCanvasRef
141
139
  })] });
142
140
  }
143
-
144
141
  //#endregion
145
142
  //#region src/hooks/useCallbackRef.ts
146
143
  /**
@@ -156,14 +153,13 @@ function useCallbackRef(callback, deps = []) {
156
153
  });
157
154
  return useCallback((...args) => callbackRef.current?.(...args), deps);
158
155
  }
159
-
160
156
  //#endregion
161
157
  //#region src/hooks/useEventListener.ts
162
158
  function useEventListener(target, event, handler, options) {
163
159
  const listener = useCallbackRef(handler);
164
160
  useEffect(() => {
165
161
  const node = typeof target === "function" ? target() : target ?? document;
166
- if (!handler || !node) return;
162
+ if (!node) return;
167
163
  node.addEventListener(event, listener, options);
168
164
  return () => {
169
165
  node.removeEventListener(event, listener, options);
@@ -172,14 +168,12 @@ function useEventListener(target, event, handler, options) {
172
168
  event,
173
169
  target,
174
170
  options,
175
- listener,
176
- handler
171
+ listener
177
172
  ]);
178
173
  return () => {
179
174
  (typeof target === "function" ? target() : target ?? document)?.removeEventListener(event, listener, options);
180
175
  };
181
176
  }
182
-
183
177
  //#endregion
184
178
  //#region src/hooks/useRefCallbackEvent.ts
185
179
  function useRefCallbackEvent(event, handler, options, deps = []) {
@@ -197,18 +191,17 @@ function useRefCallbackEvent(event, handler, options, deps = []) {
197
191
  };
198
192
  }, deps);
199
193
  }
200
-
201
194
  //#endregion
202
195
  //#region src/hooks/useDrag.ts
203
196
  /**
204
- * @category hooks
205
197
  * @returns [refCallback, pointerDownHandler]
206
198
  */
207
- function useDrag({ threshold = 1, onDrag, onDragStart, onDragEnd }) {
199
+ function useDrag({ threshold: _threshold = 1, onDrag, onDragStart, onDragEnd }) {
208
200
  const dragOffsetX = useRef(void 0);
209
201
  const dragOffsetY = useRef(void 0);
210
202
  const dragStartX = useRef(0);
211
203
  const dragStartY = useRef(0);
204
+ const threshold = Math.max(_threshold, 1);
212
205
  const handleDrag = useCallback((event, first = false) => {
213
206
  const isTouch = event instanceof TouchEvent;
214
207
  if (isTouch && event.cancelable) event.preventDefault();
@@ -229,15 +222,15 @@ function useDrag({ threshold = 1, onDrag, onDragStart, onDragEnd }) {
229
222
  dragOffsetY.current = screenY;
230
223
  }
231
224
  if (Math.abs(deltaX) < threshold && Math.abs(deltaY) < threshold) return;
232
- onDrag(screenX - dragStartX.current, screenY - dragStartY.current, deltaX, deltaY);
225
+ onDrag?.(screenX - dragStartX.current, screenY - dragStartY.current, deltaX, deltaY);
233
226
  }, [threshold, onDrag]);
234
- const refHandler = useRefCallbackEvent("touchmove", handleDrag, { passive: false }, [onDrag]);
235
227
  const pointerDownHandler = useCallback((event) => {
236
228
  dragOffsetX.current = event.screenX;
237
229
  dragOffsetY.current = event.screenY;
238
230
  handleDrag(event, true);
239
231
  onDragStart?.();
240
232
  }, [handleDrag, onDragStart]);
233
+ const refHandler = useRefCallbackEvent("touchmove", handleDrag, { passive: false }, [onDrag]);
241
234
  useEventListener(globalThis.window, "mousemove", handleDrag);
242
235
  useEventListener(globalThis.window, "pointerup", () => {
243
236
  dragOffsetX.current = void 0;
@@ -246,168 +239,10 @@ function useDrag({ threshold = 1, onDrag, onDragStart, onDragEnd }) {
246
239
  });
247
240
  return [refHandler, pointerDownHandler];
248
241
  }
249
-
250
- //#endregion
251
- //#region src/components/_util/index.ts
252
- function addNoSelect() {
253
- document.body.classList.add("tremolo-no-select");
254
- }
255
- function removeNoSelect() {
256
- document.body.classList.remove("tremolo-no-select");
257
- }
258
-
259
- //#endregion
260
- //#region src/components/AnimationKnob/index.tsx
261
- /**
262
- * Animatable rotary knob.
263
- * @category AnimationKnob
264
- */
265
- const AnimationKnob = forwardRef(({ value, min, max, defaultValue = min, startValue = min, step = 1, skew = 1, size, width = 50, height = 50, activeColor = "#7998ec", inactiveColor = "#eee", bg = "#ccc", thumb = "#4e76e6", lineWeight = 6, thumbWeight = 4, draw, bodyNoSelect = true, wheel = ["raw", 1], keyboard = ["raw", 1], enableDoubleClickDefault = true, style, className, onChange,...props }, ref) => {
266
- const valueRef = useRef(0);
267
- const elmRef = useRef(null);
268
- const isRelativeSize = typeof (size ?? width) == "string" || typeof height == "string";
269
- const onDrag = useCallback((_x, y) => {
270
- if (!onChange) return;
271
- onChange(clamp(stepValue(rawValue(valueRef.current - y / 100, min, max, skew), step), min, max));
272
- }, [
273
- max,
274
- min,
275
- onChange,
276
- skew,
277
- step
278
- ]);
279
- const handleKeyDown = useCallback((event) => {
280
- if (!keyboard || !onChange) return;
281
- const key = event.key;
282
- if ([
283
- "ArrowRight",
284
- "ArrowLeft",
285
- "ArrowUp",
286
- "ArrowDown"
287
- ].includes(key)) {
288
- event.preventDefault();
289
- const x = key == "ArrowRight" || key == "ArrowUp" ? keyboard[1] : -keyboard[1];
290
- let v;
291
- if (keyboard[0] == "normalized") v = rawValue(normalizeValue(value, min, max, skew) + x, min, max, skew);
292
- else v = value + x;
293
- onChange(clamp(stepValue(v, step), min, max));
294
- }
295
- }, [
296
- value,
297
- min,
298
- max,
299
- step,
300
- skew,
301
- keyboard,
302
- onChange
303
- ]);
304
- const [touchMoveRefCallback, pointerDownHandler] = useDrag({
305
- onDrag,
306
- onDragStart: () => {
307
- valueRef.current = normalizeValue(value, min, max, skew);
308
- if (bodyNoSelect) addNoSelect();
309
- },
310
- onDragEnd: () => {
311
- if (bodyNoSelect) removeNoSelect();
312
- }
313
- });
314
- const wheelRefCallback = useRefCallbackEvent("wheel", (event) => {
315
- if (!wheel) return;
316
- event.preventDefault();
317
- const x = event.deltaY > 0 ? -wheel[1] : wheel[1];
318
- let v;
319
- if (wheel[0] == "normalized") v = rawValue(normalizeValue(value, min, max, skew) + x, min, max, skew);
320
- else v = value + x;
321
- if (onChange) onChange(clamp(stepValue(v, step), min, max));
322
- }, { passive: false }, [
323
- wheel,
324
- value,
325
- min,
326
- max,
327
- skew,
328
- step,
329
- onChange
330
- ]);
331
- useImperativeHandle(ref, () => {
332
- return {
333
- focus() {
334
- elmRef.current?.focus();
335
- },
336
- blur() {
337
- elmRef.current?.blur();
338
- }
339
- };
340
- }, []);
341
- return /* @__PURE__ */ jsx("div", {
342
- className: clsx("tremolo-animation-knob", className),
343
- ref: (div) => {
344
- elmRef.current = div;
345
- wheelRefCallback(div);
346
- touchMoveRefCallback(div);
347
- },
348
- role: "slider",
349
- tabIndex: 0,
350
- "aria-valuenow": value,
351
- "aria-valuemax": max,
352
- "aria-valuemin": min,
353
- style: {
354
- display: "inline-block",
355
- width: size ?? width,
356
- height: size ?? height,
357
- cursor: "pointer",
358
- WebkitTapHighlightColor: "transparent",
359
- ...style
360
- },
361
- onPointerDown: pointerDownHandler,
362
- onDoubleClick: () => {
363
- if (enableDoubleClickDefault && onChange) onChange(defaultValue);
364
- },
365
- onKeyDown: handleKeyDown,
366
- ...props,
367
- children: /* @__PURE__ */ jsx(AnimationCanvas, {
368
- ...isRelativeSize ? { relativeSize: true } : {
369
- width: size ?? width,
370
- height: size ?? height
371
- },
372
- draw: draw ? draw : (ctx, { width: width$1, height: height$1 }) => {
373
- const p = normalizeValue(value, min, max, skew);
374
- const startP = normalizeValue(startValue, min, max, skew);
375
- const cx = width$1 / 2;
376
- const cy = height$1 / 2;
377
- const r = Math.min(cx, cy);
378
- if (r <= 0) return;
379
- ctx.clearRect(0, 0, width$1, height$1);
380
- ctx.lineCap = "butt";
381
- ctx.beginPath();
382
- ctx.fillStyle = bg;
383
- ctx.arc(cx, cy, r - .5, 0, 2 * Math.PI);
384
- ctx.fill();
385
- ctx.lineWidth = lineWeight;
386
- ctx.beginPath();
387
- ctx.strokeStyle = inactiveColor;
388
- ctx.arc(cx, cy, r - .5 - lineWeight / 2, radian(135), radian(45));
389
- ctx.stroke();
390
- ctx.beginPath();
391
- ctx.strokeStyle = activeColor;
392
- ctx.arc(cx, cy, r - .5 - lineWeight / 2, radian(135 + 270 * startP), radian(135 + 270 * p), p < startP);
393
- ctx.stroke();
394
- ctx.lineCap = "round";
395
- ctx.beginPath();
396
- ctx.moveTo(cx, cy);
397
- ctx.lineTo(cx + Math.cos(radian(135 + 270 * p)) * (r - .5 - thumbWeight / 2), cy + Math.sin(radian(135 + 270 * p)) * (r - .5 - thumbWeight / 2));
398
- ctx.strokeStyle = thumb;
399
- ctx.lineWidth = thumbWeight;
400
- ctx.stroke();
401
- }
402
- })
403
- });
404
- });
405
-
406
242
  //#endregion
407
243
  //#region src/components/DragObserver/index.tsx
408
- /** @category DragObserver */
409
244
  function DragObserver(props) {
410
- const { as: Component = "div", threshold = 1, children, onDrag, onDragStart, onDragEnd,...attributes } = props;
245
+ const { as: Component = "div", threshold = 1, onDrag, onDragStart, onDragEnd, children, onPointerDown, ...attributes } = props;
411
246
  const [refCallback, pointerDownHandler] = useDrag({
412
247
  threshold,
413
248
  onDrag,
@@ -416,190 +251,333 @@ function DragObserver(props) {
416
251
  });
417
252
  return /* @__PURE__ */ jsx(Component, {
418
253
  ref: refCallback,
419
- onPointerDown: pointerDownHandler,
254
+ onPointerDown: (e) => {
255
+ pointerDownHandler(e);
256
+ onPointerDown?.(e);
257
+ },
420
258
  ...attributes,
421
259
  children
422
260
  });
423
261
  }
424
-
425
262
  //#endregion
426
- //#region src/components/Knob/index.tsx
427
- /**
428
- * Interactive rotary knob component implemented in SVG.
429
- *
430
- * @category Knob
431
- */
432
- const Knob = forwardRef(({ value, min, max, step = 1, skew = 1, defaultValue = min, startValue = min, size = 50, bodyNoSelect = true, wheel = ["raw", 1], keyboard = ["raw", 1], enableDoubleClickDefault = true, disabled = false, readonly = false, activeLine, inactiveLine, thumb, thumbLine, thumbSize = 84, thumbLineWeight = 6, thumbLineLength = 35, lineWeight = 6, angleRange = 270, onChange, onKeyDown, onPointerDown, onDoubleClick, className, classes,...props }, forwardedRef) => {
433
- const valueRef = useRef(0);
434
- const elmRef = useRef(null);
435
- const onDrag = useCallback((_x, y) => {
436
- if (!onChange || readonly) return;
437
- onChange(clamp(stepValue(rawValue(valueRef.current - y / 100, min, max, skew), step), min, max));
438
- }, [
439
- max,
440
- min,
441
- onChange,
442
- readonly,
443
- skew,
444
- step
445
- ]);
446
- const updateValueByEvent = useCallback((eventType, x) => {
447
- let newValue;
448
- if (eventType == "normalized") newValue = rawValue(normalizeValue(value, min, max, skew) + x, min, max, skew);
449
- else newValue = value + x;
450
- return clamp(stepValue(newValue, step), min, max);
451
- }, [
452
- max,
453
- min,
454
- skew,
455
- step,
456
- value
457
- ]);
458
- const handleKeyDown = useCallback((event) => {
459
- if (!keyboard || !onChange || readonly) return;
460
- const key = event.key;
461
- if ([
462
- "ArrowRight",
463
- "ArrowLeft",
464
- "ArrowUp",
465
- "ArrowDown"
466
- ].includes(key)) {
467
- event.preventDefault();
468
- const x = key == "ArrowRight" || key == "ArrowUp" ? keyboard[1] : -keyboard[1];
469
- onChange(updateValueByEvent(keyboard[0], x));
470
- }
471
- }, [
472
- keyboard,
473
- onChange,
474
- readonly,
475
- updateValueByEvent
476
- ]);
477
- const [touchMoveRefCallback, pointerDownHandler] = useDrag({
478
- onDrag,
479
- onDragStart: () => {
480
- valueRef.current = normalizeValue(value, min, max, skew);
481
- if (bodyNoSelect) addNoSelect();
482
- },
483
- onDragEnd: () => {
484
- if (bodyNoSelect) removeNoSelect();
485
- }
263
+ //#region src/components/_util/index.ts
264
+ function addUserSelectNone() {
265
+ window.document.body.classList.add("tremolo-user-select-none");
266
+ }
267
+ function removeUserSelectNone() {
268
+ window.document.body.classList.remove("tremolo-user-select-none");
269
+ }
270
+ const cursorStyles = {
271
+ grabbing: "tremolo-cursor-grabbing",
272
+ grab: "tremolo-cursor-grab",
273
+ pointer: "tremolo-cursor-pointer",
274
+ move: "tremolo-cursor-move",
275
+ none: "tremolo-cursor-none"
276
+ };
277
+ function setCursorStyle(type) {
278
+ resetCursorStyle();
279
+ window.document.body.classList.add(cursorStyles[type]);
280
+ }
281
+ function resetCursorStyle() {
282
+ Object.values(cursorStyles).forEach((s) => {
283
+ window.document.body.classList.remove(s);
486
284
  });
487
- const wheelRefCallback = useRefCallbackEvent("wheel", (event) => {
488
- if (!wheel || readonly) return;
489
- event.preventDefault();
490
- if (!onChange || event.deltaY == 0) return;
491
- const x = Math.sign(event.deltaY) * -wheel[1];
492
- onChange(updateValueByEvent(wheel[0], x));
493
- }, { passive: false }, [
494
- wheel,
495
- onChange,
496
- readonly,
497
- updateValueByEvent
498
- ]);
499
- useImperativeHandle(forwardedRef, () => {
500
- return {
501
- focus() {
502
- elmRef.current?.focus();
503
- },
504
- blur() {
505
- elmRef.current?.blur();
506
- }
507
- };
508
- }, []);
509
- const viewBoxSize = 100;
510
- const center = viewBoxSize / 2;
511
- const padding = (viewBoxSize - clamp(thumbSize, 0, 100)) / 2;
285
+ }
286
+ function calcDrawingState({ value, min, max, skew, startValue, angleRange }) {
512
287
  const p = normalizeValue(value, min, max, skew);
513
288
  const s = normalizeValue(startValue, min, max, skew);
514
289
  const r1 = -angleRange / 2;
515
290
  const r2 = r1 + Math.min(p, s) * angleRange;
516
291
  const r3 = r1 + Math.max(p, s) * angleRange;
517
292
  const r4 = angleRange / 2;
518
- const x1 = center + center * Math.cos(radian(r1 - 90));
519
- const y1 = center + center * Math.sin(radian(r1 - 90));
520
- const x2 = center + center * Math.cos(radian(r2 - 90));
521
- const y2 = center + center * Math.sin(radian(r2 - 90));
522
- const x3 = center + center * Math.cos(radian(r3 - 90));
523
- const y3 = center + center * Math.sin(radian(r3 - 90));
524
- const x4 = center + center * Math.cos(radian(r4 - 90));
525
- const y4 = center + center * Math.sin(radian(r4 - 90));
293
+ return {
294
+ p,
295
+ r1,
296
+ r2,
297
+ r3,
298
+ r4,
299
+ x1: 50 + 50 * Math.cos(radian(r1 - 90)),
300
+ y1: 50 + 50 * Math.sin(radian(r1 - 90)),
301
+ x2: 50 + 50 * Math.cos(radian(r2 - 90)),
302
+ y2: 50 + 50 * Math.sin(radian(r2 - 90)),
303
+ x3: 50 + 50 * Math.cos(radian(r3 - 90)),
304
+ y3: 50 + 50 * Math.sin(radian(r3 - 90)),
305
+ x4: 50 + 50 * Math.cos(radian(r4 - 90)),
306
+ y4: 50 + 50 * Math.sin(radian(r4 - 90))
307
+ };
308
+ }
309
+ const createKnobStore = (initProps) => {
310
+ return createStore()(() => ({
311
+ ...initProps,
312
+ ...calcDrawingState(initProps)
313
+ }));
314
+ };
315
+ const KnobContext = createContext(null);
316
+ function KnobProvider({ children, ...props }) {
317
+ const storeRef = useRef(null);
318
+ if (!storeRef.current) storeRef.current = createKnobStore(props);
319
+ useEffect(() => {
320
+ if (storeRef.current) storeRef.current.setState({
321
+ ...props,
322
+ ...calcDrawingState(props)
323
+ });
324
+ else storeRef.current = createKnobStore(props);
325
+ }, [props]);
326
+ return /* @__PURE__ */ jsx(KnobContext.Provider, {
327
+ value: storeRef.current,
328
+ children
329
+ });
330
+ }
331
+ function useKnobContext(selector) {
332
+ const store = useContext(KnobContext);
333
+ if (!store) throw new Error("Missing KnobContext.Provider in the tree");
334
+ return useStore(store, selector);
335
+ }
336
+ //#endregion
337
+ //#region src/components/Knob/ActiveLine.tsx
338
+ function ActiveLine({ stroke = "currentColor", strokeWidth = 6, className, ...props }) {
339
+ const r2 = useKnobContext((s) => s.r2);
340
+ const r3 = useKnobContext((s) => s.r3);
341
+ const x2 = useKnobContext((s) => s.x2);
342
+ const y2 = useKnobContext((s) => s.y2);
343
+ const x3 = useKnobContext((s) => s.x3);
344
+ const y3 = useKnobContext((s) => s.y3);
345
+ return /* @__PURE__ */ jsx("path", {
346
+ className: clsx("tremolo-knob-active-line", className),
347
+ d: `M ${x2} ${y2} A 50 50 -135 ${r3 - r2 > 180 ? 1 : 0} 1 ${x3} ${y3}`,
348
+ fill: "none",
349
+ stroke,
350
+ strokeWidth,
351
+ ...props
352
+ });
353
+ }
354
+ //#endregion
355
+ //#region src/components/Knob/InactiveLine.tsx
356
+ function InactiveLine({ stroke = "currentColor", strokeWidth = 6, className, ...props }) {
357
+ const min = useKnobContext((s) => s.min);
358
+ const max = useKnobContext((s) => s.max);
359
+ const startValue = useKnobContext((s) => s.startValue);
360
+ const r1 = useKnobContext((s) => s.r1);
361
+ const r2 = useKnobContext((s) => s.r2);
362
+ const r3 = useKnobContext((s) => s.r3);
363
+ const r4 = useKnobContext((s) => s.r4);
364
+ const x1 = useKnobContext((s) => s.x1);
365
+ const y1 = useKnobContext((s) => s.y1);
366
+ const x2 = useKnobContext((s) => s.x2);
367
+ const y2 = useKnobContext((s) => s.y2);
368
+ const x3 = useKnobContext((s) => s.x3);
369
+ const y3 = useKnobContext((s) => s.y3);
370
+ const x4 = useKnobContext((s) => s.x4);
371
+ const y4 = useKnobContext((s) => s.y4);
372
+ return /* @__PURE__ */ jsxs(Fragment, { children: [startValue > min && /* @__PURE__ */ jsx("path", {
373
+ className: clsx("tremolo-knob-inactive-line", className),
374
+ d: `M ${x1} ${y1} A 50 50 -135 ${r2 - r1 > 180 ? 1 : 0} 1 ${x2} ${y2}`,
375
+ fill: "none",
376
+ stroke,
377
+ strokeWidth,
378
+ ...props
379
+ }), startValue < max && /* @__PURE__ */ jsx("path", {
380
+ className: clsx("tremolo-knob-inactive-line", className),
381
+ d: `M ${x3} ${y3} A 50 50 -135 ${r4 - r3 > 180 ? 1 : 0} 1 ${x4} ${y4}`,
382
+ fill: "none",
383
+ stroke,
384
+ strokeWidth,
385
+ ...props
386
+ })] });
387
+ }
388
+ //#endregion
389
+ //#region src/components/Knob/Thumb.tsx
390
+ function Thumb$2({ className, thumb = "currentColor", thumbLine = "currentColor", thumbSize = 84, thumbLineWeight = 6, thumbLineLength = 35, classes, ...props }) {
391
+ const angleRange = useKnobContext((s) => s.angleRange);
392
+ const p = useKnobContext((s) => s.p);
393
+ const r1 = useKnobContext((s) => s.r1);
526
394
  return /* @__PURE__ */ jsxs("svg", {
527
- ref: (div) => {
528
- elmRef.current = div;
529
- wheelRefCallback(div);
530
- touchMoveRefCallback(div);
531
- },
532
- className: clsx("tremolo-knob", className),
533
- viewBox: `0 0 ${viewBoxSize} ${viewBoxSize}`,
534
- width: size,
535
- height: size,
536
- tabIndex: 0,
537
- role: "slider",
538
- "aria-valuenow": value,
539
- "aria-valuemin": min,
540
- "aria-valuemax": max,
541
- "aria-disabled": disabled,
542
- "aria-readonly": readonly,
543
- onPointerDown: (event) => {
544
- pointerDownHandler(event);
545
- onPointerDown?.(event);
546
- },
547
- onDoubleClick: (event) => {
548
- if (enableDoubleClickDefault && onChange) onChange(defaultValue);
549
- onDoubleClick?.(event);
550
- },
551
- onKeyDown: (event) => {
552
- handleKeyDown(event);
553
- onKeyDown?.(event);
395
+ className: clsx("tremolo-knob-thumb", classes?.thumb),
396
+ ...props,
397
+ children: [/* @__PURE__ */ jsx("circle", {
398
+ cx: "50%",
399
+ cy: "50%",
400
+ r: `${thumbSize / 2}%`,
401
+ fill: thumb
402
+ }), /* @__PURE__ */ jsx("line", {
403
+ className: clsx("tremolo-knob-thumb-line", classes?.thumbLine),
404
+ x1: "50%",
405
+ y1: `${(100 - clamp(thumbSize, 0, 100)) / 2}%`,
406
+ x2: "50%",
407
+ y2: `${thumbLineLength}%`,
408
+ stroke: thumbLine,
409
+ strokeWidth: `${thumbLineWeight}%`,
410
+ style: {
411
+ transform: `rotate(${r1 + p * angleRange}deg)`,
412
+ transformOrigin: "50% 50%"
413
+ }
414
+ })]
415
+ });
416
+ }
417
+ //#endregion
418
+ //#region src/components/Knob/SVGRoot.tsx
419
+ function SVGRoot({ children, style, ...props }) {
420
+ return /* @__PURE__ */ jsx("svg", {
421
+ viewBox: `0 0 100 100`,
422
+ style: {
423
+ display: "block",
424
+ overflow: "visible",
425
+ ...style
554
426
  },
555
427
  ...props,
556
- children: [
557
- startValue > min && /* @__PURE__ */ jsx("path", {
558
- className: clsx("tremolo-knob-inactive-line", classes?.inactiveLine),
559
- d: `M ${x1} ${y1} A ${center} ${center} -135 ${r2 - r1 > 180 ? 1 : 0} 1 ${x2}, ${y2}`,
560
- fill: "none",
561
- stroke: inactiveLine || "currentColor",
562
- strokeWidth: lineWeight
563
- }),
564
- startValue < max && /* @__PURE__ */ jsx("path", {
565
- className: clsx("tremolo-knob-inactive-line", classes?.inactiveLine),
566
- d: `M ${x3} ${y3} A ${center} ${center} -135 ${r4 - r3 > 180 ? 1 : 0} 1 ${x4}, ${y4}`,
567
- fill: "none",
568
- stroke: inactiveLine || "currentColor",
569
- strokeWidth: lineWeight
570
- }),
571
- /* @__PURE__ */ jsx("path", {
572
- className: clsx("tremolo-knob-active-line", classes?.activeLine),
573
- d: `M ${x2} ${y2} A ${center} ${center} -135 ${r3 - r2 > 180 ? 1 : 0} 1 ${x3}, ${y3}`,
574
- fill: "none",
575
- stroke: activeLine || "currentColor",
576
- strokeWidth: lineWeight
577
- }),
578
- /* @__PURE__ */ jsxs("svg", {
579
- className: clsx("tremolo-knob-thumb", classes?.thumb),
580
- children: [/* @__PURE__ */ jsx("circle", {
581
- cx: "50%",
582
- cy: "50%",
583
- r: `${thumbSize / 2}%`,
584
- fill: thumb || "currentColor"
585
- }), /* @__PURE__ */ jsx("line", {
586
- className: clsx("tremolo-knob-thumb-line", classes?.thumbLine),
587
- x1: "50%",
588
- y1: `${padding}%`,
589
- x2: "50%",
590
- y2: `${thumbLineLength}%`,
591
- stroke: thumbLine || "currentColor",
592
- strokeWidth: `${thumbLineWeight}%`,
593
- style: {
594
- transform: `rotate(${r1 + p * angleRange}deg)`,
595
- transformOrigin: "50% 50%"
596
- }
597
- })]
598
- })
599
- ]
428
+ children: children || /* @__PURE__ */ jsxs(Fragment, { children: [
429
+ /* @__PURE__ */ jsx(InactiveLine, {}),
430
+ /* @__PURE__ */ jsx(ActiveLine, {}),
431
+ /* @__PURE__ */ jsx(Thumb$2, {})
432
+ ] })
600
433
  });
601
- });
602
-
434
+ }
435
+ //#endregion
436
+ //#region src/components/Knob/index.tsx
437
+ const defaultExternalStyles$3 = {
438
+ userSelectNone: true,
439
+ cursor: "grabbing"
440
+ };
441
+ /**
442
+ * Interactive rotary knob component implemented in SVG.
443
+ */
444
+ const Knob = {
445
+ Root: forwardRef(({ value, min, max, step = 1, skew = 1, defaultValue = min, startValue = min, size, externalStyles: _externalStyles, wheel = ["raw", 1], keyboard = ["raw", 1], enableDoubleClickDefault = true, disabled = false, readonly = false, angleRange = 270, onChange, onKeyDown, onPointerDown, onDoubleClick, className, style, children, ...props }, forwardedRef) => {
446
+ const [dragging, setDragging] = useState(false);
447
+ const valueRef = useRef(0);
448
+ const elmRef = useRef(null);
449
+ const externalStyles = {
450
+ ...defaultExternalStyles$3,
451
+ ..._externalStyles
452
+ };
453
+ const onDrag = useCallback((_x, y) => {
454
+ if (!onChange || readonly) return;
455
+ onChange(clamp(stepValue(rawValue(valueRef.current - y / 100, min, max, skew), step), min, max));
456
+ }, [
457
+ max,
458
+ min,
459
+ onChange,
460
+ readonly,
461
+ skew,
462
+ step
463
+ ]);
464
+ const updateValueByEvent = useCallback((eventType, x) => {
465
+ let newValue;
466
+ if (eventType == "normalized") newValue = rawValue(normalizeValue(value, min, max, skew) + x, min, max, skew);
467
+ else newValue = value + x;
468
+ return clamp(stepValue(newValue, step), min, max);
469
+ }, [
470
+ max,
471
+ min,
472
+ skew,
473
+ step,
474
+ value
475
+ ]);
476
+ const handleKeyDown = useCallback((event) => {
477
+ if (!keyboard || !onChange || readonly) return;
478
+ const key = event.key;
479
+ if ([
480
+ "ArrowRight",
481
+ "ArrowLeft",
482
+ "ArrowUp",
483
+ "ArrowDown"
484
+ ].includes(key)) {
485
+ event.preventDefault();
486
+ const x = key == "ArrowRight" || key == "ArrowUp" ? keyboard[1] : -keyboard[1];
487
+ onChange(updateValueByEvent(keyboard[0], x));
488
+ }
489
+ }, [
490
+ keyboard,
491
+ onChange,
492
+ readonly,
493
+ updateValueByEvent
494
+ ]);
495
+ const [touchMoveRefCallback, pointerDownHandler] = useDrag({
496
+ onDrag,
497
+ onDragStart: () => {
498
+ setDragging(true);
499
+ valueRef.current = normalizeValue(value, min, max, skew);
500
+ if (externalStyles.userSelectNone) addUserSelectNone();
501
+ if (externalStyles.cursor) setCursorStyle(externalStyles.cursor);
502
+ },
503
+ onDragEnd: () => {
504
+ setDragging(false);
505
+ if (externalStyles.userSelectNone) removeUserSelectNone();
506
+ if (externalStyles.cursor) resetCursorStyle();
507
+ }
508
+ });
509
+ const wheelRefCallback = useRefCallbackEvent("wheel", (event) => {
510
+ if (!wheel || readonly) return;
511
+ event.preventDefault();
512
+ if (!onChange || event.deltaY == 0) return;
513
+ const x = Math.sign(event.deltaY) * -wheel[1];
514
+ onChange(updateValueByEvent(wheel[0], x));
515
+ }, { passive: false }, [
516
+ wheel,
517
+ onChange,
518
+ readonly,
519
+ updateValueByEvent
520
+ ]);
521
+ useImperativeHandle(forwardedRef, () => {
522
+ return {
523
+ focus() {
524
+ elmRef.current?.focus();
525
+ },
526
+ blur() {
527
+ elmRef.current?.blur();
528
+ }
529
+ };
530
+ }, []);
531
+ return /* @__PURE__ */ jsx(KnobProvider, {
532
+ value,
533
+ min,
534
+ max,
535
+ step,
536
+ skew,
537
+ startValue,
538
+ angleRange,
539
+ children: /* @__PURE__ */ jsx("div", {
540
+ ref: (elm) => {
541
+ elmRef.current = elm;
542
+ wheelRefCallback(elm);
543
+ touchMoveRefCallback(elm);
544
+ },
545
+ className: clsx("tremolo-knob", className),
546
+ tabIndex: 0,
547
+ role: "slider",
548
+ "aria-valuenow": value,
549
+ "aria-valuemin": min,
550
+ "aria-valuemax": max,
551
+ "aria-disabled": disabled,
552
+ "aria-readonly": readonly,
553
+ "data-dragging": dragging,
554
+ style: {
555
+ width: size,
556
+ height: size,
557
+ ...style
558
+ },
559
+ onPointerDown: (event) => {
560
+ pointerDownHandler(event);
561
+ onPointerDown?.(event);
562
+ },
563
+ onDoubleClick: (event) => {
564
+ if (enableDoubleClickDefault && onChange) onChange(defaultValue);
565
+ onDoubleClick?.(event);
566
+ },
567
+ onKeyDown: (event) => {
568
+ handleKeyDown(event);
569
+ onKeyDown?.(event);
570
+ },
571
+ ...props,
572
+ children: children || /* @__PURE__ */ jsx(SVGRoot, {})
573
+ })
574
+ });
575
+ }),
576
+ SVGRoot,
577
+ InactiveLine,
578
+ ActiveLine,
579
+ Thumb: Thumb$2
580
+ };
603
581
  //#endregion
604
582
  //#region src/components/NumberInput/type.ts
605
583
  function selectUnit(units, value) {
@@ -610,8 +588,8 @@ function selectUnit(units, value) {
610
588
  function parseValue(inputString, units, digit) {
611
589
  const str = inputString.trim();
612
590
  if (!units || typeof units == "string") {
613
- const m$1 = str.match(/-?\d+(\.\d+)?/);
614
- let v = Number(m$1?.[0] ?? "0");
591
+ const m = str.match(/-?\d+(\.\d+)?/);
592
+ let v = Number(m?.[0] ?? "0");
615
593
  v = isNaN(v) ? 0 : v;
616
594
  return {
617
595
  rawValue: v,
@@ -621,26 +599,25 @@ function parseValue(inputString, units, digit) {
621
599
  }
622
600
  const unitList = units.map(([u, _s]) => u);
623
601
  const scaleList = units.map(([_u, s]) => s);
624
- let rawValue$1 = 0;
602
+ let rawValue = 0;
625
603
  let unit = unitList[0];
626
- let formatValue = (digit != void 0 ? rawValue$1.toFixed(digit) : rawValue$1) + unit;
604
+ let formatValue = (digit != void 0 ? rawValue.toFixed(digit) : rawValue) + unit;
627
605
  const m = str.match(/^(-?\d+(\.\d+)?)\s*(\w*)$/);
628
606
  if (m) {
629
- rawValue$1 = Number(m[1]) || 0;
607
+ rawValue = Number(m[1]) || 0;
630
608
  const uIndex = unitList.indexOf(m[3]);
631
- rawValue$1 *= uIndex != -1 ? scaleList[uIndex] : 1;
632
- const [u, scale] = selectUnit(units, rawValue$1);
633
- const v = rawValue$1 / scale;
609
+ rawValue *= uIndex != -1 ? scaleList[uIndex] : 1;
610
+ const [u, scale] = selectUnit(units, rawValue);
611
+ const v = rawValue / scale;
634
612
  formatValue = (digit != void 0 ? v.toFixed(digit) : v) + u;
635
613
  unit = u;
636
614
  }
637
615
  return {
638
- rawValue: rawValue$1,
616
+ rawValue,
639
617
  formatValue,
640
618
  unit
641
619
  };
642
620
  }
643
-
644
621
  //#endregion
645
622
  //#region src/components/NumberInput/context.tsx
646
623
  function safeClamp(value, min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER) {
@@ -695,7 +672,7 @@ const createNumberInputStore = (initProps) => {
695
672
  }));
696
673
  };
697
674
  const NumberInputContext = createContext(null);
698
- function NumberInputProvider({ children,...props }) {
675
+ function NumberInputProvider({ children, ...props }) {
699
676
  const storeRef = useRef(null);
700
677
  if (!storeRef.current) storeRef.current = createNumberInputStore(props);
701
678
  useEffect(() => {
@@ -713,18 +690,13 @@ function NumberInputProvider({ children,...props }) {
713
690
  children
714
691
  });
715
692
  }
716
- /** @category NumberInput */
717
693
  function useNumberInputContext(selector) {
718
694
  const store = useContext(NumberInputContext);
719
695
  if (!store) throw new Error("Missing NumberInputContext.Provider in the tree");
720
696
  return useStore(store, selector);
721
697
  }
722
-
723
698
  //#endregion
724
699
  //#region src/hooks/useInterval.ts
725
- /**
726
- * @category hooks
727
- */
728
700
  function useInterval(callback, delay) {
729
701
  const savedCallback = useRef(callback);
730
702
  useEffect(() => {
@@ -740,12 +712,8 @@ function useInterval(callback, delay) {
740
712
  };
741
713
  }, [delay]);
742
714
  }
743
-
744
715
  //#endregion
745
716
  //#region src/hooks/useLongPress.ts
746
- /**
747
- * @category hooks
748
- */
749
717
  function useLongPress(callback, initialDelay = 500, interval = 40) {
750
718
  const [pressed, setPressed] = useState(false);
751
719
  const [delay, setDelay] = useState(initialDelay);
@@ -762,11 +730,9 @@ function useLongPress(callback, initialDelay = 500, interval = 40) {
762
730
  setPressed(true);
763
731
  }, [callback]);
764
732
  }
765
-
766
733
  //#endregion
767
734
  //#region src/components/NumberInput/DecrementStepper.tsx
768
- /** @category NumberInput */
769
- function DecrementStepper({ size = 12, children, className,...props }) {
735
+ function DecrementStepper({ size = 12, children, className, ...props }) {
770
736
  const min = useNumberInputContext((s) => s.min);
771
737
  const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
772
738
  const readonly = useNumberInputContext((s) => s.readonly);
@@ -793,11 +759,9 @@ function DecrementStepper({ size = 12, children, className,...props }) {
793
759
  })
794
760
  });
795
761
  }
796
-
797
762
  //#endregion
798
763
  //#region src/components/NumberInput/IncrementStepper.tsx
799
- /** @category NumberInput */
800
- function IncrementStepper({ size = 12, children, className,...props }) {
764
+ function IncrementStepper({ size = 12, children, className, ...props }) {
801
765
  const max = useNumberInputContext((s) => s.max);
802
766
  const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
803
767
  const readonly = useNumberInputContext((s) => s.readonly);
@@ -824,10 +788,9 @@ function IncrementStepper({ size = 12, children, className,...props }) {
824
788
  })
825
789
  });
826
790
  }
827
-
828
791
  //#endregion
829
792
  //#region src/components/NumberInput/InternalInput.tsx
830
- const InternalInput = forwardRef(({ disabled, readonly, selectWithFocus, blurOnEnter, clampValueOnBlur, wheel, keyboard, className, onChange, onFocus, onBlur, onKeyDown, children: _children,...props }, ref) => {
793
+ const InternalInput = forwardRef(({ disabled, readonly, selectWithFocus, blurOnEnter, clampValueOnBlur, wheel, keyboard, className, onChange, onFocus, onBlur, onKeyDown, children: _children, ...props }, ref) => {
831
794
  const elmRef = useRef(null);
832
795
  const [editing, setEditing] = useState(false);
833
796
  const value = useNumberInputContext((s) => s.value);
@@ -851,7 +814,7 @@ const InternalInput = forwardRef(({ disabled, readonly, selectWithFocus, blurOnE
851
814
  const updateValueByEvent = useCallback((eventType, x) => {
852
815
  let newValue;
853
816
  if (eventType == "normalized") {
854
- if (!min || !max) throw new Error("required parameter: min, max");
817
+ if (!min || !max) throw new Error("[NumberInput] \"min\" and \"max\" are required when InputEventOption[0] is set to \"normalized\".");
855
818
  newValue = rawValue(normalizeValue(valueAsNumber, min, max) + x, min, max);
856
819
  return clamp(stepValue(newValue, step), min, max);
857
820
  } else {
@@ -921,8 +884,8 @@ const InternalInput = forwardRef(({ disabled, readonly, selectWithFocus, blurOnE
921
884
  const v = event.currentTarget.value;
922
885
  const cursorPos = event.target.selectionStart;
923
886
  change(v);
924
- const valueAsNumber$1 = parseValue(v, units, digit).rawValue;
925
- onChange?.(valueAsNumber$1, v);
887
+ const valueAsNumber = parseValue(v, units, digit).rawValue;
888
+ onChange?.(valueAsNumber, v);
926
889
  if (cursorPos) {
927
890
  event.target.selectionStart = cursorPos;
928
891
  event.target.selectionEnd = cursorPos;
@@ -953,11 +916,9 @@ const InternalInput = forwardRef(({ disabled, readonly, selectWithFocus, blurOnE
953
916
  ...props
954
917
  });
955
918
  });
956
-
957
919
  //#endregion
958
920
  //#region src/components/NumberInput/Stepper.tsx
959
- /** @category NumberInput */
960
- function Stepper({ dynamic = true, children, className,...props }) {
921
+ function Stepper({ dynamic = true, children, className, ...props }) {
961
922
  return /* @__PURE__ */ jsx("div", {
962
923
  className: clsx("tremolo-number-input-stepper", className),
963
924
  "data-dynamic": dynamic,
@@ -965,62 +926,58 @@ function Stepper({ dynamic = true, children, className,...props }) {
965
926
  children
966
927
  });
967
928
  }
968
-
969
- //#endregion
970
- //#region src/components/NumberInput/index.tsx
971
- const NumberInputImpl = forwardRef(({ value, min, max, step = 1, units, readonly = false, disabled = false, digit, variant = "outline", selectWithFocus = "none", blurOnEnter = true, keepWithinRange = true, clampValueOnBlur = true, wheel = ["raw", 1], keyboard = ["raw", 1], activeColor, wrapperClassName, className, onChange, onFocus, onBlur, children,...props }, forwardedRef) => {
972
- const colors = { "--active-color": activeColor };
973
- return /* @__PURE__ */ jsx(NumberInputProvider, {
974
- value: String(value),
975
- min,
976
- max,
977
- step,
978
- units,
979
- digit,
980
- readonly,
981
- keepWithinRange,
982
- onChange,
983
- children: /* @__PURE__ */ jsxs("div", {
984
- className: clsx("tremolo-number-input-wrapper", wrapperClassName),
985
- tabIndex: 0,
986
- style: { ...colors },
987
- "data-stepper": !!children,
988
- "data-variant": variant,
989
- children: [/* @__PURE__ */ jsx(InternalInput, {
990
- ref: forwardedRef,
991
- readonly,
992
- disabled,
993
- selectWithFocus,
994
- blurOnEnter,
995
- clampValueOnBlur,
996
- wheel,
997
- keyboard,
998
- className,
999
- onChange,
1000
- onFocus,
1001
- onBlur,
1002
- ...props
1003
- }), children]
1004
- })
1005
- });
1006
- });
1007
929
  /**
1008
930
  * Input with some useful functions for entering numerical values.
1009
- * @category NumberInput
1010
931
  */
1011
- const NumberInput = Object.assign(NumberInputImpl, {
932
+ const NumberInput = {
933
+ Root: forwardRef(({ value, min, max, step = 1, units, readonly = false, disabled = false, digit, variant = "outline", selectWithFocus = "none", blurOnEnter = true, keepWithinRange = true, clampValueOnBlur = true, wheel = ["raw", 1], keyboard = ["raw", 1], activeColor, wrapperClassName, className, onChange, onFocus, onBlur, children, ...props }, forwardedRef) => {
934
+ const colors = { "--active-color": activeColor };
935
+ return /* @__PURE__ */ jsx(NumberInputProvider, {
936
+ value: String(value),
937
+ min,
938
+ max,
939
+ step,
940
+ units,
941
+ digit,
942
+ readonly,
943
+ keepWithinRange,
944
+ onChange,
945
+ children: /* @__PURE__ */ jsxs("div", {
946
+ className: clsx("tremolo-number-input-wrapper", wrapperClassName),
947
+ tabIndex: 0,
948
+ style: { ...colors },
949
+ "data-stepper": !!children,
950
+ "data-variant": variant,
951
+ children: [/* @__PURE__ */ jsx(InternalInput, {
952
+ ref: forwardedRef,
953
+ readonly,
954
+ disabled,
955
+ selectWithFocus,
956
+ blurOnEnter,
957
+ clampValueOnBlur,
958
+ wheel,
959
+ keyboard,
960
+ className,
961
+ onChange,
962
+ onFocus,
963
+ onBlur,
964
+ ...props
965
+ }), children]
966
+ })
967
+ });
968
+ }),
1012
969
  Stepper,
1013
970
  IncrementStepper,
1014
971
  DecrementStepper
1015
- });
1016
-
972
+ };
1017
973
  //#endregion
1018
- //#region src/hooks/useDragWithElement.ts
974
+ //#region src/hooks/usePianoDrag.ts
1019
975
  /**
1020
- * @category hooks
976
+ * Internal
1021
977
  * @returns [refCallback, pointerDownHandler]
978
+ * @private
1022
979
  */
1023
- function useDragWithElement({ baseElementRef, onDrag, onDragStart, onDragEnd }) {
980
+ function usePianoDrag({ baseElementRef, onDrag, onDragStart, onDragEnd }) {
1024
981
  const dragged = useRef(false);
1025
982
  const normalizedX = useRef(0);
1026
983
  const normalizedY = useRef(0);
@@ -1049,9 +1006,11 @@ function useDragWithElement({ baseElementRef, onDrag, onDragStart, onDragEnd })
1049
1006
  dragged.current = false;
1050
1007
  onDragEnd?.(normalizedX.current, normalizedY.current);
1051
1008
  });
1052
- return [refHandler, pointerDownHandler];
1009
+ return {
1010
+ refHandler,
1011
+ pointerDownHandler
1012
+ };
1053
1013
  }
1054
-
1055
1014
  //#endregion
1056
1015
  //#region src/components/Piano/context.tsx
1057
1016
  const createPianoStore = (initProps) => {
@@ -1074,7 +1033,7 @@ const createPianoStore = (initProps) => {
1074
1033
  }));
1075
1034
  };
1076
1035
  const PianoContext = createContext(null);
1077
- function PianoProvider({ children,...props }) {
1036
+ function PianoProvider({ children, ...props }) {
1078
1037
  const storeRef = useRef(null);
1079
1038
  if (!storeRef.current) storeRef.current = createPianoStore(props);
1080
1039
  useEffect(() => {
@@ -1086,17 +1045,14 @@ function PianoProvider({ children,...props }) {
1086
1045
  children
1087
1046
  });
1088
1047
  }
1089
- /** @category Piano */
1090
1048
  function usePianoContext(selector) {
1091
1049
  const store = useContext(PianoContext);
1092
1050
  if (!store) throw new Error("Missing PianoContext.Provider in the tree");
1093
1051
  return useStore(store, selector);
1094
1052
  }
1095
-
1096
1053
  //#endregion
1097
1054
  //#region src/components/Piano/KeyLabel.tsx
1098
- /** @category Piano */
1099
- function KeyLabel({ label, className, wrapperClassName, wrapperStyle, __note, __label,...props }) {
1055
+ function KeyLabel({ label, className, wrapperClassName, wrapperStyle, __note, __label, ...props }) {
1100
1056
  const index = getNoteRangeArray(usePianoContext((s) => s.noteRange)).indexOf(__note);
1101
1057
  const content = label ? label(__note, index) : __label && __label(__note, index);
1102
1058
  return (content != void 0 || content != null) && /* @__PURE__ */ jsx("div", {
@@ -1109,14 +1065,7 @@ function KeyLabel({ label, className, wrapperClassName, wrapperStyle, __note, __
1109
1065
  })
1110
1066
  });
1111
1067
  }
1112
-
1113
- //#endregion
1114
- //#region src/components/Piano/key.tsx
1115
- /** @category Piano */
1116
- const defaultWhiteKeyWidth = 40;
1117
- /** @category Piano */
1118
- const defaultBlackKeyWidth = 26;
1119
- const KeyImpl = forwardRef(({ keyType, noteNumber, width, height, bg, color, activeBg, activeColor, className, style, children, __width,...props }, ref) => {
1068
+ const KeyImpl = forwardRef(({ keyType, noteNumber, width, height, bg, color, activeBg, activeColor, className, style, children, __width, ...props }, ref) => {
1120
1069
  const [played, setPlayed] = useState(false);
1121
1070
  const midiMax = usePianoContext((s) => s.midiMax);
1122
1071
  const fill = usePianoContext((s) => s.fill);
@@ -1178,8 +1127,7 @@ const KeyImpl = forwardRef(({ keyType, noteNumber, width, height, bg, color, act
1178
1127
  })
1179
1128
  });
1180
1129
  });
1181
- /** @category Piano */
1182
- const WhiteKey = forwardRef(({ width = defaultWhiteKeyWidth, height = "100%", onContextMenu = (e) => e.preventDefault(),...props }, ref) => {
1130
+ const WhiteKey = forwardRef(({ width = 40, height = "100%", onContextMenu = (e) => e.preventDefault(), ...props }, ref) => {
1183
1131
  return /* @__PURE__ */ jsx(KeyImpl, {
1184
1132
  ref,
1185
1133
  keyType: "white",
@@ -1189,8 +1137,7 @@ const WhiteKey = forwardRef(({ width = defaultWhiteKeyWidth, height = "100%", on
1189
1137
  ...props
1190
1138
  });
1191
1139
  });
1192
- /** @category Piano */
1193
- const BlackKey = forwardRef(({ width = defaultBlackKeyWidth, height = "60%", onContextMenu = (e) => e.preventDefault(),...props }, ref) => {
1140
+ const BlackKey = forwardRef(({ width = 26, height = "60%", onContextMenu = (e) => e.preventDefault(), ...props }, ref) => {
1194
1141
  return /* @__PURE__ */ jsx(KeyImpl, {
1195
1142
  ref,
1196
1143
  keyType: "black",
@@ -1200,10 +1147,8 @@ const BlackKey = forwardRef(({ width = defaultBlackKeyWidth, height = "60%", onC
1200
1147
  ...props
1201
1148
  });
1202
1149
  });
1203
-
1204
1150
  //#endregion
1205
1151
  //#region src/components/Piano/keyboardShortcuts.ts
1206
- /** @category Piano */
1207
1152
  const SHORTCUTS = { HOME_ROW: { keys: [
1208
1153
  "a",
1209
1154
  "w",
@@ -1223,208 +1168,406 @@ const SHORTCUTS = { HOME_ROW: { keys: [
1223
1168
  "p",
1224
1169
  ";"
1225
1170
  ] } };
1226
-
1227
1171
  //#endregion
1228
1172
  //#region src/components/Piano/index.tsx
1229
1173
  /**
1230
1174
  * [noteRange.first, noteRange.first + 1 ..., noteRange.last]
1231
- * @category Piano
1232
1175
  */
1233
1176
  function getNoteRangeArray(noteRange) {
1234
1177
  return Array.from({ length: noteRange.last - noteRange.first + 1 }, (_, i) => i + noteRange.first);
1235
1178
  }
1236
- const blackPerWhiteWidth = defaultBlackKeyWidth / defaultWhiteKeyWidth;
1237
- const PianoImpl = forwardRef(({ noteRange, glissando = true, midiMax = 127, keyboardShortcuts, fill = false, height = fill ? "100%" : 160, whiteNoteWidth: _whiteNoteWidth = defaultWhiteKeyWidth, style, className, onPlayNote, onStopNote, label, children, onPointerDown,...props }, forwardedRef) => {
1238
- const [whiteNoteWidth, setWhiteNoteWidth] = useState(_whiteNoteWidth);
1239
- const keyRefs = useRef([]);
1240
- for (let i = 0; i < noteRange.last - noteRange.first + 1; i++) keyRefs.current[i] = createRef();
1241
- const pianoRef = useRef(null);
1242
- const hitKeyIndex = useRef(-1);
1243
- const noteRangeArray = getNoteRangeArray(noteRange);
1244
- const whiteNotes = noteRangeArray.filter((v) => isWhiteKey(v));
1245
- const blackNotes = noteRangeArray.filter((v) => isBlackKey(v));
1246
- const whiteNoteCount = whiteNotes.length;
1247
- const padding = 1;
1248
- const staticWidth = (whiteNoteWidth + padding) * whiteNoteCount;
1249
- const childrenWithProps = useMemo(() => {
1250
- return children && React.Children.map(children, (child, index) => {
1251
- if (React.isValidElement(child)) {
1252
- const __width = child.type == WhiteKey ? whiteNoteWidth : whiteNoteWidth * .65;
1253
- const props$1 = {
1254
- ref: keyRefs.current[index],
1255
- __width
1179
+ const blackPerWhiteWidth = 26 / 40;
1180
+ /**
1181
+ * Customizable piano component.
1182
+ */
1183
+ const Piano = {
1184
+ Root: forwardRef(({ noteRange, glissando = true, midiMax = 127, keyboardShortcuts, fill = false, height = fill ? "100%" : 160, whiteNoteWidth: _whiteNoteWidth = 40, style, className, onPlayNote, onStopNote, label, children, onPointerDown, ...props }, forwardedRef) => {
1185
+ const [whiteNoteWidth, setWhiteNoteWidth] = useState(_whiteNoteWidth);
1186
+ const keyRefs = useRef([]);
1187
+ for (let i = 0; i < noteRange.last - noteRange.first + 1; i++) keyRefs.current[i] = createRef();
1188
+ const pianoRef = useRef(null);
1189
+ const hitKeyIndex = useRef(-1);
1190
+ const noteRangeArray = getNoteRangeArray(noteRange);
1191
+ const whiteNotes = noteRangeArray.filter((v) => isWhiteKey(v));
1192
+ const blackNotes = noteRangeArray.filter((v) => isBlackKey(v));
1193
+ const whiteNoteCount = whiteNotes.length;
1194
+ const padding = 1;
1195
+ const staticWidth = (whiteNoteWidth + padding) * whiteNoteCount;
1196
+ const childrenWithProps = useMemo(() => {
1197
+ return children && React.Children.map(children, (child, index) => {
1198
+ if (React.isValidElement(child)) {
1199
+ const __width = child.type == WhiteKey ? whiteNoteWidth : whiteNoteWidth * .65;
1200
+ const props = {
1201
+ ref: keyRefs.current[index],
1202
+ __width
1203
+ };
1204
+ return React.cloneElement(child, props);
1205
+ }
1206
+ return child;
1207
+ });
1208
+ }, [children, whiteNoteWidth]);
1209
+ const notePosition = useCallback((note) => {
1210
+ const pitchPositions = {
1211
+ C: 0,
1212
+ "C#": 1,
1213
+ D: 1,
1214
+ "D#": 2,
1215
+ E: 2,
1216
+ F: 3,
1217
+ "F#": 4,
1218
+ G: 4,
1219
+ "G#": 5,
1220
+ A: 5,
1221
+ "A#": 6,
1222
+ B: 6
1223
+ };
1224
+ const blackNoteWidth = whiteNoteWidth * blackPerWhiteWidth;
1225
+ const padding = 1;
1226
+ const targetNoteKey = noteKey(note);
1227
+ const firstNoteKey = noteKey(noteRange.first);
1228
+ const octave = Math.floor((note - noteRange.first) / 12);
1229
+ const octaveOffset = noteKeys.indexOf(firstNoteKey) > noteKeys.indexOf(targetNoteKey) ? 1 : 0;
1230
+ const w = whiteNoteWidth + padding;
1231
+ const pos = pitchPositions[targetNoteKey] - pitchPositions[firstNoteKey];
1232
+ const blackKeyOffset = isBlackKey(note) ? blackNoteWidth / 2 : 0;
1233
+ return pos * w + (octave + octaveOffset) * 7 * w - blackKeyOffset;
1234
+ }, [noteRange.first, whiteNoteWidth]);
1235
+ const getHitKeyIndex = useCallback((x, y) => {
1236
+ if (!pianoRef.current) return -1;
1237
+ const containerHeight = pianoRef.current.clientHeight;
1238
+ const notes = [...blackNotes, ...whiteNotes];
1239
+ for (let i = 0; i < notes.length; i++) {
1240
+ const note = notes[i];
1241
+ const pos = notePosition(note);
1242
+ const w = isWhiteKey(note) ? whiteNoteWidth : whiteNoteWidth * blackPerWhiteWidth;
1243
+ const h = isWhiteKey(note) ? containerHeight : containerHeight * .6;
1244
+ if (pos <= x && x < pos + w && 0 <= y && y < h) return note;
1245
+ }
1246
+ return -1;
1247
+ }, [
1248
+ blackNotes,
1249
+ notePosition,
1250
+ whiteNoteWidth,
1251
+ whiteNotes
1252
+ ]);
1253
+ const onDrag = useCallback((perX, perY) => {
1254
+ if (!pianoRef.current) return;
1255
+ const note = getHitKeyIndex(perX * staticWidth, perY * pianoRef.current.clientHeight);
1256
+ const index = noteRangeArray.indexOf(note);
1257
+ if (index == -1) return;
1258
+ if (hitKeyIndex.current != index) {
1259
+ keyRefs.current[hitKeyIndex.current]?.current?.stop();
1260
+ keyRefs.current[index]?.current?.play();
1261
+ hitKeyIndex.current = index;
1262
+ }
1263
+ }, [
1264
+ getHitKeyIndex,
1265
+ noteRangeArray,
1266
+ staticWidth
1267
+ ]);
1268
+ const onDragEnd = useCallback(() => {
1269
+ if (keyRefs.current[hitKeyIndex.current]?.current?.played()) keyRefs.current[hitKeyIndex.current]?.current?.stop();
1270
+ hitKeyIndex.current = -1;
1271
+ }, [keyRefs]);
1272
+ useEffect(() => {
1273
+ if (fill && pianoRef.current) {
1274
+ const parent = pianoRef.current.parentElement;
1275
+ if (!parent) throw new Error("doesn't have a parent element.");
1276
+ const resizeObserver = new ResizeObserver(() => {
1277
+ const w = pianoRef.current.clientWidth;
1278
+ setWhiteNoteWidth(w / whiteNoteCount - padding);
1279
+ });
1280
+ resizeObserver.observe(parent);
1281
+ return () => {
1282
+ resizeObserver.unobserve(parent);
1256
1283
  };
1257
- return React.cloneElement(child, props$1);
1284
+ } else setWhiteNoteWidth(_whiteNoteWidth);
1285
+ }, [
1286
+ fill,
1287
+ _whiteNoteWidth,
1288
+ whiteNoteCount
1289
+ ]);
1290
+ const { refHandler: touchMoveRefCallback, pointerDownHandler } = usePianoDrag({
1291
+ baseElementRef: pianoRef,
1292
+ onDrag,
1293
+ onDragEnd
1294
+ });
1295
+ useEventListener(globalThis.window, "keydown", (e) => {
1296
+ if (e.repeat) return;
1297
+ if (!keyboardShortcuts) return;
1298
+ const index = keyboardShortcuts.keys.indexOf(e.key);
1299
+ if (index != -1) {
1300
+ if (!keyRefs.current[index]?.current?.played()) keyRefs.current[index]?.current?.play();
1258
1301
  }
1259
- return child;
1260
1302
  });
1261
- }, [children, whiteNoteWidth]);
1262
- const notePosition = useCallback((note) => {
1263
- const pitchPositions = {
1264
- C: 0,
1265
- "C#": 1,
1266
- D: 1,
1267
- "D#": 2,
1268
- E: 2,
1269
- F: 3,
1270
- "F#": 4,
1271
- G: 4,
1272
- "G#": 5,
1273
- A: 5,
1274
- "A#": 6,
1275
- B: 6
1276
- };
1277
- const blackNoteWidth = whiteNoteWidth * blackPerWhiteWidth;
1278
- const padding$1 = 1;
1279
- const targetNoteKey = noteKey(note);
1280
- const firstNoteKey = noteKey(noteRange.first);
1281
- const octave = Math.floor((note - noteRange.first) / 12);
1282
- const octaveOffset = noteKeys.indexOf(firstNoteKey) > noteKeys.indexOf(targetNoteKey) ? 1 : 0;
1283
- const w = whiteNoteWidth + padding$1;
1284
- const pos = pitchPositions[targetNoteKey] - pitchPositions[firstNoteKey];
1285
- const blackKeyOffset = isBlackKey(note) ? blackNoteWidth / 2 : 0;
1286
- return pos * w + (octave + octaveOffset) * 7 * w - blackKeyOffset;
1287
- }, [noteRange.first, whiteNoteWidth]);
1288
- const getHitKeyIndex = useCallback((x, y) => {
1289
- if (!pianoRef.current) return -1;
1290
- const containerHeight = pianoRef.current.clientHeight;
1291
- const notes = [...blackNotes, ...whiteNotes];
1292
- for (let i = 0; i < notes.length; i++) {
1293
- const note = notes[i];
1294
- const pos = notePosition(note);
1295
- const w = isWhiteKey(note) ? whiteNoteWidth : whiteNoteWidth * blackPerWhiteWidth;
1296
- const h = isWhiteKey(note) ? containerHeight : containerHeight * .6;
1297
- if (pos <= x && x < pos + w && 0 <= y && y < h) return note;
1303
+ useEventListener(globalThis.window, "keyup", (e) => {
1304
+ if (e.repeat) return;
1305
+ if (!keyboardShortcuts) return;
1306
+ const index = keyboardShortcuts.keys.indexOf(e.key);
1307
+ if (index != -1) keyRefs.current[index]?.current?.stop();
1308
+ });
1309
+ useImperativeHandle(forwardedRef, () => {
1310
+ return {
1311
+ playNote(note, velocity) {
1312
+ const index = noteRangeArray.indexOf(note);
1313
+ if (index != -1) {
1314
+ if (!keyRefs.current[index]?.current?.played()) keyRefs.current[index]?.current?.play(velocity);
1315
+ } else onPlayNote?.(note, velocity);
1316
+ },
1317
+ stopNote(note) {
1318
+ const index = noteRangeArray.indexOf(note);
1319
+ if (index != -1) keyRefs.current[index]?.current?.stop();
1320
+ else onStopNote?.(note);
1321
+ }
1322
+ };
1323
+ }, [
1324
+ noteRangeArray,
1325
+ onPlayNote,
1326
+ onStopNote
1327
+ ]);
1328
+ return /* @__PURE__ */ jsx(PianoProvider, {
1329
+ notePosition,
1330
+ noteRange,
1331
+ glissando,
1332
+ midiMax,
1333
+ fill,
1334
+ onPlayNote,
1335
+ onStopNote,
1336
+ label,
1337
+ children: /* @__PURE__ */ jsx("div", {
1338
+ ref: (div) => {
1339
+ pianoRef.current = div;
1340
+ touchMoveRefCallback(div);
1341
+ },
1342
+ className: clsx("tremolo-piano", className),
1343
+ style: {
1344
+ width: fill ? "100%" : staticWidth,
1345
+ height,
1346
+ ...style
1347
+ },
1348
+ onPointerDown: (event) => {
1349
+ pointerDownHandler(event);
1350
+ onPointerDown?.(event);
1351
+ },
1352
+ ...props,
1353
+ children: childrenWithProps || noteRangeArray.map((note, index) => isWhiteKey(note) ? /* @__PURE__ */ jsx(WhiteKey, {
1354
+ ref: keyRefs.current[index],
1355
+ noteNumber: note,
1356
+ __width: whiteNoteWidth
1357
+ }, note) : /* @__PURE__ */ jsx(BlackKey, {
1358
+ ref: keyRefs.current[index],
1359
+ noteNumber: note,
1360
+ __width: whiteNoteWidth * blackPerWhiteWidth
1361
+ }, note))
1362
+ })
1363
+ });
1364
+ }),
1365
+ WhiteKey,
1366
+ BlackKey,
1367
+ KeyLabel
1368
+ };
1369
+ //#endregion
1370
+ //#region src/components/PointsEditor/Background.tsx
1371
+ function Background({ className, children, ...props }) {
1372
+ return /* @__PURE__ */ jsx("div", {
1373
+ className: clsx("tremolo-points-editor-background", className),
1374
+ ...props,
1375
+ children
1376
+ });
1377
+ }
1378
+ //#endregion
1379
+ //#region src/components/PointsEditor/context.tsx
1380
+ const createPointsEditorStore = (initProps) => {
1381
+ const DEFAULT_PROPS = {
1382
+ containerElementRef: createRef(),
1383
+ disabled: false,
1384
+ readonly: false,
1385
+ externalStyles: {
1386
+ userSelectNone: true,
1387
+ cursor: "grabbing"
1298
1388
  }
1299
- return -1;
1300
- }, [
1301
- blackNotes,
1302
- notePosition,
1303
- whiteNoteWidth,
1304
- whiteNotes
1305
- ]);
1306
- const onDrag = useCallback((perX, perY) => {
1307
- if (!pianoRef.current) return;
1308
- const note = getHitKeyIndex(perX * staticWidth, perY * pianoRef.current.clientHeight);
1309
- const index = noteRangeArray.indexOf(note);
1310
- if (index == -1) return;
1311
- if (hitKeyIndex.current != index) {
1312
- keyRefs.current[hitKeyIndex.current]?.current?.stop();
1313
- keyRefs.current[index]?.current?.play();
1314
- hitKeyIndex.current = index;
1389
+ };
1390
+ return createStore()((set) => ({
1391
+ ...DEFAULT_PROPS,
1392
+ ...initProps,
1393
+ setContainerElementRef: (containerElementRef) => {
1394
+ set(() => ({ containerElementRef }));
1315
1395
  }
1316
- }, [
1317
- getHitKeyIndex,
1318
- noteRangeArray,
1319
- staticWidth
1320
- ]);
1321
- const onDragEnd = useCallback(() => {
1322
- if (keyRefs.current[hitKeyIndex.current]?.current?.played()) keyRefs.current[hitKeyIndex.current]?.current?.stop();
1323
- hitKeyIndex.current = -1;
1324
- }, [keyRefs]);
1396
+ }));
1397
+ };
1398
+ const PointsEditorContext = createContext(null);
1399
+ function PointsEditorProvider({ children, ...props }) {
1400
+ const storeRef = useRef(null);
1401
+ if (!storeRef.current) storeRef.current = createPointsEditorStore(props);
1325
1402
  useEffect(() => {
1326
- if (fill && pianoRef.current) {
1327
- const parent = pianoRef.current.parentElement;
1328
- if (!parent) throw new Error("doesn't have a parent element.");
1329
- const resizeObserver = new ResizeObserver(() => {
1330
- const w = pianoRef.current.clientWidth;
1331
- setWhiteNoteWidth(w / whiteNoteCount - padding);
1332
- });
1333
- resizeObserver.observe(parent);
1334
- return () => {
1335
- resizeObserver.unobserve(parent);
1336
- };
1337
- } else setWhiteNoteWidth(_whiteNoteWidth);
1403
+ if (storeRef.current) storeRef.current.setState(props);
1404
+ else storeRef.current = createPointsEditorStore(props);
1405
+ }, [props]);
1406
+ return /* @__PURE__ */ jsx(PointsEditorContext.Provider, {
1407
+ value: storeRef.current,
1408
+ children
1409
+ });
1410
+ }
1411
+ function usePointsEditorContext(selector) {
1412
+ const store = useContext(PointsEditorContext);
1413
+ if (!store) throw new Error("Missing PointsEditorContext.Provider in the tree");
1414
+ return useStore(store, selector);
1415
+ }
1416
+ //#endregion
1417
+ //#region src/components/PointsEditor/Container.tsx
1418
+ function Container({ className, children, ...props }) {
1419
+ const containerElementRef = useRef(null);
1420
+ usePointsEditorContext((s) => s.setContainerElementRef)(containerElementRef);
1421
+ return /* @__PURE__ */ jsx("div", {
1422
+ ref: containerElementRef,
1423
+ className: clsx("tremolo-points-editor-container", className),
1424
+ ...props,
1425
+ children
1426
+ });
1427
+ }
1428
+ //#endregion
1429
+ //#region src/hooks/useDragWithElement.ts
1430
+ function useDragWithElement({ baseElementRef, onDrag, onDragStart, onDragEnd }) {
1431
+ const [dragging, setDragging] = useState(false);
1432
+ const normalizedX = useRef(0);
1433
+ const normalizedY = useRef(0);
1434
+ const handleDrag = useCallback((event) => {
1435
+ if (!baseElementRef.current || !dragging) return;
1436
+ const isTouch = event instanceof TouchEvent;
1437
+ if (isTouch && event.cancelable) event.preventDefault();
1438
+ const { left, top, right, bottom } = baseElementRef.current.getBoundingClientRect();
1439
+ const mouseX = isTouch ? event.touches[0].clientX : event.clientX;
1440
+ const mouseY = isTouch ? event.touches[0].clientY : event.clientY;
1441
+ const nx = normalizeValue(mouseX, left, right);
1442
+ const ny = normalizeValue(mouseY, top, bottom);
1443
+ normalizedX.current = nx;
1444
+ normalizedY.current = ny;
1445
+ onDrag(nx, ny);
1338
1446
  }, [
1339
- fill,
1340
- _whiteNoteWidth,
1341
- whiteNoteCount
1342
- ]);
1343
- const [touchMoveRefCallback, pointerDownHandler] = useDragWithElement({
1344
- baseElementRef: pianoRef,
1345
1447
  onDrag,
1346
- onDragEnd
1448
+ baseElementRef,
1449
+ dragging
1450
+ ]);
1451
+ const refHandler = useRefCallbackEvent("touchmove", handleDrag, { passive: false }, [onDrag]);
1452
+ const pointerDownHandler = useCallback((event) => {
1453
+ setDragging(true);
1454
+ handleDrag(event);
1455
+ onDragStart?.(normalizedX.current, normalizedY.current);
1456
+ }, [handleDrag, onDragStart]);
1457
+ useEventListener(globalThis.window, "pointermove", handleDrag);
1458
+ useEventListener(globalThis.window, "pointerup", () => {
1459
+ if (!dragging) return;
1460
+ setDragging(false);
1461
+ onDragEnd?.(normalizedX.current, normalizedY.current);
1347
1462
  });
1348
- useEventListener(globalThis.window, "keydown", (e) => {
1349
- if (e.repeat) return;
1350
- if (!keyboardShortcuts) return;
1351
- const index = keyboardShortcuts.keys.indexOf(e.key);
1352
- if (index != -1) {
1353
- if (!keyRefs.current[index]?.current?.played()) keyRefs.current[index]?.current?.play();
1463
+ return {
1464
+ refHandler,
1465
+ pointerDownHandler,
1466
+ dragging
1467
+ };
1468
+ }
1469
+ //#endregion
1470
+ //#region src/components/PointsEditor/Point.tsx
1471
+ function clampPoint(point, min, max) {
1472
+ const { x, y } = point;
1473
+ return {
1474
+ x: clamp(x, min?.x ?? 0, max?.x ?? 1),
1475
+ y: clamp(y, min?.y ?? 0, max?.y ?? 1)
1476
+ };
1477
+ }
1478
+ function Point({ value, min, max, size, width = 16, height = 16, color, disabled, readonly, onChange, onDragStart, onDragEnd, className, style, onPointerDown, ...props }) {
1479
+ const containerElementRef = usePointsEditorContext((s) => s.containerElementRef);
1480
+ const externalStyles = usePointsEditorContext((s) => s.externalStyles);
1481
+ const __disabled = usePointsEditorContext((s) => s.disabled);
1482
+ const __readonly = usePointsEditorContext((s) => s.readonly);
1483
+ const { refHandler: touchMoveRefCallback, pointerDownHandler, dragging } = useDragWithElement({
1484
+ baseElementRef: containerElementRef,
1485
+ onDrag: (x, y) => {
1486
+ if (readonly) return;
1487
+ onChange?.(clampPoint({
1488
+ x,
1489
+ y
1490
+ }, min, max));
1491
+ },
1492
+ onDragStart: (x, y) => {
1493
+ if (readonly) return;
1494
+ if (externalStyles.userSelectNone) addUserSelectNone();
1495
+ if (externalStyles.cursor) setCursorStyle(externalStyles.cursor);
1496
+ onDragStart?.(clampPoint({
1497
+ x,
1498
+ y
1499
+ }, min, max));
1500
+ },
1501
+ onDragEnd: (x, y) => {
1502
+ if (readonly) return;
1503
+ if (externalStyles.userSelectNone) removeUserSelectNone();
1504
+ if (externalStyles.cursor) resetCursorStyle();
1505
+ onDragEnd?.(clampPoint({
1506
+ x,
1507
+ y
1508
+ }, min, max));
1354
1509
  }
1355
1510
  });
1356
- useEventListener(globalThis.window, "keyup", (e) => {
1357
- if (e.repeat) return;
1358
- if (!keyboardShortcuts) return;
1359
- const index = keyboardShortcuts.keys.indexOf(e.key);
1360
- if (index != -1) keyRefs.current[index]?.current?.stop();
1361
- });
1362
- useImperativeHandle(forwardedRef, () => {
1363
- return {
1364
- playNote(note, velocity) {
1365
- const index = noteRangeArray.indexOf(note);
1366
- if (index != -1) {
1367
- if (!keyRefs.current[index]?.current?.played()) keyRefs.current[index]?.current?.play(velocity);
1368
- } else onPlayNote?.(note, velocity);
1369
- },
1370
- stopNote(note) {
1371
- const index = noteRangeArray.indexOf(note);
1372
- if (index != -1) keyRefs.current[index]?.current?.stop();
1373
- else onStopNote?.(note);
1374
- }
1375
- };
1376
- }, [
1377
- noteRangeArray,
1378
- onPlayNote,
1379
- onStopNote
1380
- ]);
1381
- return /* @__PURE__ */ jsx(PianoProvider, {
1382
- notePosition,
1383
- noteRange,
1384
- glissando,
1385
- midiMax,
1386
- fill,
1387
- onPlayNote,
1388
- onStopNote,
1389
- label,
1390
- children: /* @__PURE__ */ jsx("div", {
1391
- ref: (div) => {
1392
- pianoRef.current = div;
1393
- touchMoveRefCallback(div);
1394
- },
1395
- className: clsx("tremolo-piano", className),
1396
- style: {
1397
- width: fill ? "100%" : staticWidth,
1398
- height,
1399
- ...style
1400
- },
1401
- onPointerDown: (event) => {
1402
- pointerDownHandler(event);
1403
- onPointerDown?.(event);
1404
- },
1405
- ...props,
1406
- children: childrenWithProps || noteRangeArray.map((note, index) => isWhiteKey(note) ? /* @__PURE__ */ jsx(WhiteKey, {
1407
- ref: keyRefs.current[index],
1408
- noteNumber: note,
1409
- __width: whiteNoteWidth
1410
- }, note) : /* @__PURE__ */ jsx(BlackKey, {
1411
- ref: keyRefs.current[index],
1412
- noteNumber: note,
1413
- __width: whiteNoteWidth * blackPerWhiteWidth
1414
- }, note))
1415
- })
1511
+ const colors = { "--color": color };
1512
+ return /* @__PURE__ */ jsx("div", {
1513
+ ref: (div) => {
1514
+ touchMoveRefCallback(div);
1515
+ },
1516
+ className: clsx("tremolo-points-editor-point", className),
1517
+ tabIndex: 0,
1518
+ "aria-disabled": disabled != void 0 ? disabled : __disabled,
1519
+ "aria-readonly": readonly != void 0 ? readonly : __readonly,
1520
+ "data-dragging": dragging,
1521
+ style: {
1522
+ ...colors,
1523
+ width: size ?? width,
1524
+ height: size ?? height,
1525
+ left: `${value.x * 100}%`,
1526
+ top: `${value.y * 100}%`,
1527
+ ...style
1528
+ },
1529
+ onPointerDown: (event) => {
1530
+ pointerDownHandler(event);
1531
+ onPointerDown?.(event);
1532
+ },
1533
+ ...props
1416
1534
  });
1417
- });
1535
+ }
1536
+ //#endregion
1537
+ //#region src/components/PointsEditor/index.tsx
1538
+ const defaultExternalStyles$2 = {
1539
+ userSelectNone: true,
1540
+ cursor: "grabbing"
1541
+ };
1418
1542
  /**
1419
- * Customizable piano component.
1420
- * @category Piano
1543
+ * Multiple Point Controller
1421
1544
  */
1422
- const Piano = Object.assign(PianoImpl, {
1423
- WhiteKey,
1424
- BlackKey,
1425
- KeyLabel
1426
- });
1427
-
1545
+ const PointsEditor = {
1546
+ Root: forwardRef(({ width = 200, height = 100, disabled = false, readonly = false, externalStyles: _externalStyles, style, className, children, ...props }, forwardedRef) => {
1547
+ return /* @__PURE__ */ jsx(PointsEditorProvider, {
1548
+ disabled,
1549
+ readonly,
1550
+ externalStyles: {
1551
+ ...defaultExternalStyles$2,
1552
+ ..._externalStyles
1553
+ },
1554
+ children: /* @__PURE__ */ jsx("div", {
1555
+ ref: forwardedRef,
1556
+ className: clsx("tremolo-points-editor", className),
1557
+ style: {
1558
+ width,
1559
+ height,
1560
+ ...style
1561
+ },
1562
+ ...props,
1563
+ children
1564
+ })
1565
+ });
1566
+ }),
1567
+ Background,
1568
+ Container,
1569
+ Point
1570
+ };
1428
1571
  //#endregion
1429
1572
  //#region src/components/Slider/context.tsx
1430
1573
  const createSliderStore = (initProps) => {
@@ -1444,7 +1587,7 @@ const createSliderStore = (initProps) => {
1444
1587
  }));
1445
1588
  };
1446
1589
  const SliderContext = createContext(null);
1447
- function SliderProvider({ children,...props }) {
1590
+ function SliderProvider({ children, ...props }) {
1448
1591
  const storeRef = useRef(null);
1449
1592
  if (!storeRef.current) storeRef.current = createSliderStore(props);
1450
1593
  useEffect(() => {
@@ -1456,24 +1599,21 @@ function SliderProvider({ children,...props }) {
1456
1599
  children
1457
1600
  });
1458
1601
  }
1459
- /** @category Slider */
1460
1602
  function useSliderContext(selector) {
1461
1603
  const store = useContext(SliderContext);
1462
1604
  if (!store) throw new Error("Missing SliderContext.Provider in the tree");
1463
1605
  return useStore(store, selector);
1464
1606
  }
1465
-
1466
1607
  //#endregion
1467
1608
  //#region src/components/Slider/ScaleOption.tsx
1468
- /** @category Slider */
1469
- function ScaleOption({ value, type = "mark-number", label, thickness = 1, length = "0.5rem", gap = 2, labelWidth, classes, styles, className, style,...props }) {
1609
+ function ScaleOption({ value, type = "mark-number", label, thickness = 1, length = "0.5rem", gap = 2, labelWidth, classes, styles, className, style, ...props }) {
1470
1610
  const min = useSliderContext((s) => s.min);
1471
1611
  const max = useSliderContext((s) => s.max);
1472
1612
  const skew = useSliderContext((s) => s.skew);
1473
1613
  const vertical = useSliderContext((s) => s.vertical);
1474
1614
  const reverse = useSliderContext((s) => s.reverse);
1475
- const calcPercent = useCallback((rawValue$1) => {
1476
- return toFixed(xor(vertical, reverse) ? 100 - normalizeValue(rawValue$1, min, max, skew) * 100 : normalizeValue(rawValue$1, min, max, skew) * 100);
1615
+ const calcPercent = useCallback((rawValue) => {
1616
+ return toFixed(xor(vertical, reverse) ? 100 - normalizeValue(rawValue, min, max, skew) * 100 : normalizeValue(rawValue, min, max, skew) * 100);
1477
1617
  }, [
1478
1618
  vertical,
1479
1619
  reverse,
@@ -1510,7 +1650,6 @@ function ScaleOption({ value, type = "mark-number", label, thickness = 1, length
1510
1650
  })]
1511
1651
  });
1512
1652
  }
1513
-
1514
1653
  //#endregion
1515
1654
  //#region src/components/Slider/type.ts
1516
1655
  function generateOptionsList(options, min, max, step) {
@@ -1526,11 +1665,9 @@ function generateOptionsList(options, min, max, step) {
1526
1665
  }
1527
1666
  return optionsList;
1528
1667
  }
1529
-
1530
1668
  //#endregion
1531
1669
  //#region src/components/Slider/Scale.tsx
1532
- /** @category Slider */
1533
- function Scale({ gap = 6, options, children, className, style,...props }) {
1670
+ function Scale({ gap = 6, options, children, className, style, ...props }) {
1534
1671
  const min = useSliderContext((s) => s.min);
1535
1672
  const max = useSliderContext((s) => s.max);
1536
1673
  const step = useSliderContext((s) => s.step);
@@ -1554,13 +1691,7 @@ function Scale({ gap = 6, options, children, className, style,...props }) {
1554
1691
  }) : children
1555
1692
  });
1556
1693
  }
1557
-
1558
- //#endregion
1559
- //#region src/components/Slider/Thumb.tsx
1560
- /** @category Slider */
1561
- const defaultThumbSize = 22;
1562
- /** @category Slider */
1563
- const Thumb$1 = forwardRef(({ size, width = defaultThumbSize, height = defaultThumbSize, color, children, className, style, __percent }, ref) => {
1694
+ const Thumb$1 = forwardRef(({ size, width = 22, height = 22, color, children, className, style, __percent }, ref) => {
1564
1695
  const wrapperRef = useRef(null);
1565
1696
  const vertical = useSliderContext((s) => s.vertical);
1566
1697
  const disabled = useSliderContext((s) => s.disabled);
@@ -1596,13 +1727,7 @@ const Thumb$1 = forwardRef(({ size, width = defaultThumbSize, height = defaultTh
1596
1727
  })
1597
1728
  });
1598
1729
  });
1599
-
1600
- //#endregion
1601
- //#region src/components/Slider/Track.tsx
1602
- const defaultLength = 140;
1603
- const defaultThickness = 10;
1604
- /** @category Slider */
1605
- function Track({ length = defaultLength, thickness = defaultThickness, active, inactive, children, className, style, defaultStyle = true, __thumb, __percent = 0,...props }) {
1730
+ function Track({ length = 140, thickness = 10, active, inactive, children, className, style, defaultStyle = true, __thumb, __percent = 0, ...props }) {
1606
1731
  const vertical = useSliderContext((s) => s.vertical);
1607
1732
  const reverse = useSliderContext((s) => s.reverse);
1608
1733
  const disabled = useSliderContext((s) => s.disabled);
@@ -1627,213 +1752,259 @@ function Track({ length = defaultLength, thickness = defaultThickness, active, i
1627
1752
  children: [children, __thumb]
1628
1753
  });
1629
1754
  }
1630
-
1631
1755
  //#endregion
1632
1756
  //#region src/components/Slider/index.tsx
1633
- const SliderImpl = forwardRef(({ value, min, max, step = 1, skew = 1, vertical = false, reverse = false, bodyNoSelect = true, wheel = ["raw", 1], keyboard = ["raw", 1], disabled = false, readonly = false, onChange, onDragStart, onDragEnd, className, style, children, onFocus, onBlur, onPointerDown, onKeyDown,...props }, forwardedRef) => {
1634
- const trackElementRef = useRef(null);
1635
- const thumbRef = useRef(null);
1636
- const p = toFixed(normalizeValue(value, min, max, skew) * 100);
1637
- const rev = toFixed(100 - p);
1638
- const displayReversed = useMemo(() => xor(vertical, reverse), [vertical, reverse]);
1639
- const percent = displayReversed ? rev : p;
1640
- let trackProps = {};
1641
- let thumbProps = {};
1642
- let scaleComponent = void 0;
1643
- if (children != void 0) React.Children.forEach(children, (child) => {
1644
- if (React.isValidElement(child)) if (child.type == Thumb$1) thumbProps = child.props;
1645
- else if (child.type == Track) trackProps = child.props;
1646
- else if (child.type == Scale) scaleComponent = child;
1647
- else throw new Error("only <SliderThumb> or <SliderTrack>");
1648
- else throw new Error("children is an invalid element.");
1649
- });
1650
- const onDrag = useCallback((nx, ny) => {
1651
- if (!onChange || readonly) return;
1652
- const n = vertical ? ny : nx;
1653
- onChange(clamp(stepValue(rawValue(displayReversed ? 1 - n : n, min, max, skew), step), min, max));
1654
- }, [
1655
- displayReversed,
1656
- max,
1657
- min,
1658
- onChange,
1659
- readonly,
1660
- skew,
1661
- step,
1662
- vertical
1663
- ]);
1664
- const updateValueByEvent = useCallback((eventType, x) => {
1665
- let newValue;
1666
- if (eventType == "normalized") newValue = rawValue(normalizeValue(value, min, max, skew) + x, min, max, skew);
1667
- else newValue = value + x;
1668
- return clamp(stepValue(newValue, step), min, max);
1669
- }, [
1670
- max,
1671
- min,
1672
- skew,
1673
- step,
1674
- value
1675
- ]);
1676
- const handleKeyDown = useCallback((event) => {
1677
- if (!keyboard || !onChange || readonly) return;
1678
- const key = event.key;
1679
- if ([
1680
- "ArrowRight",
1681
- "ArrowLeft",
1682
- "ArrowUp",
1683
- "ArrowDown"
1684
- ].includes(key)) {
1685
- event.preventDefault();
1686
- let x = key == "ArrowRight" || key == "ArrowUp" ? keyboard[1] : -keyboard[1];
1687
- if (reverse) x *= -1;
1688
- onChange(updateValueByEvent(keyboard[0], x));
1689
- }
1690
- }, [
1691
- keyboard,
1692
- onChange,
1693
- readonly,
1694
- reverse,
1695
- updateValueByEvent
1696
- ]);
1697
- const [touchMoveRefCallback, pointerDownHandler] = useDragWithElement({
1698
- baseElementRef: trackElementRef,
1699
- onDrag,
1700
- onDragStart: (nx, ny) => {
1701
- if (readonly) return;
1702
- if (bodyNoSelect) addNoSelect();
1703
- thumbRef.current?.focus();
1704
- onDragStart?.(clamp(stepValue(rawValue(vertical ? ny : nx, min, max, skew), step), min, max));
1705
- },
1706
- onDragEnd: (nx, ny) => {
1707
- if (readonly) return;
1708
- if (bodyNoSelect) removeNoSelect();
1709
- onDragEnd?.(clamp(stepValue(rawValue(vertical ? ny : nx, min, max, skew), step), min, max));
1710
- }
1711
- });
1712
- const wheelRefCallback = useRefCallbackEvent("wheel", (event) => {
1713
- if (!wheel || !onChange || readonly) return;
1714
- event.preventDefault();
1715
- let x;
1716
- if (!vertical && event.deltaX != 0) x = event.deltaX > 0 ? wheel[1] : -wheel[1];
1717
- else {
1718
- if (event.deltaY == 0) return;
1719
- x = event.deltaY > 0 ? -wheel[1] : wheel[1];
1720
- }
1721
- if (vertical && reverse) x *= -1;
1722
- onChange(updateValueByEvent(wheel[0], x));
1723
- }, { passive: false }, [
1724
- wheel,
1725
- vertical,
1726
- readonly,
1727
- onChange,
1728
- updateValueByEvent
1729
- ]);
1730
- useImperativeHandle(forwardedRef, () => {
1731
- return {
1732
- focus() {
1733
- thumbRef.current?.focus();
1734
- },
1735
- blur() {
1736
- thumbRef.current?.blur();
1737
- }
1757
+ const defaultExternalStyles$1 = {
1758
+ userSelectNone: true,
1759
+ cursor: "pointer"
1760
+ };
1761
+ /**
1762
+ * Customizable slider
1763
+ */
1764
+ const Slider = {
1765
+ Root: forwardRef(({ value, min, max, step = 1, skew = 1, vertical = false, reverse = false, externalStyles: _externalStyles, wheel = ["raw", 1], keyboard = ["raw", 1], disabled = false, readonly = false, onChange, onDragStart, onDragEnd, className, style, children, onFocus, onBlur, onPointerDown, onKeyDown, ...props }, forwardedRef) => {
1766
+ const trackElementRef = useRef(null);
1767
+ const thumbRef = useRef(null);
1768
+ const externalStyles = {
1769
+ ...defaultExternalStyles$1,
1770
+ ..._externalStyles
1738
1771
  };
1739
- }, []);
1740
- return /* @__PURE__ */ jsx(SliderProvider, {
1741
- min,
1742
- max,
1743
- step,
1744
- skew,
1745
- vertical,
1746
- reverse,
1747
- disabled,
1748
- readonly,
1749
- children: /* @__PURE__ */ jsx("div", {
1750
- className: clsx("tremolo-slider", className),
1751
- ref: (div) => {
1752
- wheelRefCallback(div);
1753
- touchMoveRefCallback(div);
1754
- },
1755
- tabIndex: -1,
1756
- role: "slider",
1757
- "aria-valuenow": value,
1758
- "aria-valuemin": min,
1759
- "aria-valuemax": max,
1760
- "aria-orientation": vertical ? "vertical" : "horizontal",
1761
- "aria-disabled": disabled,
1762
- "aria-readonly": readonly,
1763
- style: {
1764
- margin: `calc(${styleHelper(thumbProps?.size ?? 22)} / 2)`,
1765
- ...style
1766
- },
1767
- onPointerDown: (event) => {
1768
- pointerDownHandler(event);
1769
- onPointerDown?.(event);
1770
- },
1771
- onKeyDown: (event) => {
1772
- handleKeyDown(event);
1773
- onKeyDown?.(event);
1774
- },
1775
- onFocus: (event) => {
1772
+ const p = toFixed(normalizeValue(value, min, max, skew) * 100);
1773
+ const rev = toFixed(100 - p);
1774
+ const displayReversed = useMemo(() => xor(vertical, reverse), [vertical, reverse]);
1775
+ const percent = displayReversed ? rev : p;
1776
+ let trackProps = {};
1777
+ let thumbProps = {};
1778
+ let scaleComponent = void 0;
1779
+ if (children != void 0) React.Children.forEach(children, (child) => {
1780
+ if (React.isValidElement(child)) if (child.type == Thumb$1) thumbProps = child.props;
1781
+ else if (child.type == Track) trackProps = child.props;
1782
+ else if (child.type == Scale) scaleComponent = child;
1783
+ else throw new Error("only <SliderThumb> or <SliderTrack>");
1784
+ else throw new Error("children is an invalid element.");
1785
+ });
1786
+ const onDrag = useCallback((nx, ny) => {
1787
+ if (!onChange || readonly) return;
1788
+ const n = vertical ? ny : nx;
1789
+ onChange(clamp(stepValue(rawValue(displayReversed ? 1 - n : n, min, max, skew), step), min, max));
1790
+ }, [
1791
+ displayReversed,
1792
+ max,
1793
+ min,
1794
+ onChange,
1795
+ readonly,
1796
+ skew,
1797
+ step,
1798
+ vertical
1799
+ ]);
1800
+ const updateValueByEvent = useCallback((eventType, x) => {
1801
+ let newValue;
1802
+ if (eventType == "normalized") newValue = rawValue(normalizeValue(value, min, max, skew) + x, min, max, skew);
1803
+ else newValue = value + x;
1804
+ return clamp(stepValue(newValue, step), min, max);
1805
+ }, [
1806
+ max,
1807
+ min,
1808
+ skew,
1809
+ step,
1810
+ value
1811
+ ]);
1812
+ const handleKeyDown = useCallback((event) => {
1813
+ if (!keyboard || !onChange || readonly) return;
1814
+ const key = event.key;
1815
+ if ([
1816
+ "ArrowRight",
1817
+ "ArrowLeft",
1818
+ "ArrowUp",
1819
+ "ArrowDown"
1820
+ ].includes(key)) {
1821
+ event.preventDefault();
1822
+ let x = key == "ArrowRight" || key == "ArrowUp" ? keyboard[1] : -keyboard[1];
1823
+ if (reverse) x *= -1;
1824
+ onChange(updateValueByEvent(keyboard[0], x));
1825
+ }
1826
+ }, [
1827
+ keyboard,
1828
+ onChange,
1829
+ readonly,
1830
+ reverse,
1831
+ updateValueByEvent
1832
+ ]);
1833
+ const { refHandler: touchMoveRefCallback, pointerDownHandler } = useDragWithElement({
1834
+ baseElementRef: trackElementRef,
1835
+ onDrag,
1836
+ onDragStart: (nx, ny) => {
1837
+ if (readonly) return;
1838
+ if (externalStyles.userSelectNone) addUserSelectNone();
1839
+ if (externalStyles.cursor) setCursorStyle(externalStyles.cursor);
1776
1840
  thumbRef.current?.focus();
1777
- onFocus?.(event);
1841
+ onDragStart?.(clamp(stepValue(rawValue(vertical ? ny : nx, min, max, skew), step), min, max));
1778
1842
  },
1779
- onBlur: (event) => {
1780
- thumbRef.current?.blur();
1781
- onBlur?.(event);
1782
- },
1783
- ...props,
1784
- children: /* @__PURE__ */ jsxs("div", {
1843
+ onDragEnd: (nx, ny) => {
1844
+ if (readonly) return;
1845
+ if (externalStyles.userSelectNone) removeUserSelectNone();
1846
+ if (externalStyles.cursor) resetCursorStyle();
1847
+ onDragEnd?.(clamp(stepValue(rawValue(vertical ? ny : nx, min, max, skew), step), min, max));
1848
+ }
1849
+ });
1850
+ const wheelRefCallback = useRefCallbackEvent("wheel", (event) => {
1851
+ if (!wheel || !onChange || readonly) return;
1852
+ event.preventDefault();
1853
+ let x;
1854
+ if (!vertical && event.deltaX != 0) x = event.deltaX > 0 ? wheel[1] : -wheel[1];
1855
+ else {
1856
+ if (event.deltaY == 0) return;
1857
+ x = event.deltaY > 0 ? -wheel[1] : wheel[1];
1858
+ }
1859
+ if (vertical && reverse) x *= -1;
1860
+ onChange(updateValueByEvent(wheel[0], x));
1861
+ }, { passive: false }, [
1862
+ wheel,
1863
+ vertical,
1864
+ readonly,
1865
+ onChange,
1866
+ updateValueByEvent
1867
+ ]);
1868
+ useImperativeHandle(forwardedRef, () => {
1869
+ return {
1870
+ focus() {
1871
+ thumbRef.current?.focus();
1872
+ },
1873
+ blur() {
1874
+ thumbRef.current?.blur();
1875
+ }
1876
+ };
1877
+ }, []);
1878
+ return /* @__PURE__ */ jsx(SliderProvider, {
1879
+ min,
1880
+ max,
1881
+ step,
1882
+ skew,
1883
+ vertical,
1884
+ reverse,
1885
+ disabled,
1886
+ readonly,
1887
+ children: /* @__PURE__ */ jsx("div", {
1888
+ className: clsx("tremolo-slider", className),
1889
+ ref: (div) => {
1890
+ wheelRefCallback(div);
1891
+ touchMoveRefCallback(div);
1892
+ },
1893
+ tabIndex: -1,
1894
+ role: "slider",
1895
+ "aria-valuenow": value,
1896
+ "aria-valuemin": min,
1897
+ "aria-valuemax": max,
1898
+ "aria-orientation": vertical ? "vertical" : "horizontal",
1899
+ "aria-disabled": disabled,
1900
+ "aria-readonly": readonly,
1785
1901
  style: {
1786
- display: "flex",
1787
- flexDirection: vertical ? "row" : "column",
1788
- width: !vertical ? "100%" : void 0,
1789
- height: vertical ? "100%" : void 0
1902
+ margin: `calc(${styleHelper(thumbProps?.size ?? 22)} / 2)`,
1903
+ ...style
1904
+ },
1905
+ onPointerDown: (event) => {
1906
+ pointerDownHandler(event);
1907
+ onPointerDown?.(event);
1908
+ },
1909
+ onKeyDown: (event) => {
1910
+ handleKeyDown(event);
1911
+ onKeyDown?.(event);
1790
1912
  },
1791
- children: [/* @__PURE__ */ jsx("div", {
1792
- className: "tremolo-slider-track-wrapper",
1793
- ref: trackElementRef,
1794
- children: /* @__PURE__ */ jsx(Track, {
1795
- __percent: percent,
1796
- __thumb: /* @__PURE__ */ jsx(Thumb$1, {
1797
- ref: thumbRef,
1913
+ onFocus: (event) => {
1914
+ thumbRef.current?.focus();
1915
+ onFocus?.(event);
1916
+ },
1917
+ onBlur: (event) => {
1918
+ thumbRef.current?.blur();
1919
+ onBlur?.(event);
1920
+ },
1921
+ ...props,
1922
+ children: /* @__PURE__ */ jsxs("div", {
1923
+ style: {
1924
+ display: "flex",
1925
+ flexDirection: vertical ? "row" : "column",
1926
+ width: !vertical ? "100%" : void 0,
1927
+ height: vertical ? "100%" : void 0
1928
+ },
1929
+ children: [/* @__PURE__ */ jsx("div", {
1930
+ className: "tremolo-slider-track-wrapper",
1931
+ ref: trackElementRef,
1932
+ children: /* @__PURE__ */ jsx(Track, {
1798
1933
  __percent: percent,
1799
- ...thumbProps
1800
- }),
1801
- ...trackProps
1802
- })
1803
- }), scaleComponent]
1934
+ __thumb: /* @__PURE__ */ jsx(Thumb$1, {
1935
+ ref: thumbRef,
1936
+ __percent: percent,
1937
+ ...thumbProps
1938
+ }),
1939
+ ...trackProps
1940
+ })
1941
+ }), scaleComponent]
1942
+ })
1804
1943
  })
1805
- })
1806
- });
1807
- });
1808
- /**
1809
- * Customizable slider
1810
- * @category Slider
1811
- */
1812
- const Slider = Object.assign(SliderImpl, {
1944
+ });
1945
+ }),
1813
1946
  Thumb: Thumb$1,
1814
1947
  Track,
1815
1948
  Scale,
1816
1949
  ScaleOption
1817
- });
1818
-
1950
+ };
1951
+ //#endregion
1952
+ //#region src/components/_util/composeRefs.tsx
1953
+ /**
1954
+ * Set a given ref to a given value
1955
+ * This utility takes care of different types of refs: callback refs and RefObject(s)
1956
+ */
1957
+ function setRef(ref, value) {
1958
+ if (typeof ref === "function") return ref(value);
1959
+ else if (ref !== null && ref !== void 0) ref.current = value;
1960
+ }
1961
+ /**
1962
+ * A utility to compose multiple refs together
1963
+ * Accepts callback refs and RefObject(s)
1964
+ */
1965
+ function composeRefs(...refs) {
1966
+ return (node) => {
1967
+ let hasCleanup = false;
1968
+ const cleanups = refs.map((ref) => {
1969
+ const cleanup = setRef(ref, node);
1970
+ if (!hasCleanup && typeof cleanup == "function") hasCleanup = true;
1971
+ return cleanup;
1972
+ });
1973
+ if (hasCleanup) return () => {
1974
+ for (let i = 0; i < cleanups.length; i++) {
1975
+ const cleanup = cleanups[i];
1976
+ if (typeof cleanup == "function") cleanup();
1977
+ else setRef(refs[i], null);
1978
+ }
1979
+ };
1980
+ };
1981
+ }
1819
1982
  //#endregion
1820
1983
  //#region src/components/WheelObserver/index.tsx
1821
- /** @category WheelObserver */
1984
+ /**
1985
+ * @example
1986
+ * <WheelObserver
1987
+ * onWheel={(event) => {
1988
+ * event.preventDefault()
1989
+ * console.log(event.deltaY)
1990
+ * }
1991
+ * >
1992
+ * <div>Wheel here</div>
1993
+ * </WheelObserver>
1994
+ */
1822
1995
  function WheelObserver(props) {
1823
- const { children, onWheel, as: Component = "div",...attributes } = props;
1996
+ const { as: Component = "div", children, onWheel, ref, ...attributes } = props;
1824
1997
  return /* @__PURE__ */ jsx(Component, {
1825
- ref: useRefCallbackEvent("wheel", (event) => {
1998
+ ref: composeRefs(ref, useRefCallbackEvent("wheel", (event) => {
1826
1999
  if (onWheel) onWheel(event);
1827
- }, { passive: false }, [onWheel]),
2000
+ }, { passive: false }, [onWheel])),
1828
2001
  ...attributes,
1829
2002
  children
1830
2003
  });
1831
2004
  }
1832
-
1833
2005
  //#endregion
1834
2006
  //#region src/components/XYPad/Area.tsx
1835
- /** @category XYPad */
1836
- function Area({ width = 120, height = 120, color, children, className, style, __thumb,...props }) {
2007
+ function Area({ width = 120, height = 120, color, children, className, style, __thumb, ...props }) {
1837
2008
  return /* @__PURE__ */ jsxs("div", {
1838
2009
  className: clsx("tremolo-xy-pad-area", className),
1839
2010
  style: {
@@ -1846,11 +2017,9 @@ function Area({ width = 120, height = 120, color, children, className, style, __
1846
2017
  children: [children, __thumb]
1847
2018
  });
1848
2019
  }
1849
-
1850
2020
  //#endregion
1851
2021
  //#region src/components/XYPad/Thumb.tsx
1852
- /** @category XYPad */
1853
- const Thumb = forwardRef(({ size, width = 22, height = 22, color, children, wrapperClassName, wrapperStyle, className, style, __disabled, __readonly, __css,...props }, ref) => {
2022
+ const Thumb = forwardRef(({ size, width = 22, height = 22, color, children, wrapperClassName, wrapperStyle, className, style, __disabled, __readonly, __css, ...props }, ref) => {
1854
2023
  const wrapperRef = useRef(null);
1855
2024
  useImperativeHandle(ref, () => {
1856
2025
  return {
@@ -1884,7 +2053,6 @@ const Thumb = forwardRef(({ size, width = 22, height = 22, color, children, wrap
1884
2053
  })
1885
2054
  });
1886
2055
  });
1887
-
1888
2056
  //#endregion
1889
2057
  //#region src/components/XYPad/index.tsx
1890
2058
  const defaultValueOptions = {
@@ -1894,197 +2062,201 @@ const defaultValueOptions = {
1894
2062
  wheel: ["raw", 1],
1895
2063
  keyboard: ["raw", 1]
1896
2064
  };
1897
- const XYPadImpl = forwardRef(({ x: _x, y: _y, className, style, bodyNoSelect = true, disabled = false, readonly = false, onChange, onDragStart, onDragEnd, onPointerDown, onKeyDown, onFocus, onBlur, children,...props }, forwardedRef) => {
1898
- const x = useMemo(() => {
1899
- return {
1900
- ...defaultValueOptions,
1901
- ..._x
1902
- };
1903
- }, [_x]);
1904
- const y = useMemo(() => {
1905
- return {
1906
- ...defaultValueOptions,
1907
- ..._y
2065
+ const defaultExternalStyles = {
2066
+ userSelectNone: true,
2067
+ cursor: "pointer"
2068
+ };
2069
+ /**
2070
+ * Simple XYPad
2071
+ */
2072
+ const XYPad = {
2073
+ Root: forwardRef(({ x: _x, y: _y, className, style, externalStyles: _externalStyles, disabled = false, readonly = false, onChange, onDragStart, onDragEnd, onPointerDown, onKeyDown, onFocus, onBlur, children, ...props }, forwardedRef) => {
2074
+ const x = useMemo(() => {
2075
+ return {
2076
+ ...defaultValueOptions,
2077
+ ..._x
2078
+ };
2079
+ }, [_x]);
2080
+ const y = useMemo(() => {
2081
+ return {
2082
+ ...defaultValueOptions,
2083
+ ..._y
2084
+ };
2085
+ }, [_y]);
2086
+ const rootRef = useRef(null);
2087
+ const areaElementRef = useRef(null);
2088
+ const thumbRef = useRef(null);
2089
+ const externalStyles = {
2090
+ ...defaultExternalStyles,
2091
+ ..._externalStyles
1908
2092
  };
1909
- }, [_y]);
1910
- const areaElementRef = useRef(null);
1911
- const thumbRef = useRef(null);
1912
- const nx = normalizeValue(x.value, x.min, x.max, x.skew);
1913
- const ny = normalizeValue(y.value, y.min, y.max, y.skew);
1914
- const percentX = toFixed((x.reverse ? 1 - nx : nx) * 100);
1915
- const percentY = toFixed((y.reverse ? 1 - ny : ny) * 100);
1916
- let areaProps = {};
1917
- let thumbProps = {};
1918
- if (children != void 0) React.Children.forEach(children, (child) => {
1919
- if (React.isValidElement(child)) if (child.type == Thumb) thumbProps = child.props;
1920
- else if (child.type == Area) areaProps = child.props;
1921
- else throw new Error("only <XYPadThumb> or <XYPadArea>");
1922
- else throw new Error("children is an invalid element.");
1923
- });
1924
- const onDrag = useCallback((nx$1, ny$1) => {
1925
- if (!onChange || readonly) return;
1926
- const vx = rawValue(x.reverse ? 1 - nx$1 : nx$1, x.min, x.max, x.skew);
1927
- const vy = rawValue(y.reverse ? 1 - ny$1 : ny$1, y.min, y.max, y.skew);
1928
- onChange(clamp(stepValue(vx, x.step), x.min, x.max), clamp(stepValue(vy, y.step), y.min, y.max));
1929
- }, [
1930
- onChange,
1931
- readonly,
1932
- x.max,
1933
- x.min,
1934
- x.reverse,
1935
- x.skew,
1936
- x.step,
1937
- y.max,
1938
- y.min,
1939
- y.reverse,
1940
- y.skew,
1941
- y.step
1942
- ]);
1943
- const updateValueByEvent = useCallback((eventType, target, x$1) => {
1944
- let v;
1945
- if (eventType == "normalized") v = rawValue(normalizeValue(target.value, target.min, target.max, target.skew) + x$1, target.min, target.max, target.skew);
1946
- else v = target.value + x$1;
1947
- return clamp(stepValue(v, target.step), target.min, target.max);
1948
- }, []);
1949
- const handleKeyDown = useCallback((event) => {
1950
- if (!onChange || readonly) return;
1951
- const key = event.key;
1952
- if ([
1953
- "ArrowRight",
1954
- "ArrowLeft",
1955
- "ArrowUp",
1956
- "ArrowDown"
1957
- ].includes(key)) {
1958
- const isHorizontal = key == "ArrowRight" || key == "ArrowLeft";
1959
- const target = isHorizontal ? x : y;
1960
- if (!target.keyboard) return;
2093
+ const nx = normalizeValue(x.value, x.min, x.max, x.skew);
2094
+ const ny = normalizeValue(y.value, y.min, y.max, y.skew);
2095
+ const percentX = toFixed((x.reverse ? 1 - nx : nx) * 100);
2096
+ const percentY = toFixed((y.reverse ? 1 - ny : ny) * 100);
2097
+ let areaProps = {};
2098
+ let thumbProps = {};
2099
+ if (children != void 0) React.Children.forEach(children, (child) => {
2100
+ if (React.isValidElement(child)) if (child.type == Thumb) thumbProps = child.props;
2101
+ else if (child.type == Area) areaProps = child.props;
2102
+ else throw new Error("only <XYPadThumb> or <XYPadArea>");
2103
+ else throw new Error("children is an invalid element.");
2104
+ });
2105
+ const onDrag = useCallback((nx, ny) => {
2106
+ if (!onChange || readonly) return;
2107
+ const vx = rawValue(x.reverse ? 1 - nx : nx, x.min, x.max, x.skew);
2108
+ const vy = rawValue(y.reverse ? 1 - ny : ny, y.min, y.max, y.skew);
2109
+ onChange(clamp(stepValue(vx, x.step), x.min, x.max), clamp(stepValue(vy, y.step), y.min, y.max));
2110
+ }, [
2111
+ onChange,
2112
+ readonly,
2113
+ x.max,
2114
+ x.min,
2115
+ x.reverse,
2116
+ x.skew,
2117
+ x.step,
2118
+ y.max,
2119
+ y.min,
2120
+ y.reverse,
2121
+ y.skew,
2122
+ y.step
2123
+ ]);
2124
+ const updateValueByEvent = useCallback((eventType, target, x) => {
2125
+ let v;
2126
+ if (eventType == "normalized") v = rawValue(normalizeValue(target.value, target.min, target.max, target.skew) + x, target.min, target.max, target.skew);
2127
+ else v = target.value + x;
2128
+ return clamp(stepValue(v, target.step), target.min, target.max);
2129
+ }, []);
2130
+ const handleKeyDown = useCallback((event) => {
2131
+ if (!onChange || readonly) return;
2132
+ const key = event.key;
2133
+ if ([
2134
+ "ArrowRight",
2135
+ "ArrowLeft",
2136
+ "ArrowUp",
2137
+ "ArrowDown"
2138
+ ].includes(key)) {
2139
+ const isHorizontal = key == "ArrowRight" || key == "ArrowLeft";
2140
+ const target = isHorizontal ? x : y;
2141
+ if (!target.keyboard) return;
2142
+ event.preventDefault();
2143
+ let delta = target.keyboard[1];
2144
+ if (key == "ArrowLeft" || key == "ArrowUp") delta *= -1;
2145
+ if (target.reverse) delta *= -1;
2146
+ const newValue = updateValueByEvent(target.keyboard[0], target, delta);
2147
+ if (isHorizontal) onChange(newValue, y.value);
2148
+ else onChange(x.value, newValue);
2149
+ }
2150
+ }, [
2151
+ onChange,
2152
+ readonly,
2153
+ x,
2154
+ y,
2155
+ updateValueByEvent
2156
+ ]);
2157
+ const { refHandler: touchMoveRefCallback, pointerDownHandler } = useDragWithElement({
2158
+ baseElementRef: areaElementRef,
2159
+ onDrag,
2160
+ onDragStart: (nx, ny) => {
2161
+ if (readonly) return;
2162
+ if (externalStyles.userSelectNone) addUserSelectNone();
2163
+ if (externalStyles.cursor) setCursorStyle(externalStyles.cursor);
2164
+ thumbRef.current?.focus();
2165
+ const valueX = clamp(stepValue(rawValue(nx, x.min, x.max, x.skew), x.step), x.min, x.max);
2166
+ const valueY = clamp(stepValue(rawValue(ny, y.min, y.max, y.skew), y.step), y.min, y.max);
2167
+ onDragStart?.(valueX, valueY);
2168
+ },
2169
+ onDragEnd: (nx, ny) => {
2170
+ if (readonly) return;
2171
+ if (externalStyles.userSelectNone) removeUserSelectNone();
2172
+ if (externalStyles.cursor) resetCursorStyle();
2173
+ const valueX = clamp(stepValue(rawValue(nx, x.min, x.max, x.skew), x.step), x.min, x.max);
2174
+ const valueY = clamp(stepValue(rawValue(ny, y.min, y.max, y.skew), y.step), y.min, y.max);
2175
+ onDragEnd?.(valueX, valueY);
2176
+ }
2177
+ });
2178
+ const wheelRefCallback = useRefCallbackEvent("wheel", (event) => {
2179
+ if (!onChange || readonly) return;
2180
+ const target = event.shiftKey ? x : y;
2181
+ if (!target.wheel) return;
1961
2182
  event.preventDefault();
1962
- let delta = target.keyboard[1];
1963
- if (key == "ArrowLeft" || key == "ArrowUp") delta *= -1;
2183
+ let delta = target.wheel[1];
2184
+ if (event.deltaY < 0) delta *= -1;
1964
2185
  if (target.reverse) delta *= -1;
1965
- const newValue = updateValueByEvent(target.keyboard[0], target, delta);
1966
- if (isHorizontal) onChange(newValue, y.value);
2186
+ const newValue = updateValueByEvent(target.wheel[0], target, delta);
2187
+ if (event.shiftKey) onChange(newValue, y.value);
1967
2188
  else onChange(x.value, newValue);
1968
- }
1969
- }, [
1970
- onChange,
1971
- readonly,
1972
- x,
1973
- y,
1974
- updateValueByEvent
1975
- ]);
1976
- const [touchMoveRefCallback, pointerDownHandler] = useDragWithElement({
1977
- baseElementRef: areaElementRef,
1978
- onDrag,
1979
- onDragStart: (nx$1, ny$1) => {
1980
- if (readonly) return;
1981
- if (bodyNoSelect) addNoSelect();
1982
- thumbRef.current?.focus();
1983
- const valueX = clamp(stepValue(rawValue(nx$1, x.min, x.max, x.skew), x.step), x.min, x.max);
1984
- const valueY = clamp(stepValue(rawValue(ny$1, y.min, y.max, y.skew), y.step), y.min, y.max);
1985
- onDragStart?.(valueX, valueY);
1986
- },
1987
- onDragEnd: (nx$1, ny$1) => {
1988
- if (readonly) return;
1989
- if (bodyNoSelect) removeNoSelect();
1990
- const valueX = clamp(stepValue(rawValue(nx$1, x.min, x.max, x.skew), x.step), x.min, x.max);
1991
- const valueY = clamp(stepValue(rawValue(ny$1, y.min, y.max, y.skew), y.step), y.min, y.max);
1992
- onDragEnd?.(valueX, valueY);
1993
- }
1994
- });
1995
- const wheelRefCallback = useRefCallbackEvent("wheel", (event) => {
1996
- if (!onChange || readonly) return;
1997
- const target = event.shiftKey ? x : y;
1998
- if (!target.wheel) return;
1999
- event.preventDefault();
2000
- let delta = target.wheel[1];
2001
- if (event.deltaY < 0) delta *= -1;
2002
- if (target.reverse) delta *= -1;
2003
- const newValue = updateValueByEvent(target.wheel[0], target, delta);
2004
- if (event.shiftKey) onChange(newValue, y.value);
2005
- else onChange(x.value, newValue);
2006
- }, { passive: false }, [
2007
- x,
2008
- y,
2009
- onChange,
2010
- readonly,
2011
- updateValueByEvent
2012
- ]);
2013
- useImperativeHandle(forwardedRef, () => {
2014
- return {
2015
- focus() {
2189
+ }, { passive: false }, [
2190
+ x,
2191
+ y,
2192
+ onChange,
2193
+ readonly,
2194
+ updateValueByEvent
2195
+ ]);
2196
+ useImperativeHandle(forwardedRef, () => {
2197
+ return {
2198
+ focus() {
2199
+ thumbRef.current?.focus();
2200
+ },
2201
+ blur() {
2202
+ thumbRef.current?.blur();
2203
+ },
2204
+ original: rootRef
2205
+ };
2206
+ }, []);
2207
+ return /* @__PURE__ */ jsx("div", {
2208
+ className: clsx("tremolo-xy-pad", className),
2209
+ ref: composeRefs(rootRef, wheelRefCallback, touchMoveRefCallback),
2210
+ tabIndex: -1,
2211
+ "aria-disabled": disabled,
2212
+ "aria-readonly": readonly,
2213
+ style: {
2214
+ margin: `calc(${styleHelper(thumbProps?.size ?? 22)} / 2)`,
2215
+ cursor: readonly ? "not-allowed" : "pointer",
2216
+ WebkitTapHighlightColor: "transparent",
2217
+ ...style
2218
+ },
2219
+ onPointerDown: (event) => {
2220
+ pointerDownHandler(event);
2221
+ onPointerDown?.(event);
2222
+ },
2223
+ onKeyDown: (event) => {
2224
+ handleKeyDown(event);
2225
+ onKeyDown?.(event);
2226
+ },
2227
+ onFocus: (event) => {
2016
2228
  thumbRef.current?.focus();
2229
+ onFocus?.(event);
2017
2230
  },
2018
- blur() {
2231
+ onBlur: (event) => {
2019
2232
  thumbRef.current?.blur();
2020
- }
2021
- };
2022
- }, []);
2023
- return /* @__PURE__ */ jsx("div", {
2024
- className: clsx("tremolo-xy-pad", className),
2025
- ref: (div) => {
2026
- wheelRefCallback(div);
2027
- touchMoveRefCallback(div);
2028
- },
2029
- tabIndex: -1,
2030
- "aria-disabled": disabled,
2031
- "aria-readonly": readonly,
2032
- style: {
2033
- margin: `calc(${styleHelper(thumbProps?.size ?? 22)} / 2)`,
2034
- cursor: readonly ? "not-allowed" : "pointer",
2035
- WebkitTapHighlightColor: "transparent",
2036
- ...style
2037
- },
2038
- onPointerDown: (event) => {
2039
- pointerDownHandler(event);
2040
- onPointerDown?.(event);
2041
- },
2042
- onKeyDown: (event) => {
2043
- handleKeyDown(event);
2044
- onKeyDown?.(event);
2045
- },
2046
- onFocus: (event) => {
2047
- thumbRef.current?.focus();
2048
- onFocus?.(event);
2049
- },
2050
- onBlur: (event) => {
2051
- thumbRef.current?.blur();
2052
- onBlur?.(event);
2053
- },
2054
- ...props,
2055
- children: /* @__PURE__ */ jsx("div", {
2056
- className: "tremolo-xy-pad-area-wrapper",
2057
- ref: areaElementRef,
2058
- children: /* @__PURE__ */ jsx(Area, {
2059
- __thumb: /* @__PURE__ */ jsx(Thumb, {
2060
- ref: thumbRef,
2061
- __disabled: disabled,
2062
- __readonly: readonly,
2063
- __css: {
2064
- top: `${percentY}%`,
2065
- left: `${percentX}%`
2066
- },
2067
- ...thumbProps
2068
- }),
2069
- ...areaProps
2233
+ onBlur?.(event);
2234
+ },
2235
+ ...props,
2236
+ children: /* @__PURE__ */ jsx("div", {
2237
+ className: "tremolo-xy-pad-area-wrapper",
2238
+ ref: areaElementRef,
2239
+ children: /* @__PURE__ */ jsx(Area, {
2240
+ __thumb: /* @__PURE__ */ jsx(Thumb, {
2241
+ ref: thumbRef,
2242
+ __disabled: disabled,
2243
+ __readonly: readonly,
2244
+ __css: {
2245
+ top: `${percentY}%`,
2246
+ left: `${percentX}%`
2247
+ },
2248
+ ...thumbProps
2249
+ }),
2250
+ ...areaProps
2251
+ })
2070
2252
  })
2071
- })
2072
- });
2073
- });
2074
- /**
2075
- * Simple XYPad
2076
- * @category XYPad
2077
- */
2078
- const XYPad = Object.assign(XYPadImpl, {
2253
+ });
2254
+ }),
2079
2255
  Thumb,
2080
2256
  Area
2081
- });
2082
-
2257
+ };
2083
2258
  //#endregion
2084
2259
  //#region src/hooks/useAnimationFrame.ts
2085
- /**
2086
- * @category hooks
2087
- */
2088
2260
  function useAnimationFrame(callback = () => {}, deps = []) {
2089
2261
  const reqIdRef = useRef(-1);
2090
2262
  const loop = useCallback(() => {
@@ -2096,7 +2268,6 @@ function useAnimationFrame(callback = () => {}, deps = []) {
2096
2268
  return () => cancelAnimationFrame(reqIdRef.current);
2097
2269
  }, [loop]);
2098
2270
  }
2099
-
2100
2271
  //#endregion
2101
2272
  //#region src/hooks/useMIDIAccess.ts
2102
2273
  /** @private */
@@ -2105,7 +2276,6 @@ const PERMISSION_DENIED = "PERMISSION_DENIED";
2105
2276
  const NOT_SUPPORTED = "NOT_SUPPORTED";
2106
2277
  /**
2107
2278
  * Hooks for requesting MIDI access in the browser. The first argument allows you to choose whether to request access on mount.
2108
- * @category hooks
2109
2279
  */
2110
2280
  function useMIDIAccess(requestOnMount = true) {
2111
2281
  const [midiAccess, setMidiAccess] = useState(null);
@@ -2128,12 +2298,10 @@ function useMIDIAccess(requestOnMount = true) {
2128
2298
  error
2129
2299
  };
2130
2300
  }
2131
-
2132
2301
  //#endregion
2133
2302
  //#region src/hooks/useMIDIMessage.ts
2134
2303
  /**
2135
2304
  * Hooks for when you want to process MIDI events in more detail than useMIDIInput.
2136
- * @category hooks
2137
2305
  */
2138
2306
  function useMIDIMessage(midiAccess, onMIDIMessage) {
2139
2307
  useEffect(() => {
@@ -2144,7 +2312,6 @@ function useMIDIMessage(midiAccess, onMIDIMessage) {
2144
2312
  };
2145
2313
  }, [midiAccess, onMIDIMessage]);
2146
2314
  }
2147
-
2148
2315
  //#endregion
2149
2316
  //#region src/hooks/useMIDIInput.ts
2150
2317
  const MIDI_EVENT_TO_NUMBER = {
@@ -2154,7 +2321,6 @@ const MIDI_EVENT_TO_NUMBER = {
2154
2321
  };
2155
2322
  /**
2156
2323
  * Hooks for handling note on/off events. To be used with useMIDIAccess. Internally uses useMIDIMessage.
2157
- * @category hooks
2158
2324
  */
2159
2325
  function useMIDIInput(midiAccess, onNoteOnEvent, onNoteOffEvent, onPitchBendEvent) {
2160
2326
  useMIDIMessage(midiAccess, useCallback((event) => {
@@ -2169,7 +2335,7 @@ function useMIDIInput(midiAccess, onNoteOnEvent, onNoteOffEvent, onPitchBendEven
2169
2335
  onPitchBendEvent
2170
2336
  ]));
2171
2337
  }
2172
-
2173
2338
  //#endregion
2174
- export { AnimationCanvas, AnimationKnob, DragObserver, Knob, NOT_SUPPORTED, NumberInput, PERMISSION_DENIED, Piano, SHORTCUTS, Slider, WheelObserver, XYPad, getNoteRangeArray, useAnimationFrame, useDrag, useDragWithElement, useEventListener, useInterval, useLongPress, useMIDIAccess, useMIDIInput, useMIDIMessage, useSliderContext };
2339
+ export { AnimationCanvas, DragObserver, Knob, NOT_SUPPORTED, NumberInput, PERMISSION_DENIED, Piano, PointsEditor, SHORTCUTS, Slider, WheelObserver, XYPad, clampPoint, getNoteRangeArray, useAnimationFrame, useDrag, useDragWithElement, useEventListener, useInterval, useLongPress, useMIDIAccess, useMIDIInput, useMIDIMessage, useSliderContext };
2340
+
2175
2341
  //# sourceMappingURL=index.js.map