@tremolo-ui/react 0.0.10 → 0.0.12

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 (137) hide show
  1. package/dist/cjs/index.js +435 -203
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/types/components/AnimationCanvas/index.d.ts +2 -2
  4. package/dist/cjs/types/components/AnimationCanvas/index.d.ts.map +1 -1
  5. package/dist/cjs/types/components/AnimationKnob/index.d.ts +10 -3
  6. package/dist/cjs/types/components/AnimationKnob/index.d.ts.map +1 -1
  7. package/dist/cjs/types/components/Knob/index.d.ts +13 -3
  8. package/dist/cjs/types/components/Knob/index.d.ts.map +1 -1
  9. package/dist/cjs/types/components/NumberInput/DecrementStepper.d.ts +2 -0
  10. package/dist/cjs/types/components/NumberInput/DecrementStepper.d.ts.map +1 -1
  11. package/dist/cjs/types/components/NumberInput/IncrementStepper.d.ts +2 -0
  12. package/dist/cjs/types/components/NumberInput/IncrementStepper.d.ts.map +1 -1
  13. package/dist/cjs/types/components/NumberInput/InternalInput.d.ts +18 -0
  14. package/dist/cjs/types/components/NumberInput/InternalInput.d.ts.map +1 -0
  15. package/dist/cjs/types/components/NumberInput/Stepper.d.ts +2 -0
  16. package/dist/cjs/types/components/NumberInput/Stepper.d.ts.map +1 -1
  17. package/dist/cjs/types/components/NumberInput/context.d.ts +3 -2
  18. package/dist/cjs/types/components/NumberInput/context.d.ts.map +1 -1
  19. package/dist/cjs/types/components/NumberInput/index.d.ts +12 -3
  20. package/dist/cjs/types/components/NumberInput/index.d.ts.map +1 -1
  21. package/dist/cjs/types/components/Piano/BlackKey.d.ts.map +1 -1
  22. package/dist/cjs/types/components/Piano/WhiteKey.d.ts.map +1 -1
  23. package/dist/cjs/types/components/Piano/index.d.ts +7 -4
  24. package/dist/cjs/types/components/Piano/index.d.ts.map +1 -1
  25. package/dist/cjs/types/components/Piano/key.d.ts +4 -0
  26. package/dist/cjs/types/components/Piano/key.d.ts.map +1 -1
  27. package/dist/cjs/types/components/Piano/keyboardShortcuts.d.ts +2 -2
  28. package/dist/cjs/types/components/Slider/Scale.d.ts +1 -3
  29. package/dist/cjs/types/components/Slider/Scale.d.ts.map +1 -1
  30. package/dist/cjs/types/components/Slider/Thumb.d.ts +1 -0
  31. package/dist/cjs/types/components/Slider/Thumb.d.ts.map +1 -1
  32. package/dist/cjs/types/components/Slider/Track.d.ts +2 -4
  33. package/dist/cjs/types/components/Slider/Track.d.ts.map +1 -1
  34. package/dist/cjs/types/components/Slider/context.d.ts +2 -10
  35. package/dist/cjs/types/components/Slider/context.d.ts.map +1 -1
  36. package/dist/cjs/types/components/Slider/index.d.ts +5 -1
  37. package/dist/cjs/types/components/Slider/index.d.ts.map +1 -1
  38. package/dist/cjs/types/components/WheelObserver/index.d.ts +3 -3
  39. package/dist/cjs/types/components/WheelObserver/index.d.ts.map +1 -1
  40. package/dist/cjs/types/components/XYPad/Area.d.ts +3 -3
  41. package/dist/cjs/types/components/XYPad/Area.d.ts.map +1 -1
  42. package/dist/cjs/types/components/XYPad/Thumb.d.ts +1 -0
  43. package/dist/cjs/types/components/XYPad/Thumb.d.ts.map +1 -1
  44. package/dist/cjs/types/components/XYPad/index.d.ts +6 -1
  45. package/dist/cjs/types/components/XYPad/index.d.ts.map +1 -1
  46. package/dist/cjs/types/hooks/useAnimationFrame.d.ts.map +1 -1
  47. package/dist/cjs/types/hooks/useInterval.d.ts +2 -0
  48. package/dist/cjs/types/hooks/useInterval.d.ts.map +1 -0
  49. package/dist/cjs/types/hooks/useLongPress.d.ts +2 -0
  50. package/dist/cjs/types/hooks/useLongPress.d.ts.map +1 -0
  51. package/dist/cjs/types/index.d.ts +5 -0
  52. package/dist/cjs/types/index.d.ts.map +1 -1
  53. package/dist/esm/index.js +431 -204
  54. package/dist/esm/index.js.map +1 -1
  55. package/dist/esm/types/components/AnimationCanvas/index.d.ts +2 -2
  56. package/dist/esm/types/components/AnimationCanvas/index.d.ts.map +1 -1
  57. package/dist/esm/types/components/AnimationKnob/index.d.ts +10 -3
  58. package/dist/esm/types/components/AnimationKnob/index.d.ts.map +1 -1
  59. package/dist/esm/types/components/Knob/index.d.ts +13 -3
  60. package/dist/esm/types/components/Knob/index.d.ts.map +1 -1
  61. package/dist/esm/types/components/NumberInput/DecrementStepper.d.ts +2 -0
  62. package/dist/esm/types/components/NumberInput/DecrementStepper.d.ts.map +1 -1
  63. package/dist/esm/types/components/NumberInput/IncrementStepper.d.ts +2 -0
  64. package/dist/esm/types/components/NumberInput/IncrementStepper.d.ts.map +1 -1
  65. package/dist/esm/types/components/NumberInput/InternalInput.d.ts +18 -0
  66. package/dist/esm/types/components/NumberInput/InternalInput.d.ts.map +1 -0
  67. package/dist/esm/types/components/NumberInput/Stepper.d.ts +2 -0
  68. package/dist/esm/types/components/NumberInput/Stepper.d.ts.map +1 -1
  69. package/dist/esm/types/components/NumberInput/context.d.ts +3 -2
  70. package/dist/esm/types/components/NumberInput/context.d.ts.map +1 -1
  71. package/dist/esm/types/components/NumberInput/index.d.ts +12 -3
  72. package/dist/esm/types/components/NumberInput/index.d.ts.map +1 -1
  73. package/dist/esm/types/components/Piano/BlackKey.d.ts.map +1 -1
  74. package/dist/esm/types/components/Piano/WhiteKey.d.ts.map +1 -1
  75. package/dist/esm/types/components/Piano/index.d.ts +7 -4
  76. package/dist/esm/types/components/Piano/index.d.ts.map +1 -1
  77. package/dist/esm/types/components/Piano/key.d.ts +4 -0
  78. package/dist/esm/types/components/Piano/key.d.ts.map +1 -1
  79. package/dist/esm/types/components/Piano/keyboardShortcuts.d.ts +2 -2
  80. package/dist/esm/types/components/Slider/Scale.d.ts +1 -3
  81. package/dist/esm/types/components/Slider/Scale.d.ts.map +1 -1
  82. package/dist/esm/types/components/Slider/Thumb.d.ts +1 -0
  83. package/dist/esm/types/components/Slider/Thumb.d.ts.map +1 -1
  84. package/dist/esm/types/components/Slider/Track.d.ts +2 -4
  85. package/dist/esm/types/components/Slider/Track.d.ts.map +1 -1
  86. package/dist/esm/types/components/Slider/context.d.ts +2 -10
  87. package/dist/esm/types/components/Slider/context.d.ts.map +1 -1
  88. package/dist/esm/types/components/Slider/index.d.ts +5 -1
  89. package/dist/esm/types/components/Slider/index.d.ts.map +1 -1
  90. package/dist/esm/types/components/WheelObserver/index.d.ts +3 -3
  91. package/dist/esm/types/components/WheelObserver/index.d.ts.map +1 -1
  92. package/dist/esm/types/components/XYPad/Area.d.ts +3 -3
  93. package/dist/esm/types/components/XYPad/Area.d.ts.map +1 -1
  94. package/dist/esm/types/components/XYPad/Thumb.d.ts +1 -0
  95. package/dist/esm/types/components/XYPad/Thumb.d.ts.map +1 -1
  96. package/dist/esm/types/components/XYPad/index.d.ts +6 -1
  97. package/dist/esm/types/components/XYPad/index.d.ts.map +1 -1
  98. package/dist/esm/types/hooks/useAnimationFrame.d.ts.map +1 -1
  99. package/dist/esm/types/hooks/useInterval.d.ts +2 -0
  100. package/dist/esm/types/hooks/useInterval.d.ts.map +1 -0
  101. package/dist/esm/types/hooks/useLongPress.d.ts +2 -0
  102. package/dist/esm/types/hooks/useLongPress.d.ts.map +1 -0
  103. package/dist/esm/types/index.d.ts +5 -0
  104. package/dist/esm/types/index.d.ts.map +1 -1
  105. package/dist/styles/index.css +115 -33
  106. package/package.json +41 -40
  107. package/src/components/AnimationCanvas/index.tsx +15 -7
  108. package/src/components/AnimationKnob/index.tsx +216 -187
  109. package/src/components/Knob/index.tsx +230 -171
  110. package/src/components/NumberInput/DecrementStepper.tsx +9 -2
  111. package/src/components/NumberInput/IncrementStepper.tsx +9 -2
  112. package/src/components/NumberInput/InternalInput.tsx +219 -0
  113. package/src/components/NumberInput/Stepper.tsx +2 -0
  114. package/src/components/NumberInput/context.tsx +21 -9
  115. package/src/components/NumberInput/index.css +76 -22
  116. package/src/components/NumberInput/index.tsx +94 -162
  117. package/src/components/Piano/BlackKey.tsx +14 -0
  118. package/src/components/Piano/WhiteKey.tsx +14 -0
  119. package/src/components/Piano/index.css +22 -6
  120. package/src/components/Piano/index.tsx +20 -22
  121. package/src/components/Piano/key.ts +5 -0
  122. package/src/components/Piano/keyboardShortcuts.ts +2 -2
  123. package/src/components/Slider/Scale.tsx +1 -4
  124. package/src/components/Slider/Thumb.tsx +6 -0
  125. package/src/components/Slider/Track.tsx +5 -4
  126. package/src/components/Slider/context.tsx +5 -20
  127. package/src/components/Slider/index.css +5 -2
  128. package/src/components/Slider/index.tsx +52 -26
  129. package/src/components/WheelObserver/index.tsx +4 -2
  130. package/src/components/XYPad/Area.tsx +7 -6
  131. package/src/components/XYPad/Thumb.tsx +6 -0
  132. package/src/components/XYPad/index.css +12 -2
  133. package/src/components/XYPad/index.tsx +57 -41
  134. package/src/hooks/useInterval.ts +27 -0
  135. package/src/hooks/useLongPress.ts +31 -0
  136. package/src/index.ts +6 -0
  137. package/src/styles/index.css +0 -1
package/dist/cjs/index.js CHANGED
@@ -69,10 +69,10 @@ function setDprConfig(canvas, context, width, height, dpr) {
69
69
  canvas.style.height = `${height}px`;
70
70
  }
71
71
  function AnimationCanvas(_a) {
72
- var { width = 100, height = 100, relativeSize = false, reduceFlickering = true, options, init, draw, className } = _a, props = __rest(_a, ["width", "height", "relativeSize", "reduceFlickering", "options", "init", "draw", "className"]);
72
+ var { width = 100, height = 100, relativeSize = false, reduceFlickering = true, options, init, draw, className, onContextMenu = (event) => event.preventDefault() } = _a, props = __rest(_a, ["width", "height", "relativeSize", "reduceFlickering", "options", "init", "draw", "className", "onContextMenu"]);
73
73
  const canvasRef = React.useRef(null);
74
74
  const memoCanvasRef = React.useRef(null);
75
- const reqIdRef = React.useRef();
75
+ const reqIdRef = React.useRef(-1);
76
76
  const widthRef = React.useRef(0);
77
77
  const heightRef = React.useRef(0);
78
78
  const loop = React.useCallback((context, width, height, count) => {
@@ -118,7 +118,11 @@ function AnimationCanvas(_a) {
118
118
  for (const prop of drawingState) {
119
119
  context[prop] = contextMemo[prop];
120
120
  }
121
- if (reduceFlickering && memoContext) {
121
+ if (reduceFlickering &&
122
+ memoContext &&
123
+ memoCanvas &&
124
+ memoCanvas.width > 0 &&
125
+ memoCanvas.height > 0) {
122
126
  context.drawImage(memoContext.canvas, 0, 0);
123
127
  }
124
128
  }
@@ -150,7 +154,7 @@ function AnimationCanvas(_a) {
150
154
  };
151
155
  }
152
156
  }, [loop, init, options, reduceFlickering, relativeSize]);
153
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("canvas", Object.assign({ className: clsx('tremolo-animation-canvas', className), width: relativeSize ? 0 : width, height: relativeSize ? 0 : height, ref: canvasRef }, props)), relativeSize && reduceFlickering && (jsxRuntime.jsx("canvas", { style: { display: 'none' }, ref: memoCanvasRef }))] }));
157
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("canvas", Object.assign({ className: clsx('tremolo-animation-canvas', className), width: relativeSize ? 0 : width, height: relativeSize ? 0 : height, ref: canvasRef, onContextMenu: onContextMenu }, props)), relativeSize && reduceFlickering && (jsxRuntime.jsx("canvas", { style: { display: 'none' }, ref: memoCanvasRef }))] }));
154
158
  }
155
159
 
156
160
  /**
@@ -258,7 +262,7 @@ function xor(a = false, b = false) {
258
262
  /**
259
263
  * @category MIDI
260
264
  */
261
- const noteNames = [
265
+ const noteKeys = [
262
266
  'C',
263
267
  'C#',
264
268
  'D',
@@ -292,18 +296,16 @@ function parseNoteName(noteName) {
292
296
  */
293
297
  function noteNumber(noteName) {
294
298
  const { letter, accidental, octave } = parseNoteName(noteName);
295
- const noteIndex = noteNames.indexOf(letter.toLocaleUpperCase());
299
+ const noteIndex = noteKeys.indexOf(letter.toLocaleUpperCase());
296
300
  const accidentalValue = (accidental[0] == 'b' ? -1 : 1) * accidental.length;
297
301
  return noteIndex + 12 * (Number(octave) + 1) + accidentalValue;
298
302
  }
299
303
  /**
300
- * Convert noteNumber to noteName
304
+ * Convert noteNumber to noteKey
301
305
  * @category MIDI
302
306
  */
303
- function noteName(noteNumber) {
304
- const noteIndex = mod(noteNumber, 12);
305
- const octave = Math.floor(noteNumber / 12) - 1;
306
- return noteNames[noteIndex] + octave;
307
+ function noteKey(noteNumber) {
308
+ return noteKeys[mod(noteNumber, 12)];
307
309
  }
308
310
  /**
309
311
  * @param note noteNumber: 0 ~ 127 or noteName e.g. 'C3'
@@ -426,13 +428,14 @@ function removeNoSelect() {
426
428
  }
427
429
 
428
430
  /**
429
- * simple rotary Knob.
431
+ * Animatable rotary knob.
430
432
  * @category AnimationKnob
431
433
  */
432
- function AnimationKnob(_a) {
434
+ const AnimationKnob = React.forwardRef((_a, ref) => {
433
435
  var { 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 } = _a, props = __rest(_a, ["value", "min", "max", "defaultValue", "startValue", "step", "skew", "size", "width", "height", "activeColor", "inactiveColor", "bg", "thumb", "lineWeight", "thumbWeight", "draw", "bodyNoSelect", "wheel", "keyboard", "enableDoubleClickDefault", "style", "className", "onChange"]);
434
436
  // -- state and ref ---
435
437
  const valueRef = React.useRef(0);
438
+ const elmRef = React.useRef(null);
436
439
  // --- interpret props ---
437
440
  const isRelativeSize = typeof (size !== null && size !== void 0 ? size : width) == 'string' || typeof height == 'string';
438
441
  const onDrag = React.useCallback((_x, y) => {
@@ -489,14 +492,27 @@ function AnimationKnob(_a) {
489
492
  if (onChange)
490
493
  onChange(clamp(stepValue(v, step), min, max));
491
494
  }, { passive: false }, [wheel, value, min, max, skew, step, onChange]);
495
+ React.useImperativeHandle(ref, () => {
496
+ return {
497
+ focus() {
498
+ var _a;
499
+ (_a = elmRef.current) === null || _a === void 0 ? void 0 : _a.focus();
500
+ },
501
+ blur() {
502
+ var _a;
503
+ (_a = elmRef.current) === null || _a === void 0 ? void 0 : _a.blur();
504
+ },
505
+ };
506
+ }, []);
492
507
  return (jsxRuntime.jsx("div", Object.assign({ className: clsx('tremolo-animation-knob', className), ref: (div) => {
508
+ elmRef.current = div;
493
509
  wheelRefCallback(div);
494
510
  touchMoveRefCallback(div);
495
511
  }, role: "slider", tabIndex: 0, "aria-valuenow": value, "aria-valuemax": max, "aria-valuemin": min, style: Object.assign({ display: 'inline-block', width: size !== null && size !== void 0 ? size : width, height: size !== null && size !== void 0 ? size : height, cursor: 'pointer', WebkitTapHighlightColor: 'transparent' }, style), onPointerDown: pointerDownHandler, onDoubleClick: () => {
496
512
  if (enableDoubleClickDefault && onChange) {
497
513
  onChange(defaultValue);
498
514
  }
499
- }, onKeyDown: handleKeyDown, onContextMenu: (e) => e.preventDefault() }, props, { children: jsxRuntime.jsx(AnimationCanvas, Object.assign({}, (isRelativeSize
515
+ }, onKeyDown: handleKeyDown }, props, { children: jsxRuntime.jsx(AnimationCanvas, Object.assign({}, (isRelativeSize
500
516
  ? { relativeSize: true }
501
517
  : {
502
518
  width: size !== null && size !== void 0 ? size : width,
@@ -543,7 +559,7 @@ function AnimationKnob(_a) {
543
559
  ctx.lineWidth = thumbWeight;
544
560
  ctx.stroke();
545
561
  } })) })));
546
- }
562
+ });
547
563
 
548
564
  /** @category DragObserver */
549
565
  function DragObserver(props) {
@@ -557,15 +573,20 @@ function DragObserver(props) {
557
573
  return (jsxRuntime.jsx(Component, Object.assign({ ref: refCallback, onPointerDown: pointerDownHandler }, attributes, { children: children })));
558
574
  }
559
575
 
560
- /** @category Knob */
561
- function Knob(_a) {
562
- var { 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, onChange, className, classes } = _a, props = __rest(_a, ["value", "min", "max", "step", "skew", "defaultValue", "startValue", "size", "bodyNoSelect", "wheel", "keyboard", "enableDoubleClickDefault", "disabled", "readonly", "activeLine", "inactiveLine", "thumb", "thumbLine", "onChange", "className", "classes"]);
576
+ /**
577
+ * Interactive rotary knob component implemented in SVG.
578
+ *
579
+ * @category Knob
580
+ */
581
+ const Knob = React.forwardRef((_a, ref) => {
582
+ var { 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, onChange, onKeyDown, onPointerDown, onDoubleClick, className, classes } = _a, props = __rest(_a, ["value", "min", "max", "step", "skew", "defaultValue", "startValue", "size", "bodyNoSelect", "wheel", "keyboard", "enableDoubleClickDefault", "disabled", "readonly", "activeLine", "inactiveLine", "thumb", "thumbLine", "onChange", "onKeyDown", "onPointerDown", "onDoubleClick", "className", "classes"]);
563
583
  const valueRef = React.useRef(0);
584
+ const elmRef = React.useRef(null);
564
585
  // --- interpret props ---
565
586
  const padding = 8; // %
566
587
  const thumbLineWeight = 6; // %
567
- const thumbLength = 35; // %
568
- const lineWeight = 3; // px
588
+ const thumbLineLength = 35; // %
589
+ const lineWeight = size * 0.06; // px
569
590
  const p = normalizeValue(value, min, max, skew);
570
591
  const s = normalizeValue(startValue, min, max, skew);
571
592
  // --- internal functions ---
@@ -576,6 +597,17 @@ function Knob(_a) {
576
597
  const v2 = clamp(stepValue(v, step), min, max);
577
598
  onChange(v2);
578
599
  }, [max, min, onChange, readonly, skew, step]);
600
+ const updateValueByEvent = React.useCallback((eventType, x) => {
601
+ let newValue;
602
+ if (eventType == 'normalized') {
603
+ const n = normalizeValue(value, min, max, skew);
604
+ newValue = rawValue(n + x, min, max, skew);
605
+ }
606
+ else {
607
+ newValue = value + x;
608
+ }
609
+ return clamp(stepValue(newValue, step), min, max);
610
+ }, [max, min, skew, step, value]);
579
611
  const handleKeyDown = React.useCallback((event) => {
580
612
  if (!keyboard || !onChange || readonly)
581
613
  return;
@@ -583,17 +615,9 @@ function Knob(_a) {
583
615
  if (['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown'].includes(key)) {
584
616
  event.preventDefault();
585
617
  const x = key == 'ArrowRight' || key == 'ArrowUp' ? keyboard[1] : -keyboard[1];
586
- let v;
587
- if (keyboard[0] == 'normalized') {
588
- const n = normalizeValue(value, min, max, skew);
589
- v = rawValue(n + x, min, max, skew);
590
- }
591
- else {
592
- v = value + x;
593
- }
594
- onChange(clamp(stepValue(v, step), min, max));
618
+ onChange(updateValueByEvent(keyboard[0], x));
595
619
  }
596
- }, [value, min, max, step, skew, keyboard, readonly, onChange]);
620
+ }, [keyboard, onChange, readonly, updateValueByEvent]);
597
621
  // --- hooks ---
598
622
  const [touchMoveRefCallback, pointerDownHandler] = useDrag({
599
623
  onDrag: onDrag,
@@ -608,21 +632,26 @@ function Knob(_a) {
608
632
  },
609
633
  });
610
634
  const wheelRefCallback = useRefCallbackEvent('wheel', (event) => {
611
- if (!wheel)
635
+ if (!wheel || readonly)
612
636
  return;
613
637
  event.preventDefault();
614
- const x = event.deltaY > 0 ? -wheel[1] : wheel[1];
615
- let v;
616
- if (wheel[0] == 'normalized') {
617
- const n = normalizeValue(value, min, max, skew);
618
- v = rawValue(n + x, min, max, skew);
619
- }
620
- else {
621
- v = value + x;
622
- }
623
- if (onChange)
624
- onChange(clamp(stepValue(v, step), min, max));
625
- }, { passive: false }, [wheel, value, min, max, skew, step, onChange]);
638
+ if (!onChange || event.deltaY == 0)
639
+ return;
640
+ const x = Math.sign(event.deltaY) * -wheel[1];
641
+ onChange(updateValueByEvent(wheel[0], x));
642
+ }, { passive: false }, [wheel, onChange, readonly, updateValueByEvent]);
643
+ React.useImperativeHandle(ref, () => {
644
+ return {
645
+ focus() {
646
+ var _a;
647
+ (_a = elmRef.current) === null || _a === void 0 ? void 0 : _a.focus();
648
+ },
649
+ blur() {
650
+ var _a;
651
+ (_a = elmRef.current) === null || _a === void 0 ? void 0 : _a.blur();
652
+ },
653
+ };
654
+ }, []);
626
655
  const center = size / 2;
627
656
  const r1 = -135;
628
657
  const r2 = -135 + Math.min(p, s) * 270;
@@ -637,13 +666,62 @@ function Knob(_a) {
637
666
  const x4 = center + center * Math.cos(radian(r4 - 90));
638
667
  const y4 = center + center * Math.sin(radian(r4 - 90));
639
668
  return (jsxRuntime.jsxs("svg", Object.assign({ className: clsx('tremolo-knob', className), ref: (div) => {
669
+ elmRef.current = div;
640
670
  wheelRefCallback(div);
641
671
  touchMoveRefCallback(div);
642
- }, width: size, height: size, tabIndex: 0, "aria-valuenow": value, "aria-valuemin": min, "aria-valuemax": max, "aria-disabled": disabled, "aria-readonly": readonly, onPointerDown: pointerDownHandler, onDoubleClick: () => {
672
+ }, width: size, height: size, tabIndex: 0, "aria-valuenow": value, "aria-valuemin": min, "aria-valuemax": max, "aria-disabled": disabled, "aria-readonly": readonly, onPointerDown: (event) => {
673
+ pointerDownHandler(event);
674
+ onPointerDown === null || onPointerDown === void 0 ? void 0 : onPointerDown(event);
675
+ }, onDoubleClick: (event) => {
643
676
  if (enableDoubleClickDefault && onChange) {
644
677
  onChange(defaultValue);
645
678
  }
646
- }, onKeyDown: handleKeyDown, onContextMenu: (e) => e.preventDefault() }, props, { children: [startValue > min && (jsxRuntime.jsx("path", { className: clsx('tremolo-knob-inactive-line', classes === null || classes === void 0 ? void 0 : classes.inactiveLine), d: `M ${x1} ${y1} A ${center} ${center} -135 ${r2 - r1 > 180 ? 1 : 0} 1 ${x2}, ${y2}`, fill: "none", stroke: inactiveLine || 'currentColor', strokeWidth: lineWeight })), jsxRuntime.jsx("path", { className: clsx('tremolo-knob-active-line', classes === null || classes === void 0 ? void 0 : classes.activeLine), d: `M ${x2} ${y2} A ${center} ${center} -135 ${r3 - r2 > 180 ? 1 : 0} 1 ${x3}, ${y3}`, fill: "none", stroke: activeLine || 'currentColor', strokeWidth: lineWeight }), startValue < max && (jsxRuntime.jsx("path", { className: clsx('tremolo-knob-inactive-line', classes === null || classes === void 0 ? void 0 : classes.inactiveLine), d: `M ${x3} ${y3} A ${center} ${center} -135 ${r4 - r3 > 180 ? 1 : 0} 1 ${x4}, ${y4}`, fill: "none", stroke: inactiveLine || 'currentColor', strokeWidth: lineWeight })), jsxRuntime.jsxs("svg", { className: clsx('tremolo-knob-thumb', classes === null || classes === void 0 ? void 0 : classes.thumb), children: [jsxRuntime.jsx("circle", { cx: "50%", cy: "50%", r: `${50 - padding}%`, fill: thumb || 'currentColor' }), jsxRuntime.jsx("line", { className: clsx('tremolo-knob-thumb-line', classes === null || classes === void 0 ? void 0 : classes.thumbLine), x1: "50%", y1: `${padding}%`, x2: "50%", y2: `${thumbLength}%`, stroke: thumbLine || 'currentColor', strokeWidth: `${thumbLineWeight}%`, transform: `rotate(${ -135 + p * 270})`, "transform-origin": "50% 50%" })] })] })));
679
+ onDoubleClick === null || onDoubleClick === void 0 ? void 0 : onDoubleClick(event);
680
+ }, onKeyDown: (event) => {
681
+ handleKeyDown(event);
682
+ onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event);
683
+ } }, props, { children: [startValue > min && (jsxRuntime.jsx("path", { className: clsx('tremolo-knob-inactive-line', classes === null || classes === void 0 ? void 0 : classes.inactiveLine), d: `M ${x1} ${y1} A ${center} ${center} -135 ${r2 - r1 > 180 ? 1 : 0} 1 ${x2}, ${y2}`, fill: "none", stroke: inactiveLine || 'currentColor', strokeWidth: lineWeight })), jsxRuntime.jsx("path", { className: clsx('tremolo-knob-active-line', classes === null || classes === void 0 ? void 0 : classes.activeLine), d: `M ${x2} ${y2} A ${center} ${center} -135 ${r3 - r2 > 180 ? 1 : 0} 1 ${x3}, ${y3}`, fill: "none", stroke: activeLine || 'currentColor', strokeWidth: lineWeight }), startValue < max && (jsxRuntime.jsx("path", { className: clsx('tremolo-knob-inactive-line', classes === null || classes === void 0 ? void 0 : classes.inactiveLine), d: `M ${x3} ${y3} A ${center} ${center} -135 ${r4 - r3 > 180 ? 1 : 0} 1 ${x4}, ${y4}`, fill: "none", stroke: inactiveLine || 'currentColor', strokeWidth: lineWeight })), jsxRuntime.jsxs("svg", { className: clsx('tremolo-knob-thumb', classes === null || classes === void 0 ? void 0 : classes.thumb), children: [jsxRuntime.jsx("circle", { cx: "50%", cy: "50%", r: `${50 - padding}%`, fill: thumb || 'currentColor' }), jsxRuntime.jsx("line", { className: clsx('tremolo-knob-thumb-line', classes === null || classes === void 0 ? void 0 : classes.thumbLine), x1: "50%", y1: `${padding}%`, x2: "50%", y2: `${thumbLineLength}%`, stroke: thumbLine || 'currentColor', strokeWidth: `${thumbLineWeight}%`,
684
+ // NOTE
685
+ // https://bugs.webkit.org/show_bug.cgi?id=201854
686
+ // https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/transform-origin#browser_compatibility
687
+ style: {
688
+ transform: `rotate(${ -135 + p * 270}deg)`,
689
+ transformOrigin: '50% 50%',
690
+ } })] })] })));
691
+ });
692
+
693
+ const createStoreImpl = (createState) => {
694
+ let state;
695
+ const listeners = /* @__PURE__ */ new Set();
696
+ const setState = (partial, replace) => {
697
+ const nextState = typeof partial === "function" ? partial(state) : partial;
698
+ if (!Object.is(nextState, state)) {
699
+ const previousState = state;
700
+ state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState);
701
+ listeners.forEach((listener) => listener(state, previousState));
702
+ }
703
+ };
704
+ const getState = () => state;
705
+ const getInitialState = () => initialState;
706
+ const subscribe = (listener) => {
707
+ listeners.add(listener);
708
+ return () => listeners.delete(listener);
709
+ };
710
+ const api = { setState, getState, getInitialState, subscribe };
711
+ const initialState = state = createState(setState, getState, api);
712
+ return api;
713
+ };
714
+ const createStore = (createState) => createState ? createStoreImpl(createState) : createStoreImpl;
715
+
716
+ const identity = (arg) => arg;
717
+ function useStore(api, selector = identity) {
718
+ const slice = React.useSyncExternalStore(
719
+ api.subscribe,
720
+ () => selector(api.getState()),
721
+ () => selector(api.getInitialState())
722
+ );
723
+ React.useDebugValue(slice);
724
+ return slice;
647
725
  }
648
726
 
649
727
  /**
@@ -692,40 +770,6 @@ function parseValue(inputString, units, digit) {
692
770
  };
693
771
  }
694
772
 
695
- const createStoreImpl = (createState) => {
696
- let state;
697
- const listeners = /* @__PURE__ */ new Set();
698
- const setState = (partial, replace) => {
699
- const nextState = typeof partial === "function" ? partial(state) : partial;
700
- if (!Object.is(nextState, state)) {
701
- const previousState = state;
702
- state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState);
703
- listeners.forEach((listener) => listener(state, previousState));
704
- }
705
- };
706
- const getState = () => state;
707
- const getInitialState = () => initialState;
708
- const subscribe = (listener) => {
709
- listeners.add(listener);
710
- return () => listeners.delete(listener);
711
- };
712
- const api = { setState, getState, getInitialState, subscribe };
713
- const initialState = state = createState(setState, getState, api);
714
- return api;
715
- };
716
- const createStore = (createState) => createState ? createStoreImpl(createState) : createStoreImpl;
717
-
718
- const identity = (arg) => arg;
719
- function useStore(api, selector = identity) {
720
- const slice = React.useSyncExternalStore(
721
- api.subscribe,
722
- () => selector(api.getState()),
723
- () => selector(api.getInitialState())
724
- );
725
- React.useDebugValue(slice);
726
- return slice;
727
- }
728
-
729
773
  function safeClamp(value, min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER) {
730
774
  return clamp(value, min, max);
731
775
  }
@@ -736,6 +780,7 @@ const createNumberInputStore = (initProps) => {
736
780
  min: 0,
737
781
  max: 0,
738
782
  step: 1,
783
+ readonly: false,
739
784
  keepWithinRange: true,
740
785
  onChange: (_v, _t) => { },
741
786
  };
@@ -744,8 +789,10 @@ const createNumberInputStore = (initProps) => {
744
789
  valueAsNumber: parseValue(String((initProps === null || initProps === void 0 ? void 0 : initProps.value) || ''), initProps === null || initProps === void 0 ? void 0 : initProps.units, initProps === null || initProps === void 0 ? void 0 : initProps.digit).rawValue,
745
790
  }), { increment: () => set((state) => {
746
791
  var _a, _b;
747
- let next = parseValue(state.value, state.units, state.digit).rawValue +
748
- ((_a = state.step) !== null && _a !== void 0 ? _a : 1);
792
+ if (state.readonly)
793
+ return {};
794
+ const current = parseValue(state.value, state.units, state.digit).rawValue;
795
+ let next = current + ((_a = state.step) !== null && _a !== void 0 ? _a : 1);
749
796
  if (state.keepWithinRange) {
750
797
  next = safeClamp(next, state.min, state.max);
751
798
  }
@@ -757,8 +804,10 @@ const createNumberInputStore = (initProps) => {
757
804
  };
758
805
  }), decrement: () => set((state) => {
759
806
  var _a, _b;
760
- let next = parseValue(state.value, state.units, state.digit).rawValue -
761
- ((_a = state.step) !== null && _a !== void 0 ? _a : 1);
807
+ if (state.readonly)
808
+ return {};
809
+ const current = parseValue(state.value, state.units, state.digit).rawValue;
810
+ let next = current - ((_a = state.step) !== null && _a !== void 0 ? _a : 1);
762
811
  if (state.keepWithinRange) {
763
812
  next = safeClamp(next, state.min, state.max);
764
813
  }
@@ -769,6 +818,8 @@ const createNumberInputStore = (initProps) => {
769
818
  valueAsNumber: next,
770
819
  };
771
820
  }), change: (value) => set((state) => {
821
+ if (state.readonly)
822
+ return {};
772
823
  const v = value !== null && value !== void 0 ? value : '';
773
824
  return {
774
825
  value: v,
@@ -779,7 +830,7 @@ const createNumberInputStore = (initProps) => {
779
830
  const NumberInputContext = React.createContext(null);
780
831
  function NumberInputProvider(_a) {
781
832
  var { children } = _a, props = __rest(_a, ["children"]);
782
- const storeRef = React.useRef();
833
+ const storeRef = React.useRef(null);
783
834
  if (!storeRef.current) {
784
835
  storeRef.current = createNumberInputStore(props);
785
836
  }
@@ -805,36 +856,17 @@ function useNumberInputContext(selector) {
805
856
  return useStore(store, selector);
806
857
  }
807
858
 
808
- function Stepper(_a) {
809
- var { dynamic = true, children, className } = _a, props = __rest(_a, ["dynamic", "children", "className"]);
810
- return (jsxRuntime.jsx("div", Object.assign({ className: clsx('tremolo-number-input-stepper', className), "data-dynamic": dynamic }, props, { children: children })));
811
- }
812
-
813
- function IncrementStepper(_a) {
814
- var { size = 12, children, className } = _a, props = __rest(_a, ["size", "children", "className"]);
815
- const max = useNumberInputContext((s) => s.max);
816
- const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
817
- const keepWithinRange = useNumberInputContext((s) => s.keepWithinRange);
818
- const increment = useNumberInputContext((s) => s.increment);
819
- // TODO: add long press event
820
- return (jsxRuntime.jsx("div", Object.assign({ className: clsx('tremolo-number-input-increment-stepper', className), role: "button", tabIndex: -1, "aria-disabled": keepWithinRange && valueAsNumber >= (max !== null && max !== void 0 ? max : Number.MAX_SAFE_INTEGER), onPointerDown: increment }, props, { children: children || (jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("polyline", { points: "18 15 12 9 6 15" }) })) })));
821
- }
822
-
823
- function DecrementStepper(_a) {
824
- var { size = 12, children, className } = _a, props = __rest(_a, ["size", "children", "className"]);
825
- const min = useNumberInputContext((s) => s.min);
826
- const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
827
- const keepWithinRange = useNumberInputContext((s) => s.keepWithinRange);
828
- const decrement = useNumberInputContext((s) => s.decrement);
829
- // TODO: add long press event
830
- return (jsxRuntime.jsx("div", Object.assign({ className: clsx('tremolo-number-input-decrement-stepper', className), role: "button", tabIndex: -1, "aria-disabled": keepWithinRange && valueAsNumber <= (min !== null && min !== void 0 ? min : Number.MIN_SAFE_INTEGER), onPointerDown: decrement }, props, { children: children || (jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("polyline", { points: "6 9 12 15 18 9" }) })) })));
831
- }
832
-
833
- function InternalInput(_a) {
834
- var { min, max, step, defaultValue, units, disabled, digit, selectWithFocus, blurOnEnter, keepWithinRange, clampValueOnBlur, wheel, keyboard, wrapperClassName, className, onChange, onFocus, onBlur, children: _children } = _a, props = __rest(_a, ["min", "max", "step", "defaultValue", "units", "disabled", "digit", "selectWithFocus", "blurOnEnter", "keepWithinRange", "clampValueOnBlur", "wheel", "keyboard", "wrapperClassName", "className", "onChange", "onFocus", "onBlur", "children"]);
859
+ const InternalInput = React.forwardRef((_a, ref) => {
860
+ var { disabled, readonly, selectWithFocus, blurOnEnter, clampValueOnBlur, wheel, keyboard, className, onChange, onFocus, onBlur, onKeyDown, children: _children } = _a, props = __rest(_a, ["disabled", "readonly", "selectWithFocus", "blurOnEnter", "clampValueOnBlur", "wheel", "keyboard", "className", "onChange", "onFocus", "onBlur", "onKeyDown", "children"]);
861
+ const elmRef = React.useRef(null);
835
862
  const [editing, setEditing] = React.useState(false);
836
863
  const value = useNumberInputContext((s) => s.value);
837
864
  const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
865
+ const min = useNumberInputContext((s) => s.min);
866
+ const max = useNumberInputContext((s) => s.max);
867
+ const step = useNumberInputContext((s) => s.step);
868
+ const units = useNumberInputContext((s) => s.units);
869
+ const digit = useNumberInputContext((s) => s.digit);
838
870
  const change = useNumberInputContext((s) => s.change);
839
871
  const error = React.useMemo(() => {
840
872
  if (editing)
@@ -842,11 +874,71 @@ function InternalInput(_a) {
842
874
  const v = valueAsNumber;
843
875
  return (min !== null && min !== void 0 ? min : -Infinity) > v || v > (max !== null && max !== void 0 ? max : Infinity);
844
876
  }, [editing, max, min, valueAsNumber]);
845
- return (jsxRuntime.jsx("input", Object.assign({ className: clsx('tremolo-number-input', className), value: value, min: min, max: max, step: step, type: "text", disabled: disabled, tabIndex: -1, "data-error": error, onChange: (event) => {
877
+ const updateValueByEvent = React.useCallback((eventType, x) => {
878
+ let newValue;
879
+ if (eventType == 'normalized') {
880
+ if (!min || !max) {
881
+ throw new Error('required parameter: min, max'); // TODO
882
+ }
883
+ const n = normalizeValue(valueAsNumber, min, max);
884
+ newValue = rawValue(n + x, min, max);
885
+ return clamp(stepValue(newValue, step), min, max);
886
+ }
887
+ else {
888
+ newValue = valueAsNumber + x;
889
+ return stepValue(newValue, step);
890
+ }
891
+ }, [max, min, step, valueAsNumber]);
892
+ const handleKeyDown = React.useCallback((event) => {
893
+ if (!keyboard || !onChange || readonly)
894
+ return;
895
+ const key = event.key;
896
+ if (!['ArrowUp', 'ArrowDown'].includes(key))
897
+ return;
898
+ event.preventDefault();
899
+ const x = key == 'ArrowUp' ? keyboard[1] : -keyboard[1];
900
+ const parsed = parseValue(String(updateValueByEvent(keyboard[0], x)));
901
+ console.log('key control');
902
+ onChange === null || onChange === void 0 ? void 0 : onChange(parsed.rawValue, parsed.formatValue);
903
+ }, [keyboard, readonly, onChange, updateValueByEvent]);
904
+ const wheelRefCallback = useRefCallbackEvent('wheel', (event) => {
905
+ if (!wheel || readonly)
906
+ return;
907
+ event.preventDefault();
908
+ if (!onChange || event.deltaY == 0)
909
+ return;
910
+ const x = Math.sign(event.deltaY) * -wheel[1];
911
+ const parsed = parseValue(String(updateValueByEvent(wheel[0], x)));
912
+ console.log('wheel control');
913
+ onChange === null || onChange === void 0 ? void 0 : onChange(parsed.rawValue, parsed.formatValue);
914
+ }, { passive: false }, [wheel, readonly, onChange, updateValueByEvent]);
915
+ React.useImperativeHandle(ref, () => {
916
+ return {
917
+ focus() {
918
+ var _a;
919
+ (_a = elmRef.current) === null || _a === void 0 ? void 0 : _a.focus();
920
+ },
921
+ blur() {
922
+ var _a;
923
+ (_a = elmRef.current) === null || _a === void 0 ? void 0 : _a.blur();
924
+ },
925
+ };
926
+ }, []);
927
+ return (jsxRuntime.jsx("input", Object.assign({ ref: (input) => {
928
+ elmRef.current = input;
929
+ wheelRefCallback(input);
930
+ }, className: clsx('tremolo-number-input', className), value: value, min: min, max: max, step: step, type: "text", "aria-disabled": disabled, "aria-readonly": readonly, tabIndex: -1, "data-error": error, onChange: (event) => {
931
+ if (readonly)
932
+ return;
846
933
  const v = event.currentTarget.value;
934
+ const cursorPos = event.target.selectionStart;
847
935
  change(v);
848
936
  const valueAsNumber = parseValue(v, units, digit).rawValue;
849
937
  onChange === null || onChange === void 0 ? void 0 : onChange(valueAsNumber, v);
938
+ if (cursorPos) {
939
+ event.target.selectionStart = cursorPos;
940
+ event.target.selectionEnd = cursorPos;
941
+ }
850
942
  }, onFocus: (event) => {
851
943
  setEditing(true);
852
944
  const v = event.currentTarget.value;
@@ -875,18 +967,97 @@ function InternalInput(_a) {
875
967
  if (blurOnEnter && event.key == 'Enter') {
876
968
  event.currentTarget.blur();
877
969
  }
970
+ handleKeyDown(event);
971
+ onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event);
878
972
  } }, props)));
973
+ });
974
+
975
+ /** @category NumberInput */
976
+ function Stepper(_a) {
977
+ var { dynamic = true, children, className } = _a, props = __rest(_a, ["dynamic", "children", "className"]);
978
+ return (jsxRuntime.jsx("div", Object.assign({ className: clsx('tremolo-number-input-stepper', className), "data-dynamic": dynamic }, props, { children: children })));
979
+ }
980
+
981
+ function useInterval(callback, delay) {
982
+ const savedCallback = React.useRef(callback);
983
+ // Remember the latest callback if it changes.
984
+ React.useEffect(() => {
985
+ savedCallback.current = callback;
986
+ }, [callback]);
987
+ // Set up the interval.
988
+ React.useEffect(() => {
989
+ // Don't schedule if no delay is specified.
990
+ // Note: 0 is a valid value for delay.
991
+ if (delay === null) {
992
+ return;
993
+ }
994
+ const id = setInterval(() => {
995
+ savedCallback.current();
996
+ }, delay);
997
+ return () => {
998
+ clearInterval(id);
999
+ };
1000
+ }, [delay]);
879
1001
  }
1002
+
1003
+ function useLongPress(callback, initialDelay = 500, interval = 40) {
1004
+ const [pressed, setPressed] = React.useState(false);
1005
+ const [delay, setDelay] = React.useState(initialDelay);
1006
+ useInterval(() => {
1007
+ callback();
1008
+ setDelay(interval);
1009
+ }, pressed ? delay : null);
1010
+ useEventListener(globalThis.window, 'pointerup', () => {
1011
+ setPressed(false);
1012
+ setDelay(initialDelay);
1013
+ });
1014
+ return React.useCallback(() => {
1015
+ callback();
1016
+ setPressed(true);
1017
+ }, [callback]);
1018
+ }
1019
+
1020
+ /** @category NumberInput */
1021
+ function IncrementStepper(_a) {
1022
+ var { size = 12, children, className } = _a, props = __rest(_a, ["size", "children", "className"]);
1023
+ const max = useNumberInputContext((s) => s.max);
1024
+ const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
1025
+ const readonly = useNumberInputContext((s) => s.readonly);
1026
+ const keepWithinRange = useNumberInputContext((s) => s.keepWithinRange);
1027
+ const increment = useNumberInputContext((s) => s.increment);
1028
+ const press = useLongPress(increment);
1029
+ return (
1030
+ // eslint-disable-next-line jsx-a11y/role-supports-aria-props
1031
+ jsxRuntime.jsx("div", Object.assign({ className: clsx('tremolo-number-input-increment-stepper', className), role: "button", tabIndex: -1, "aria-disabled": keepWithinRange && valueAsNumber >= (max !== null && max !== void 0 ? max : Number.MAX_SAFE_INTEGER), "aria-readonly": readonly, onPointerDown: press }, props, { children: children || (jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("polyline", { points: "18 15 12 9 6 15" }) })) })));
1032
+ }
1033
+
1034
+ /** @category NumberInput */
1035
+ function DecrementStepper(_a) {
1036
+ var { size = 12, children, className } = _a, props = __rest(_a, ["size", "children", "className"]);
1037
+ const min = useNumberInputContext((s) => s.min);
1038
+ const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
1039
+ const readonly = useNumberInputContext((s) => s.readonly);
1040
+ const keepWithinRange = useNumberInputContext((s) => s.keepWithinRange);
1041
+ const decrement = useNumberInputContext((s) => s.decrement);
1042
+ const press = useLongPress(decrement);
1043
+ return (
1044
+ // eslint-disable-next-line jsx-a11y/role-supports-aria-props
1045
+ jsxRuntime.jsx("div", Object.assign({ className: clsx('tremolo-number-input-decrement-stepper', className), role: "button", tabIndex: -1, "aria-disabled": keepWithinRange && valueAsNumber <= (min !== null && min !== void 0 ? min : Number.MIN_SAFE_INTEGER), "aria-readonly": readonly, onPointerDown: press }, props, { children: children || (jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("polyline", { points: "6 9 12 15 18 9" }) })) })));
1046
+ }
1047
+
880
1048
  /**
881
1049
  * Input with some useful functions for entering numerical values.
882
1050
  * @category NumberInput
883
1051
  */
884
- function NumberInput(_a) {
885
- var { value, min, max, step = 1, defaultValue, units, disabled = false, digit, selectWithFocus = 'none', blurOnEnter = true, keepWithinRange = true, clampValueOnBlur = true, wheel = ['raw', 1], keyboard = ['raw', 1], wrapperClassName, className, onChange, onFocus, onBlur, children } = _a, props = __rest(_a, ["value", "min", "max", "step", "defaultValue", "units", "disabled", "digit", "selectWithFocus", "blurOnEnter", "keepWithinRange", "clampValueOnBlur", "wheel", "keyboard", "wrapperClassName", "className", "onChange", "onFocus", "onBlur", "children"]);
886
- return (jsxRuntime.jsx(NumberInputProvider, { value: String(value), min: min, max: max, step: step, units: units, digit: digit, keepWithinRange: keepWithinRange, onChange: onChange, children: jsxRuntime.jsxs("div", { className: clsx('tremolo-number-input-wrapper', wrapperClassName),
1052
+ const NumberInput = React.forwardRef((_a, ref) => {
1053
+ var { 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 } = _a, props = __rest(_a, ["value", "min", "max", "step", "units", "readonly", "disabled", "digit", "variant", "selectWithFocus", "blurOnEnter", "keepWithinRange", "clampValueOnBlur", "wheel", "keyboard", "activeColor", "wrapperClassName", "className", "onChange", "onFocus", "onBlur", "children"]);
1054
+ const colors = {
1055
+ '--active-color': activeColor,
1056
+ };
1057
+ return (jsxRuntime.jsx(NumberInputProvider, { value: String(value), min: min, max: max, step: step, units: units, digit: digit, readonly: readonly, keepWithinRange: keepWithinRange, onChange: onChange, children: jsxRuntime.jsxs("div", { className: clsx('tremolo-number-input-wrapper', wrapperClassName),
887
1058
  // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
888
- tabIndex: 0, "data-stepper": !!children, children: [jsxRuntime.jsx(InternalInput, Object.assign({ min: min, max: max, step: step, defaultValue: defaultValue, units: units, disabled: disabled, digit: digit, selectWithFocus: selectWithFocus, blurOnEnter: blurOnEnter, keepWithinRange: keepWithinRange, clampValueOnBlur: clampValueOnBlur, wheel: wheel, keyboard: keyboard, wrapperClassName: wrapperClassName, className: className, onChange: onChange, onFocus: onFocus, onBlur: onBlur }, props)), children] }) }));
889
- }
1059
+ tabIndex: 0, style: Object.assign({}, colors), "data-stepper": !!children, "data-variant": variant, children: [jsxRuntime.jsx(InternalInput, Object.assign({ ref: ref, readonly: readonly, disabled: disabled, selectWithFocus: selectWithFocus, blurOnEnter: blurOnEnter, clampValueOnBlur: clampValueOnBlur, wheel: wheel, keyboard: keyboard, className: className, onChange: onChange, onFocus: onFocus, onBlur: onBlur }, props)), children] }) }));
1060
+ });
890
1061
 
891
1062
  /** @category Piano */
892
1063
  function KeyLabel(_a) {
@@ -901,8 +1072,14 @@ function KeyLabel(_a) {
901
1072
  const defaultWhiteKeyWidth = 40;
902
1073
  /** @category Piano */
903
1074
  const WhiteKey = React.forwardRef((_a, ref) => {
904
- var { width = defaultWhiteKeyWidth, height = '100%', className, style, children, __glissando, __position, __note, __disabled, __index, __fill, __width, __onPlayNote, __onStopNote, __label } = _a, props = __rest(_a, ["width", "height", "className", "style", "children", "__glissando", "__position", "__note", "__disabled", "__index", "__fill", "__width", "__onPlayNote", "__onStopNote", "__label"]);
1075
+ var { width = defaultWhiteKeyWidth, height = '100%', bg, color, activeBg, activeColor, className, style, children, __glissando, __position, __note, __disabled, __index, __fill, __width, __onPlayNote, __onStopNote, __label, onContextMenu = (e) => e.preventDefault() } = _a, props = __rest(_a, ["width", "height", "bg", "color", "activeBg", "activeColor", "className", "style", "children", "__glissando", "__position", "__note", "__disabled", "__index", "__fill", "__width", "__onPlayNote", "__onStopNote", "__label", "onContextMenu"]);
905
1076
  const [played, setPlayed] = React.useState(false);
1077
+ const colors = {
1078
+ '--bg': bg,
1079
+ '--color': color,
1080
+ '--active-bg': activeBg,
1081
+ '--active-color': activeColor,
1082
+ };
906
1083
  React.useImperativeHandle(ref, () => {
907
1084
  return {
908
1085
  play() {
@@ -938,7 +1115,7 @@ const WhiteKey = React.forwardRef((_a, ref) => {
938
1115
  }
939
1116
  });
940
1117
  }
941
- return (jsxRuntime.jsx("div", Object.assign({ className: clsx(`tremolo-piano-white-key`, className), "aria-disabled": __disabled, "data-active": played, style: Object.assign({ left: __position, width: __fill ? __width : (width !== null && width !== void 0 ? width : __width), height: height }, style), onPointerDown: () => {
1118
+ return (jsxRuntime.jsx("div", Object.assign({ className: clsx(`tremolo-piano-white-key`, className), "aria-disabled": __disabled, "data-active": played, style: Object.assign(Object.assign(Object.assign({}, colors), { left: __position, width: __fill ? __width : (width !== null && width !== void 0 ? width : __width), height: height }), style), onPointerDown: () => {
942
1119
  if (__disabled)
943
1120
  return;
944
1121
  setPlayed(true);
@@ -966,13 +1143,19 @@ const WhiteKey = React.forwardRef((_a, ref) => {
966
1143
  if (__onStopNote)
967
1144
  __onStopNote(__note);
968
1145
  }
969
- } }, props, { children: jsxRuntime.jsx(KeyLabel, Object.assign({ __note: __note, __label: __label, __index: __index }, keyLabelProps)) })));
1146
+ }, onContextMenu: onContextMenu }, props, { children: jsxRuntime.jsx(KeyLabel, Object.assign({ __note: __note, __label: __label, __index: __index }, keyLabelProps)) })));
970
1147
  });
971
1148
 
972
1149
  /** @category Piano */
973
1150
  const BlackKey = React.forwardRef((_a, ref) => {
974
- var { width = defaultWhiteKeyWidth * 0.65, height = '60%', className, style, children, __glissando, __position, __note, __disabled, __index, __fill, __width, __onPlayNote, __onStopNote, __label } = _a, props = __rest(_a, ["width", "height", "className", "style", "children", "__glissando", "__position", "__note", "__disabled", "__index", "__fill", "__width", "__onPlayNote", "__onStopNote", "__label"]);
1151
+ var { width = defaultWhiteKeyWidth * 0.65, height = '60%', bg, color, activeBg, activeColor, className, style, children, __glissando, __position, __note, __disabled, __index, __fill, __width, __onPlayNote, __onStopNote, __label, onContextMenu = (e) => e.preventDefault() } = _a, props = __rest(_a, ["width", "height", "bg", "color", "activeBg", "activeColor", "className", "style", "children", "__glissando", "__position", "__note", "__disabled", "__index", "__fill", "__width", "__onPlayNote", "__onStopNote", "__label", "onContextMenu"]);
975
1152
  const [played, setPlayed] = React.useState(false);
1153
+ const colors = {
1154
+ '--bg': bg,
1155
+ '--color': color,
1156
+ '--active-bg': activeBg,
1157
+ '--active-color': activeColor,
1158
+ };
976
1159
  React.useImperativeHandle(ref, () => {
977
1160
  return {
978
1161
  play() {
@@ -1008,7 +1191,7 @@ const BlackKey = React.forwardRef((_a, ref) => {
1008
1191
  }
1009
1192
  });
1010
1193
  }
1011
- return (jsxRuntime.jsx("div", Object.assign({ className: clsx(`tremolo-piano-black-key`, className), "aria-disabled": __disabled, "data-active": played, style: Object.assign({ left: __position, width: __fill ? __width : (width !== null && width !== void 0 ? width : __width), height: height }, style), onPointerDown: () => {
1194
+ return (jsxRuntime.jsx("div", Object.assign({ className: clsx(`tremolo-piano-black-key`, className), "aria-disabled": __disabled, "data-active": played, style: Object.assign(Object.assign(Object.assign({}, colors), { left: __position, width: __fill ? __width : (width !== null && width !== void 0 ? width : __width), height: height }), style), onPointerDown: () => {
1012
1195
  if (__disabled)
1013
1196
  return;
1014
1197
  setPlayed(true);
@@ -1036,11 +1219,11 @@ const BlackKey = React.forwardRef((_a, ref) => {
1036
1219
  if (__onStopNote)
1037
1220
  __onStopNote(__note);
1038
1221
  }
1039
- } }, props, { children: jsxRuntime.jsx(KeyLabel, Object.assign({ __note: __note, __label: __label, __index: __index }, keyLabelProps)) })));
1222
+ }, onContextMenu: onContextMenu }, props, { children: jsxRuntime.jsx(KeyLabel, Object.assign({ __note: __note, __label: __label, __index: __index }, keyLabelProps)) })));
1040
1223
  });
1041
1224
 
1042
1225
  /** @category Piano */
1043
- const Shortcuts = {
1226
+ const SHORTCUTS = {
1044
1227
  HOME_ROW: {
1045
1228
  keys: [
1046
1229
  'a',
@@ -1064,9 +1247,12 @@ const Shortcuts = {
1064
1247
  },
1065
1248
  };
1066
1249
 
1067
- /** @category Piano */
1250
+ /**
1251
+ * Customizable piano component.
1252
+ * @category Piano
1253
+ */
1068
1254
  function Piano(_a) {
1069
- var { noteRange, glissando = true, midiMax = 127, keyboardShortcuts, fill = false, height = fill ? '100%' : 160, style, className, onPlayNote, onStopNote, label, children } = _a, props = __rest(_a, ["noteRange", "glissando", "midiMax", "keyboardShortcuts", "fill", "height", "style", "className", "onPlayNote", "onStopNote", "label", "children"]);
1255
+ var { noteRange, glissando = true, midiMax = 127, keyboardShortcuts, fill = false, height = fill ? '100%' : 160, style, className, onPlayNote, onStopNote, label, children, onPointerUp, onPointerDown } = _a, props = __rest(_a, ["noteRange", "glissando", "midiMax", "keyboardShortcuts", "fill", "height", "style", "className", "onPlayNote", "onStopNote", "label", "children", "onPointerUp", "onPointerDown"]);
1070
1256
  // -- state and ref ---
1071
1257
  const [pressed, setPressed] = React.useState(false);
1072
1258
  const [whiteNoteWidth, setWhiteNoteWidth] = React.useState(-1);
@@ -1113,15 +1299,11 @@ function Piano(_a) {
1113
1299
  'A#': 5.85,
1114
1300
  B: 6,
1115
1301
  };
1116
- const toNoteName = (n) => {
1117
- const { letter, accidental } = parseNoteName(noteName(n));
1118
- return (letter + accidental);
1119
- };
1120
- const _noteName = toNoteName(note);
1121
- const firstNoteName = toNoteName(noteRange.first);
1122
- const pos = pitchPositions[_noteName] - pitchPositions[firstNoteName];
1302
+ const targetNoteKey = noteKey(note);
1303
+ const firstNoteKey = noteKey(noteRange.first);
1304
+ const pos = pitchPositions[targetNoteKey] - pitchPositions[firstNoteKey];
1123
1305
  const octave = Math.floor((note - noteRange.first) / 12);
1124
- const octaveOffset = noteNames.indexOf(firstNoteName) > noteNames.indexOf(_noteName) ? 1 : 0;
1306
+ const octaveOffset = noteKeys.indexOf(firstNoteKey) > noteKeys.indexOf(targetNoteKey) ? 1 : 0;
1125
1307
  const length = whiteNoteWidth + 3;
1126
1308
  return pos * length + (octave + octaveOffset) * 7 * length;
1127
1309
  }
@@ -1136,6 +1318,9 @@ function Piano(_a) {
1136
1318
  setWhiteNoteWidth(w / whiteNoteCount - 3);
1137
1319
  });
1138
1320
  resizeObserver.observe(parent);
1321
+ return () => {
1322
+ resizeObserver.unobserve(parent);
1323
+ };
1139
1324
  }
1140
1325
  else {
1141
1326
  setWhiteNoteWidth((whiteKeyProps === null || whiteKeyProps === void 0 ? void 0 : whiteKeyProps.width) || defaultWhiteKeyWidth);
@@ -1166,13 +1351,11 @@ function Piano(_a) {
1166
1351
  }
1167
1352
  });
1168
1353
  return (jsxRuntime.jsx("div", Object.assign({ ref: pianoRef, className: clsx('tremolo-piano', className), style: Object.assign({ width: fill ? '100%' : (whiteNoteWidth + 3) * whiteNoteCount, height: height }, style), onPointerDown: (e) => {
1169
- if (props.onPointerDown)
1170
- props.onPointerDown(e);
1171
1354
  setPressed(true);
1355
+ onPointerDown === null || onPointerDown === void 0 ? void 0 : onPointerDown(e);
1172
1356
  }, onPointerUp: (e) => {
1173
- if (props.onPointerUp)
1174
- props.onPointerUp(e);
1175
1357
  setPressed(false);
1358
+ onPointerUp === null || onPointerUp === void 0 ? void 0 : onPointerUp(e);
1176
1359
  } }, props, { children: noteRangeArray.map((note, index) => {
1177
1360
  return isWhiteKey(note) ? (jsxRuntime.jsx(WhiteKey, Object.assign({ ref: keyRefs.current[index], __glissando: glissando && pressed, __position: notePosition(note), __note: note, __disabled: note > midiMax, __index: index, __fill: fill, __width: whiteNoteWidth, __onPlayNote: onPlayNote, __onStopNote: onStopNote, __label: label }, whiteKeyProps), note)) : (jsxRuntime.jsx(BlackKey, Object.assign({ ref: keyRefs.current[index], __glissando: glissando && pressed, __position: notePosition(note), __note: note, __disabled: note > midiMax, __index: index, __fill: fill, __width: whiteNoteWidth * 0.65, __onPlayNote: onPlayNote, __onStopNote: onStopNote, __label: label }, blackKeyProps), note));
1178
1361
  }) })));
@@ -1187,13 +1370,14 @@ const createSliderStore = (initProps) => {
1187
1370
  vertical: false,
1188
1371
  reverse: false,
1189
1372
  disabled: false,
1373
+ readonly: false,
1190
1374
  };
1191
- return createStore()((set) => (Object.assign(Object.assign(Object.assign({}, DEFAULT_PROPS), initProps), { updateMin: (min) => set(() => ({ min: min })), updateMax: (max) => set(() => ({ max: max })), updateStep: (step) => set(() => ({ step: step })), updateSkew: (skew) => set(() => ({ skew: skew })), updateVertical: (vertical) => set(() => ({ vertical: vertical })), updateReverse: (reverse) => set(() => ({ reverse: reverse })), updateDisabled: (disabled) => set(() => ({ disabled: disabled })) })));
1375
+ return createStore()(() => (Object.assign(Object.assign({}, DEFAULT_PROPS), initProps)));
1192
1376
  };
1193
1377
  const SliderContext = React.createContext(null);
1194
1378
  function SliderProvider(_a) {
1195
1379
  var { children } = _a, props = __rest(_a, ["children"]);
1196
- const storeRef = React.useRef();
1380
+ const storeRef = React.useRef(null);
1197
1381
  if (!storeRef.current) {
1198
1382
  storeRef.current = createSliderStore(props);
1199
1383
  }
@@ -1288,10 +1472,11 @@ function Scale(_a) {
1288
1472
  /** @category Slider */
1289
1473
  const defaultThumbSize = 22;
1290
1474
  /** @category Slider */
1291
- const SliderThumb = React.forwardRef(({ size, width = defaultThumbSize, height = defaultThumbSize, children, className, style, __percent, }, ref) => {
1475
+ const SliderThumb = React.forwardRef(({ size, width = defaultThumbSize, height = defaultThumbSize, color, children, className, style, __percent, }, ref) => {
1292
1476
  const wrapperRef = React.useRef(null);
1293
1477
  const vertical = useSliderContext((s) => s.vertical);
1294
1478
  const disabled = useSliderContext((s) => s.disabled);
1479
+ const readonly = useSliderContext((s) => s.readonly);
1295
1480
  React.useImperativeHandle(ref, () => {
1296
1481
  return {
1297
1482
  focus() {
@@ -1311,15 +1496,14 @@ const SliderThumb = React.forwardRef(({ size, width = defaultThumbSize, height =
1311
1496
  // default slider thumb
1312
1497
  jsxRuntime.jsx("div", { ref: wrapperRef, className: clsx('tremolo-slider-thumb', className),
1313
1498
  // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
1314
- tabIndex: 0, "aria-disabled": disabled, style: Object.assign({ width: size !== null && size !== void 0 ? size : width, height: size !== null && size !== void 0 ? size : height }, style) })) }));
1499
+ tabIndex: 0, "aria-disabled": disabled, "aria-readonly": readonly, style: Object.assign(Object.assign({ '--color': color }, { width: size !== null && size !== void 0 ? size : width, height: size !== null && size !== void 0 ? size : height }), style) })) }));
1315
1500
  });
1316
1501
 
1317
- /** @category Slider */
1318
1502
  const defaultLength = 140;
1319
- /** @category Slider */
1320
1503
  const defaultThickness = 10;
1321
1504
  /** @category Slider */
1322
- function SliderTrack({ length = defaultLength, thickness = defaultThickness, active, inactive, children, className, style, defaultStyle = true, __thumb, __percent = 0, }) {
1505
+ function SliderTrack(_a) {
1506
+ var { length = defaultLength, thickness = defaultThickness, active, inactive, children, className, style, defaultStyle = true, __thumb, __percent = 0 } = _a, props = __rest(_a, ["length", "thickness", "active", "inactive", "children", "className", "style", "defaultStyle", "__thumb", "__percent"]);
1323
1507
  const vertical = useSliderContext((s) => s.vertical);
1324
1508
  const reverse = useSliderContext((s) => s.reverse);
1325
1509
  const disabled = useSliderContext((s) => s.disabled);
@@ -1328,11 +1512,11 @@ function SliderTrack({ length = defaultLength, thickness = defaultThickness, act
1328
1512
  '--active': active,
1329
1513
  '--inactive': inactive,
1330
1514
  };
1331
- return (jsxRuntime.jsxs("div", { className: clsx('tremolo-slider-track', className), "aria-disabled": disabled, "data-vertical": vertical, style: !defaultStyle
1515
+ return (jsxRuntime.jsxs("div", Object.assign({ className: clsx('tremolo-slider-track', className), "aria-disabled": disabled, "data-vertical": vertical, style: !defaultStyle
1332
1516
  ? style
1333
1517
  : Object.assign(Object.assign(Object.assign({}, colors), { background: xor(vertical, reverse)
1334
1518
  ? `linear-gradient(to ${direction}, var(--inactive, #eee) ${__percent}%, var(--active, #7998ec) ${__percent}%)`
1335
- : `linear-gradient(to ${direction}, var(--active, #7998ec) ${__percent}%, var(--inactive, #eee) ${__percent}%)`, borderRadius: styleHelper(thickness, '/', 2), width: !vertical ? length : thickness, height: vertical ? length : thickness }), style), children: [children, __thumb] }));
1519
+ : `linear-gradient(to ${direction}, var(--active, #7998ec) ${__percent}%, var(--inactive, #eee) ${__percent}%)`, borderRadius: styleHelper(thickness, '/', 2), width: !vertical ? length : thickness, height: vertical ? length : thickness }), style) }, props, { children: [children, __thumb] })));
1336
1520
  }
1337
1521
 
1338
1522
  /**
@@ -1340,8 +1524,8 @@ function SliderTrack({ length = defaultLength, thickness = defaultThickness, act
1340
1524
  * @category Slider
1341
1525
  */
1342
1526
  const Slider = React.forwardRef((_a, ref) => {
1343
- var _b, _c, _d;
1344
- var { value, min, max, step = 1, skew = 1, vertical = false, reverse = false, className, style, bodyNoSelect = true, wheel = ['raw', 1], keyboard = ['raw', 1], disabled = false, readonly = false, onChange, children } = _a, props = __rest(_a, ["value", "min", "max", "step", "skew", "vertical", "reverse", "className", "style", "bodyNoSelect", "wheel", "keyboard", "disabled", "readonly", "onChange", "children"]);
1527
+ var _b;
1528
+ var { 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 } = _a, props = __rest(_a, ["value", "min", "max", "step", "skew", "vertical", "reverse", "bodyNoSelect", "wheel", "keyboard", "disabled", "readonly", "onChange", "onDragStart", "onDragEnd", "className", "style", "children", "onFocus", "onBlur", "onPointerDown", "onKeyDown"]);
1345
1529
  // -- state and ref ---
1346
1530
  const trackElementRef = React.useRef(null);
1347
1531
  const thumbRef = React.useRef(null);
@@ -1400,8 +1584,21 @@ const Slider = React.forwardRef((_a, ref) => {
1400
1584
  ? normalizeValue(mouseY, y1, y2)
1401
1585
  : normalizeValue(mouseX, x1, x2);
1402
1586
  const v = rawValue(displayReversed ? 1 - n : n, min, max, skew);
1403
- onChange(clamp(stepValue(v, step), min, max));
1587
+ const v2 = clamp(stepValue(v, step), min, max);
1588
+ onChange(v2);
1589
+ return v2;
1404
1590
  };
1591
+ const updateValueByEvent = React.useCallback((eventType, x) => {
1592
+ let newValue;
1593
+ if (eventType == 'normalized') {
1594
+ const n = normalizeValue(value, min, max, skew);
1595
+ newValue = rawValue(n + x, min, max, skew);
1596
+ }
1597
+ else {
1598
+ newValue = value + x;
1599
+ }
1600
+ return clamp(stepValue(newValue, step), min, max);
1601
+ }, [max, min, skew, step, value]);
1405
1602
  const handleKeyDown = React.useCallback((event) => {
1406
1603
  if (!keyboard || !onChange || readonly)
1407
1604
  return;
@@ -1411,17 +1608,9 @@ const Slider = React.forwardRef((_a, ref) => {
1411
1608
  let x = key == 'ArrowRight' || key == 'ArrowUp' ? keyboard[1] : -keyboard[1];
1412
1609
  if (reverse)
1413
1610
  x *= -1;
1414
- let v;
1415
- if (keyboard[0] == 'normalized') {
1416
- const n = normalizeValue(value, min, max, skew);
1417
- v = rawValue(n + x, min, max, skew);
1418
- }
1419
- else {
1420
- v = value + x;
1421
- }
1422
- onChange(clamp(stepValue(v, step), min, max));
1611
+ onChange(updateValueByEvent(keyboard[0], x));
1423
1612
  }
1424
- }, [value, min, max, step, skew, reverse, keyboard, onChange, readonly]);
1613
+ }, [keyboard, onChange, readonly, reverse, updateValueByEvent]);
1425
1614
  // --- hooks ---
1426
1615
  const touchMoveRefCallback = useRefCallbackEvent('touchmove', handleValue, { passive: false }, [min, max, skew, step, vertical, reverse, onChange, readonly]);
1427
1616
  const wheelRefCallback = useRefCallbackEvent('wheel', (event) => {
@@ -1439,23 +1628,18 @@ const Slider = React.forwardRef((_a, ref) => {
1439
1628
  }
1440
1629
  if (vertical && reverse)
1441
1630
  x *= -1;
1442
- let v;
1443
- if (wheel[0] == 'normalized') {
1444
- const n = normalizeValue(value, min, max, skew);
1445
- v = rawValue(n + x, min, max, skew);
1446
- }
1447
- else {
1448
- v = value + x;
1449
- }
1450
- onChange(clamp(stepValue(v, step), min, max));
1631
+ onChange(updateValueByEvent(wheel[0], x));
1451
1632
  }, {
1452
1633
  passive: false,
1453
- }, [wheel, value, min, max, skew, step]);
1634
+ }, [wheel, vertical, readonly, onChange, updateValueByEvent]);
1454
1635
  useEventListener(globalThis.window, 'mousemove', (event) => {
1455
1636
  handleValue(event);
1456
1637
  });
1457
1638
  useEventListener(globalThis.window, 'mouseup', () => {
1639
+ if (!thumbDragged.current || readonly)
1640
+ return;
1458
1641
  thumbDragged.current = false;
1642
+ onDragEnd === null || onDragEnd === void 0 ? void 0 : onDragEnd(value);
1459
1643
  if (bodyNoSelect)
1460
1644
  removeNoSelect();
1461
1645
  });
@@ -1471,13 +1655,27 @@ const Slider = React.forwardRef((_a, ref) => {
1471
1655
  },
1472
1656
  };
1473
1657
  }, []);
1474
- return (jsxRuntime.jsx(SliderProvider, { min: min, max: max, step: step, skew: skew, vertical: vertical, reverse: reverse, disabled: disabled, children: jsxRuntime.jsx("div", Object.assign({ className: clsx('tremolo-slider', className), ref: (div) => {
1658
+ return (jsxRuntime.jsx(SliderProvider, { min: min, max: max, step: step, skew: skew, vertical: vertical, reverse: reverse, disabled: disabled, readonly: readonly, children: jsxRuntime.jsx("div", Object.assign({ className: clsx('tremolo-slider', className), ref: (div) => {
1475
1659
  wheelRefCallback(div);
1476
1660
  touchMoveRefCallback(div);
1477
1661
  }, tabIndex: -1, role: "slider", "aria-valuenow": value, "aria-valuemin": min, "aria-valuemax": max, "aria-orientation": vertical ? 'vertical' : 'horizontal', "aria-disabled": disabled, "aria-readonly": readonly, style: Object.assign({ margin: `calc(${styleHelper((_b = thumbProps === null || thumbProps === void 0 ? void 0 : thumbProps.size) !== null && _b !== void 0 ? _b : 22)} / 2)` }, style), onPointerDown: (event) => {
1478
1662
  thumbDragged.current = true;
1479
- handleValue(event);
1480
- }, onKeyDown: handleKeyDown, onFocus: (_c = thumbRef.current) === null || _c === void 0 ? void 0 : _c.focus, onBlur: (_d = thumbRef.current) === null || _d === void 0 ? void 0 : _d.blur }, props, { children: jsxRuntime.jsxs("div", { style: {
1663
+ const v = handleValue(event);
1664
+ if (!readonly)
1665
+ onDragStart === null || onDragStart === void 0 ? void 0 : onDragStart(v !== null && v !== void 0 ? v : value);
1666
+ onPointerDown === null || onPointerDown === void 0 ? void 0 : onPointerDown(event);
1667
+ }, onKeyDown: (event) => {
1668
+ handleKeyDown(event);
1669
+ onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event);
1670
+ }, onFocus: (event) => {
1671
+ var _a;
1672
+ (_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.focus();
1673
+ onFocus === null || onFocus === void 0 ? void 0 : onFocus(event);
1674
+ }, onBlur: (event) => {
1675
+ var _a;
1676
+ (_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.blur();
1677
+ onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
1678
+ } }, props, { children: jsxRuntime.jsxs("div", { style: {
1481
1679
  display: 'flex',
1482
1680
  flexDirection: vertical ? 'row' : 'column',
1483
1681
  width: !vertical ? '100%' : undefined,
@@ -1496,12 +1694,14 @@ function WheelObserver(props) {
1496
1694
  }
1497
1695
 
1498
1696
  /** @category XYPad */
1499
- function XYPadArea({ width = 120, height = 120, bg = '#eee', children, className, style, __thumb, }) {
1500
- return (jsxRuntime.jsxs("div", { className: clsx('tremolo-xy-pad-area', className), style: Object.assign({ position: 'relative', width: width, height: height, background: bg }, style), children: [children, __thumb] }));
1697
+ function XYPadArea(_a) {
1698
+ var { width = 120, height = 120, color, children, className, style, __thumb } = _a, props = __rest(_a, ["width", "height", "color", "children", "className", "style", "__thumb"]);
1699
+ return (jsxRuntime.jsxs("div", Object.assign({ className: clsx('tremolo-xy-pad-area', className), style: Object.assign(Object.assign({ '--color': color }, { width: width, height: height }), style) }, props, { children: [children, __thumb] })));
1501
1700
  }
1502
1701
 
1503
1702
  /** @category XYPad */
1504
- const XYPadThumb = React.forwardRef(({ size, width = 22, height = 22, children, wrapperClassName, wrapperStyle, className, style, __disabled, __css, }, ref) => {
1703
+ const XYPadThumb = React.forwardRef((_a, ref) => {
1704
+ var { size, width = 22, height = 22, color, children, wrapperClassName, wrapperStyle, className, style, __disabled, __css } = _a, props = __rest(_a, ["size", "width", "height", "color", "children", "wrapperClassName", "wrapperStyle", "className", "style", "__disabled", "__css"]);
1505
1705
  const wrapperRef = React.useRef(null);
1506
1706
  React.useImperativeHandle(ref, () => {
1507
1707
  return {
@@ -1515,11 +1715,11 @@ const XYPadThumb = React.forwardRef(({ size, width = 22, height = 22, children,
1515
1715
  },
1516
1716
  };
1517
1717
  }, []);
1518
- return (jsxRuntime.jsx("div", { className: clsx('tremolo-xy-pad-thumb-wrapper', wrapperClassName), style: Object.assign(Object.assign({}, __css), wrapperStyle), children: children ? (children) : (
1718
+ return (jsxRuntime.jsx("div", Object.assign({ className: clsx('tremolo-xy-pad-thumb-wrapper', wrapperClassName), style: Object.assign(Object.assign({}, __css), wrapperStyle) }, props, { children: children ? (children) : (
1519
1719
  // default thumb
1520
1720
  jsxRuntime.jsx("div", { ref: wrapperRef, className: clsx('tremolo-xy-pad-thumb', className),
1521
1721
  // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
1522
- tabIndex: 0, "aria-disabled": __disabled, style: Object.assign({ width: size !== null && size !== void 0 ? size : width, height: size !== null && size !== void 0 ? size : height }, style) })) }));
1722
+ tabIndex: 0, "aria-disabled": __disabled, style: Object.assign(Object.assign({ '--color': color }, { width: size !== null && size !== void 0 ? size : width, height: size !== null && size !== void 0 ? size : height }), style) })) })));
1523
1723
  });
1524
1724
 
1525
1725
  const defaultValueOptions = {
@@ -1534,8 +1734,8 @@ const defaultValueOptions = {
1534
1734
  * @category XYPad
1535
1735
  */
1536
1736
  const XYPad = React.forwardRef((_a, ref) => {
1537
- var _b, _c, _d;
1538
- var { x: _x, y: _y, className, style, bodyNoSelect = true, disabled = false, readonly = false, onChange, children } = _a, props = __rest(_a, ["x", "y", "className", "style", "bodyNoSelect", "disabled", "readonly", "onChange", "children"]);
1737
+ var _b;
1738
+ var { x: _x, y: _y, className, style, bodyNoSelect = true, disabled = false, readonly = false, onChange, onDragStart, onDragEnd, onPointerDown, onKeyDown, onFocus, onBlur, children } = _a, props = __rest(_a, ["x", "y", "className", "style", "bodyNoSelect", "disabled", "readonly", "onChange", "onDragStart", "onDragEnd", "onPointerDown", "onKeyDown", "onFocus", "onBlur", "children"]);
1539
1739
  const x = React.useMemo(() => {
1540
1740
  return Object.assign(Object.assign({}, defaultValueOptions), _x);
1541
1741
  }, [_x]);
@@ -1592,7 +1792,19 @@ const XYPad = React.forwardRef((_a, ref) => {
1592
1792
  const newX = clamp(stepValue(vx, x.step), x.min, x.max);
1593
1793
  const newY = clamp(stepValue(vy, y.step), y.min, y.max);
1594
1794
  onChange(newX, newY);
1795
+ return [newX, newY];
1595
1796
  };
1797
+ const updateValueByEvent = React.useCallback((eventType, target, x) => {
1798
+ let v;
1799
+ if (eventType == 'normalized') {
1800
+ const n = normalizeValue(target.value, target.min, target.max, target.skew);
1801
+ v = rawValue(n + x, target.min, target.max, target.skew);
1802
+ }
1803
+ else {
1804
+ v = target.value + x;
1805
+ }
1806
+ return clamp(stepValue(v, target.step), target.min, target.max);
1807
+ }, []);
1596
1808
  const handleKeyDown = React.useCallback((event) => {
1597
1809
  if (!onChange || readonly)
1598
1810
  return;
@@ -1608,15 +1820,7 @@ const XYPad = React.forwardRef((_a, ref) => {
1608
1820
  delta *= -1;
1609
1821
  if (target.reverse)
1610
1822
  delta *= -1;
1611
- let v;
1612
- if (target.keyboard[0] == 'normalized') {
1613
- const n = normalizeValue(target.value, target.min, target.max, target.skew);
1614
- v = rawValue(n + delta, target.min, target.max, target.skew);
1615
- }
1616
- else {
1617
- v = target.value + delta;
1618
- }
1619
- const newValue = clamp(stepValue(v, target.step), target.min, target.max);
1823
+ const newValue = updateValueByEvent(target.keyboard[0], target, delta);
1620
1824
  if (isHorizontal) {
1621
1825
  onChange(newValue, y.value);
1622
1826
  }
@@ -1624,7 +1828,7 @@ const XYPad = React.forwardRef((_a, ref) => {
1624
1828
  onChange(x.value, newValue);
1625
1829
  }
1626
1830
  }
1627
- }, [x, y, onChange, readonly]);
1831
+ }, [onChange, readonly, x, y, updateValueByEvent]);
1628
1832
  // --- hooks ---
1629
1833
  const touchMoveRefCallback = useRefCallbackEvent('touchmove', handleValue, { passive: false }, [x, onChange, readonly]);
1630
1834
  const wheelRefCallback = useRefCallbackEvent('wheel', (event) => {
@@ -1639,15 +1843,7 @@ const XYPad = React.forwardRef((_a, ref) => {
1639
1843
  delta *= -1;
1640
1844
  if (target.reverse)
1641
1845
  delta *= -1;
1642
- let v;
1643
- if (target.wheel[0] == 'normalized') {
1644
- const n = normalizeValue(target.value, target.min, target.max, target.skew);
1645
- v = rawValue(n + delta, target.min, target.max, target.skew);
1646
- }
1647
- else {
1648
- v = target.value + delta;
1649
- }
1650
- const newValue = clamp(stepValue(v, target.step), target.min, target.max);
1846
+ const newValue = updateValueByEvent(target.wheel[0], target, delta);
1651
1847
  if (event.shiftKey) {
1652
1848
  onChange(newValue, y.value);
1653
1849
  }
@@ -1656,12 +1852,15 @@ const XYPad = React.forwardRef((_a, ref) => {
1656
1852
  }
1657
1853
  }, {
1658
1854
  passive: false,
1659
- }, [x, y]);
1855
+ }, [x, y, onChange, readonly, updateValueByEvent]);
1660
1856
  useEventListener(globalThis.window, 'mousemove', (event) => {
1661
1857
  handleValue(event);
1662
1858
  });
1663
1859
  useEventListener(globalThis.window, 'mouseup', () => {
1860
+ if (!thumbDragged.current || readonly)
1861
+ return;
1664
1862
  thumbDragged.current = false;
1863
+ onDragEnd === null || onDragEnd === void 0 ? void 0 : onDragEnd(x.value, y.value);
1665
1864
  if (bodyNoSelect)
1666
1865
  removeNoSelect();
1667
1866
  });
@@ -1683,14 +1882,42 @@ const XYPad = React.forwardRef((_a, ref) => {
1683
1882
  wheelRefCallback(div);
1684
1883
  touchMoveRefCallback(div);
1685
1884
  }, tabIndex: -1, "aria-disabled": disabled, "aria-readonly": readonly, style: Object.assign({ margin: `calc(${styleHelper((_b = thumbProps === null || thumbProps === void 0 ? void 0 : thumbProps.size) !== null && _b !== void 0 ? _b : 22)} / 2)`, cursor: readonly ? 'not-allowed' : 'pointer', WebkitTapHighlightColor: 'transparent' }, style), onPointerDown: (event) => {
1885
+ var _a, _b;
1686
1886
  thumbDragged.current = true;
1687
- handleValue(event);
1688
- }, onKeyDown: handleKeyDown, onFocus: (_c = thumbRef.current) === null || _c === void 0 ? void 0 : _c.focus, onBlur: (_d = thumbRef.current) === null || _d === void 0 ? void 0 : _d.blur }, props, { children: jsxRuntime.jsx("div", { className: "tremolo-xy-pad-area-wrapper", ref: areaElementRef, children: jsxRuntime.jsx(XYPadArea, Object.assign({ __thumb: jsxRuntime.jsx(XYPadThumb, Object.assign({ ref: thumbRef, __disabled: disabled, __css: {
1887
+ const v = handleValue(event);
1888
+ if (!readonly)
1889
+ onDragStart === null || onDragStart === void 0 ? void 0 : onDragStart((_a = v === null || v === void 0 ? void 0 : v[0]) !== null && _a !== void 0 ? _a : x.value, (_b = v === null || v === void 0 ? void 0 : v[1]) !== null && _b !== void 0 ? _b : y.value);
1890
+ onPointerDown === null || onPointerDown === void 0 ? void 0 : onPointerDown(event);
1891
+ }, onKeyDown: (event) => {
1892
+ handleKeyDown(event);
1893
+ onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event);
1894
+ }, onFocus: (event) => {
1895
+ var _a;
1896
+ (_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.focus();
1897
+ onFocus === null || onFocus === void 0 ? void 0 : onFocus(event);
1898
+ }, onBlur: (event) => {
1899
+ var _a;
1900
+ (_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.blur();
1901
+ onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
1902
+ } }, props, { children: jsxRuntime.jsx("div", { className: "tremolo-xy-pad-area-wrapper", ref: areaElementRef, children: jsxRuntime.jsx(XYPadArea, Object.assign({ __thumb: jsxRuntime.jsx(XYPadThumb, Object.assign({ ref: thumbRef, __disabled: disabled, __css: {
1689
1903
  top: `${percentY}%`,
1690
1904
  left: `${percentX}%`,
1691
1905
  } }, thumbProps)) }, areaProps)) }) })));
1692
1906
  });
1693
1907
 
1908
+ const useAnimationFrame = (callback = () => { }, deps = []) => {
1909
+ const reqIdRef = React.useRef(-1);
1910
+ const loop = React.useCallback(() => {
1911
+ reqIdRef.current = requestAnimationFrame(loop);
1912
+ callback();
1913
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1914
+ }, [callback, ...deps]);
1915
+ React.useEffect(() => {
1916
+ reqIdRef.current = requestAnimationFrame(loop);
1917
+ return () => cancelAnimationFrame(reqIdRef.current);
1918
+ }, [loop]);
1919
+ };
1920
+
1694
1921
  exports.AnimationCanvas = AnimationCanvas;
1695
1922
  exports.AnimationKnob = AnimationKnob;
1696
1923
  exports.BlackKey = BlackKey;
@@ -1701,9 +1928,9 @@ exports.KeyLabel = KeyLabel;
1701
1928
  exports.Knob = Knob;
1702
1929
  exports.NumberInput = NumberInput;
1703
1930
  exports.Piano = Piano;
1931
+ exports.SHORTCUTS = SHORTCUTS;
1704
1932
  exports.Scale = Scale;
1705
1933
  exports.ScaleOption = ScaleOption;
1706
- exports.Shortcuts = Shortcuts;
1707
1934
  exports.Slider = Slider;
1708
1935
  exports.SliderThumb = SliderThumb;
1709
1936
  exports.SliderTrack = SliderTrack;
@@ -1713,5 +1940,10 @@ exports.WhiteKey = WhiteKey;
1713
1940
  exports.XYPad = XYPad;
1714
1941
  exports.XYPadArea = XYPadArea;
1715
1942
  exports.XYPadThumb = XYPadThumb;
1943
+ exports.useAnimationFrame = useAnimationFrame;
1944
+ exports.useDrag = useDrag;
1945
+ exports.useEventListener = useEventListener;
1946
+ exports.useInterval = useInterval;
1947
+ exports.useLongPress = useLongPress;
1716
1948
  exports.useSliderContext = useSliderContext;
1717
1949
  //# sourceMappingURL=index.js.map