@tremolo-ui/react 0.0.6 → 0.0.8

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 (133) hide show
  1. package/dist/cjs/index.js +954 -217
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/types/components/AnimationCanvas/index.d.ts +16 -9
  4. package/dist/cjs/types/components/AnimationCanvas/index.d.ts.map +1 -1
  5. package/dist/cjs/types/components/AnimationKnob/index.d.ts +53 -0
  6. package/dist/cjs/types/components/AnimationKnob/index.d.ts.map +1 -0
  7. package/dist/cjs/types/components/DragObserver/index.d.ts +19 -0
  8. package/dist/cjs/types/components/DragObserver/index.d.ts.map +1 -0
  9. package/dist/cjs/types/components/Knob/index.d.ts +26 -26
  10. package/dist/cjs/types/components/Knob/index.d.ts.map +1 -1
  11. package/dist/cjs/types/components/NumberInput/index.d.ts +2 -5
  12. package/dist/cjs/types/components/NumberInput/index.d.ts.map +1 -1
  13. package/dist/cjs/types/components/Piano/BlackKey.d.ts +3 -3
  14. package/dist/cjs/types/components/Piano/BlackKey.d.ts.map +1 -1
  15. package/dist/cjs/types/components/Piano/KeyLabel.d.ts +5 -8
  16. package/dist/cjs/types/components/Piano/KeyLabel.d.ts.map +1 -1
  17. package/dist/cjs/types/components/Piano/WhiteKey.d.ts +3 -3
  18. package/dist/cjs/types/components/Piano/WhiteKey.d.ts.map +1 -1
  19. package/dist/cjs/types/components/Piano/index.d.ts +4 -5
  20. package/dist/cjs/types/components/Piano/index.d.ts.map +1 -1
  21. package/dist/cjs/types/components/Piano/key.d.ts +4 -7
  22. package/dist/cjs/types/components/Piano/key.d.ts.map +1 -1
  23. package/dist/cjs/types/components/Piano/keyboardShortcuts.d.ts.map +1 -1
  24. package/dist/cjs/types/components/Slider/Scale.d.ts +11 -0
  25. package/dist/cjs/types/components/Slider/Scale.d.ts.map +1 -0
  26. package/dist/cjs/types/components/Slider/ScaleOption.d.ts +25 -0
  27. package/dist/cjs/types/components/Slider/ScaleOption.d.ts.map +1 -0
  28. package/dist/cjs/types/components/Slider/Thumb.d.ts +5 -13
  29. package/dist/cjs/types/components/Slider/Thumb.d.ts.map +1 -1
  30. package/dist/cjs/types/components/Slider/Track.d.ts +10 -21
  31. package/dist/cjs/types/components/Slider/Track.d.ts.map +1 -1
  32. package/dist/cjs/types/components/Slider/context.d.ts +21 -0
  33. package/dist/cjs/types/components/Slider/context.d.ts.map +1 -0
  34. package/dist/cjs/types/components/Slider/index.d.ts +18 -12
  35. package/dist/cjs/types/components/Slider/index.d.ts.map +1 -1
  36. package/dist/cjs/types/components/WheelObserver/index.d.ts +24 -0
  37. package/dist/cjs/types/components/WheelObserver/index.d.ts.map +1 -0
  38. package/dist/cjs/types/components/XYPad/Area.d.ts +13 -0
  39. package/dist/cjs/types/components/XYPad/Area.d.ts.map +1 -0
  40. package/dist/cjs/types/components/XYPad/Thumb.d.ts +19 -0
  41. package/dist/cjs/types/components/XYPad/Thumb.d.ts.map +1 -0
  42. package/dist/cjs/types/components/XYPad/index.d.ts +39 -0
  43. package/dist/cjs/types/components/XYPad/index.d.ts.map +1 -0
  44. package/dist/cjs/types/hooks/useCallbackRef.d.ts.map +1 -1
  45. package/dist/cjs/types/index.d.ts +11 -0
  46. package/dist/cjs/types/index.d.ts.map +1 -1
  47. package/dist/esm/index.js +932 -218
  48. package/dist/esm/index.js.map +1 -1
  49. package/dist/esm/types/components/AnimationCanvas/index.d.ts +16 -9
  50. package/dist/esm/types/components/AnimationCanvas/index.d.ts.map +1 -1
  51. package/dist/esm/types/components/AnimationKnob/index.d.ts +53 -0
  52. package/dist/esm/types/components/AnimationKnob/index.d.ts.map +1 -0
  53. package/dist/esm/types/components/DragObserver/index.d.ts +19 -0
  54. package/dist/esm/types/components/DragObserver/index.d.ts.map +1 -0
  55. package/dist/esm/types/components/Knob/index.d.ts +26 -26
  56. package/dist/esm/types/components/Knob/index.d.ts.map +1 -1
  57. package/dist/esm/types/components/NumberInput/index.d.ts +2 -5
  58. package/dist/esm/types/components/NumberInput/index.d.ts.map +1 -1
  59. package/dist/esm/types/components/Piano/BlackKey.d.ts +3 -3
  60. package/dist/esm/types/components/Piano/BlackKey.d.ts.map +1 -1
  61. package/dist/esm/types/components/Piano/KeyLabel.d.ts +5 -8
  62. package/dist/esm/types/components/Piano/KeyLabel.d.ts.map +1 -1
  63. package/dist/esm/types/components/Piano/WhiteKey.d.ts +3 -3
  64. package/dist/esm/types/components/Piano/WhiteKey.d.ts.map +1 -1
  65. package/dist/esm/types/components/Piano/index.d.ts +4 -5
  66. package/dist/esm/types/components/Piano/index.d.ts.map +1 -1
  67. package/dist/esm/types/components/Piano/key.d.ts +4 -7
  68. package/dist/esm/types/components/Piano/key.d.ts.map +1 -1
  69. package/dist/esm/types/components/Piano/keyboardShortcuts.d.ts.map +1 -1
  70. package/dist/esm/types/components/Slider/Scale.d.ts +11 -0
  71. package/dist/esm/types/components/Slider/Scale.d.ts.map +1 -0
  72. package/dist/esm/types/components/Slider/ScaleOption.d.ts +25 -0
  73. package/dist/esm/types/components/Slider/ScaleOption.d.ts.map +1 -0
  74. package/dist/esm/types/components/Slider/Thumb.d.ts +5 -13
  75. package/dist/esm/types/components/Slider/Thumb.d.ts.map +1 -1
  76. package/dist/esm/types/components/Slider/Track.d.ts +10 -21
  77. package/dist/esm/types/components/Slider/Track.d.ts.map +1 -1
  78. package/dist/esm/types/components/Slider/context.d.ts +21 -0
  79. package/dist/esm/types/components/Slider/context.d.ts.map +1 -0
  80. package/dist/esm/types/components/Slider/index.d.ts +18 -12
  81. package/dist/esm/types/components/Slider/index.d.ts.map +1 -1
  82. package/dist/esm/types/components/WheelObserver/index.d.ts +24 -0
  83. package/dist/esm/types/components/WheelObserver/index.d.ts.map +1 -0
  84. package/dist/esm/types/components/XYPad/Area.d.ts +13 -0
  85. package/dist/esm/types/components/XYPad/Area.d.ts.map +1 -0
  86. package/dist/esm/types/components/XYPad/Thumb.d.ts +19 -0
  87. package/dist/esm/types/components/XYPad/Thumb.d.ts.map +1 -0
  88. package/dist/esm/types/components/XYPad/index.d.ts +39 -0
  89. package/dist/esm/types/components/XYPad/index.d.ts.map +1 -0
  90. package/dist/esm/types/hooks/useCallbackRef.d.ts.map +1 -1
  91. package/dist/esm/types/index.d.ts +11 -0
  92. package/dist/esm/types/index.d.ts.map +1 -1
  93. package/dist/styles/index.css +241 -0
  94. package/package.json +2 -5
  95. package/src/components/AnimationCanvas/index.tsx +87 -57
  96. package/src/components/AnimationKnob/index.tsx +305 -0
  97. package/src/components/DragObserver/index.tsx +94 -0
  98. package/src/components/Knob/index.css +48 -0
  99. package/src/components/Knob/index.tsx +152 -165
  100. package/src/components/NumberInput/index.css +25 -0
  101. package/src/components/NumberInput/index.tsx +8 -41
  102. package/src/components/Piano/BlackKey.tsx +104 -98
  103. package/src/components/Piano/KeyLabel.tsx +18 -40
  104. package/src/components/Piano/WhiteKey.tsx +92 -87
  105. package/src/components/Piano/index.css +63 -0
  106. package/src/components/Piano/index.tsx +78 -61
  107. package/src/components/Piano/key.ts +4 -7
  108. package/src/components/Piano/keyboardShortcuts.ts +23 -4
  109. package/src/components/Slider/Scale.tsx +55 -0
  110. package/src/components/Slider/ScaleOption.tsx +103 -0
  111. package/src/components/Slider/Thumb.tsx +69 -76
  112. package/src/components/Slider/Track.tsx +37 -48
  113. package/src/components/Slider/context.tsx +66 -0
  114. package/src/components/Slider/index.css +76 -0
  115. package/src/components/Slider/index.tsx +256 -298
  116. package/src/components/WheelObserver/index.tsx +47 -0
  117. package/src/components/XYPad/Area.tsx +40 -0
  118. package/src/components/XYPad/Thumb.tsx +92 -0
  119. package/src/components/XYPad/index.css +24 -0
  120. package/src/components/XYPad/index.tsx +317 -0
  121. package/src/hooks/useCallbackRef.ts +1 -0
  122. package/src/hooks/useRefCallbackEvent.ts +1 -0
  123. package/src/index.ts +12 -0
  124. package/dist/cjs/types/components/Slider/type.d.ts +0 -7
  125. package/dist/cjs/types/components/Slider/type.d.ts.map +0 -1
  126. package/dist/cjs/types/system/pseudo.d.ts +0 -14
  127. package/dist/cjs/types/system/pseudo.d.ts.map +0 -1
  128. package/dist/esm/types/components/Slider/type.d.ts +0 -7
  129. package/dist/esm/types/components/Slider/type.d.ts.map +0 -1
  130. package/dist/esm/types/system/pseudo.d.ts +0 -14
  131. package/dist/esm/types/system/pseudo.d.ts.map +0 -1
  132. package/src/components/Slider/type.ts +0 -16
  133. package/src/system/pseudo.ts +0 -55
package/dist/esm/index.js CHANGED
@@ -1,6 +1,5 @@
1
- import { jsxs, Fragment, jsx } from '@emotion/react/jsx-runtime';
2
- import { css, Global } from '@emotion/react';
3
- import React, { useRef, useEffect, useInsertionEffect, useCallback, useMemo, useState, forwardRef, useImperativeHandle } from 'react';
1
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
+ import React, { useRef, useCallback, useEffect, useInsertionEffect, useMemo, useState, forwardRef, useImperativeHandle, createRef, createContext, useContext } from 'react';
4
3
 
5
4
  /******************************************************************************
6
5
  Copyright (c) Microsoft Corporation.
@@ -67,20 +66,17 @@ function setDprConfig(canvas, context, width, height, dpr) {
67
66
  canvas.style.width = `${width}px`;
68
67
  canvas.style.height = `${height}px`;
69
68
  }
70
- /**
71
- * A simple animatable canvas with requestAnimationFrame()
72
- */
73
69
  function AnimationCanvas(_a) {
74
- var { width = 100, height = 100, style, relativeSize = false, reduceFlickering = true, options, init, draw, className } = _a, props = __rest(_a, ["width", "height", "style", "relativeSize", "reduceFlickering", "options", "init", "draw", "className"]);
70
+ 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"]);
75
71
  const canvasRef = useRef(null);
76
72
  const memoCanvasRef = useRef(null);
77
73
  const reqIdRef = useRef();
78
74
  const widthRef = useRef(0);
79
75
  const heightRef = useRef(0);
80
- const loop = (context, width, height, count) => {
76
+ const loop = useCallback((context, width, height, count) => {
81
77
  reqIdRef.current = requestAnimationFrame(() => loop(context, width, height, count + 1));
82
78
  draw(context, width, height, count + 1);
83
- };
79
+ }, [draw]);
84
80
  useEffect(() => {
85
81
  if (!canvasRef.current)
86
82
  return;
@@ -94,30 +90,34 @@ function AnimationCanvas(_a) {
94
90
  if (!parent)
95
91
  throw new Error("Canvas doesn't have a parent element.");
96
92
  const memoCanvas = memoCanvasRef.current;
97
- const memoContext = memoCanvas === null || memoCanvas === undefined ? undefined : memoCanvas.getContext('2d', options);
98
- const resizeObserver = new ResizeObserver(() => {
99
- // console.log('resize')
100
- // Prevents loss of some context when the canvas is resized
101
- const contextMemo = {};
102
- for (const prop of drawingState) {
103
- contextMemo[prop] = context[prop];
104
- }
105
- const w = Math.floor(parent.clientWidth);
106
- const h = Math.floor(parent.clientHeight);
107
- if (reduceFlickering && memoCanvas && memoContext) {
108
- memoCanvas.width = w * dpr;
109
- memoCanvas.height = h * dpr;
110
- memoContext.scale(1 / dpr, 1 / dpr);
111
- memoContext.drawImage(context.canvas, 0, 0);
112
- }
113
- setDprConfig(canvas, context, w, h, dpr);
114
- widthRef.current = w;
115
- heightRef.current = h;
116
- for (const prop of drawingState) {
117
- context[prop] = contextMemo[prop];
118
- }
119
- if (reduceFlickering && memoContext) {
120
- context.drawImage(memoContext.canvas, 0, 0);
93
+ const memoContext = memoCanvas === null || memoCanvas === void 0 ? void 0 : memoCanvas.getContext('2d', options);
94
+ const resizeObserver = new ResizeObserver((entries) => {
95
+ // TODO: If the parent element has no absolute height, the height increases infinitely.
96
+ // : 親要素に絶対的な高さを持たない場合、高さは無限に増加します。
97
+ // https://zenn.dev/megeton/articles/be1c677e174c84#%E6%A0%B9%E6%9C%AC%E7%9A%84%E3%81%AB-resizeobserver-loop-limit-exceeded-%E3%81%8C%E5%87%BA%E3%82%8B%E5%95%8F%E9%A1%8C%E3%81%AB%E5%90%91%E3%81%8D%E5%90%88%E3%81%86
98
+ for (const entry of entries) {
99
+ // Prevents loss of some context when the canvas is resized
100
+ const contextMemo = {};
101
+ for (const prop of drawingState) {
102
+ contextMemo[prop] = context[prop];
103
+ }
104
+ const w = Math.floor(entry.contentRect.width);
105
+ const h = Math.floor(entry.contentRect.height);
106
+ if (reduceFlickering && memoCanvas && memoContext) {
107
+ memoCanvas.width = w * dpr;
108
+ memoCanvas.height = h * dpr;
109
+ memoContext.scale(1 / dpr, 1 / dpr);
110
+ memoContext.drawImage(context.canvas, 0, 0);
111
+ }
112
+ setDprConfig(canvas, context, w, h, dpr);
113
+ widthRef.current = w;
114
+ heightRef.current = h;
115
+ for (const prop of drawingState) {
116
+ context[prop] = contextMemo[prop];
117
+ }
118
+ if (reduceFlickering && memoContext) {
119
+ context.drawImage(memoContext.canvas, 0, 0);
120
+ }
121
121
  }
122
122
  });
123
123
  resizeObserver.observe(parent);
@@ -146,8 +146,8 @@ function AnimationCanvas(_a) {
146
146
  cancelAnimationFrame(reqIdRef.current);
147
147
  };
148
148
  }
149
- }, [loop]);
150
- return (jsxs(Fragment, { children: [jsx("canvas", Object.assign({ className: clsx('tremolo-animation-canvas', className), width: relativeSize ? 0 : width, height: relativeSize ? 0 : height, css: css(style), ref: canvasRef }, props)), relativeSize && reduceFlickering && (jsx("canvas", { style: { display: 'none' }, width: relativeSize ? 0 : width, height: relativeSize ? 0 : height, ref: memoCanvasRef }))] }));
149
+ }, [loop, init, options, reduceFlickering, relativeSize]);
150
+ return (jsxs(Fragment, { children: [jsx("canvas", Object.assign({ className: clsx('tremolo-animation-canvas', className), width: relativeSize ? 0 : width, height: relativeSize ? 0 : height, ref: canvasRef }, props)), relativeSize && reduceFlickering && (jsx("canvas", { style: { display: 'none' }, ref: memoCanvasRef }))] }));
151
151
  }
152
152
 
153
153
  /**
@@ -188,7 +188,7 @@ function stepValue(value, step) {
188
188
  if (step <= 0)
189
189
  throw new RangeError('requirements: step > 0');
190
190
  const quotient = Math.floor(value / step);
191
- const decimalDigits = (_a = decimalPart$1(step)) === null || _a === undefined ? undefined : _a.length;
191
+ const decimalDigits = (_a = decimalPart$1(step)) === null || _a === void 0 ? void 0 : _a.length;
192
192
  const v = toFixed$2(quotient * step, decimalDigits);
193
193
  const next = toFixed$2((quotient + 1) * step, decimalDigits);
194
194
  return Math.abs(value - v) < Math.abs(value - next) ? v : next;
@@ -240,6 +240,81 @@ function styleHelper(value, op, influencer) {
240
240
  }
241
241
  }
242
242
  }
243
+ /**
244
+ * @category Utility
245
+ */
246
+ function mod(n, m) {
247
+ return ((n % m) + m) % m;
248
+ }
249
+ /**
250
+ * @category Utility
251
+ */
252
+ function xor(a = false, b = false) {
253
+ return (a || b) && a != b;
254
+ }
255
+ /**
256
+ * @category MIDI
257
+ */
258
+ const noteNames = [
259
+ 'C',
260
+ 'C#',
261
+ 'D',
262
+ 'D#',
263
+ 'E',
264
+ 'F',
265
+ 'F#',
266
+ 'G',
267
+ 'G#',
268
+ 'A',
269
+ 'A#',
270
+ 'B',
271
+ ];
272
+ /**
273
+ * @category MIDI
274
+ */
275
+ function parseNoteName(noteName) {
276
+ const m = noteName.match(/^([a-g])(#{0,2}|b{0,2})(-?\d+)$/i);
277
+ if (!m)
278
+ throw new Error('Invalid note name');
279
+ const [, letter, accidental, octave] = m;
280
+ return {
281
+ letter: letter.toLocaleUpperCase(),
282
+ accidental: accidental,
283
+ octave: Number(octave),
284
+ };
285
+ }
286
+ /**
287
+ *
288
+ * @category MIDI
289
+ */
290
+ function noteNumber(noteName) {
291
+ const { letter, accidental, octave } = parseNoteName(noteName);
292
+ const noteIndex = noteNames.indexOf(letter.toLocaleUpperCase());
293
+ const accidentalValue = (accidental[0] == 'b' ? -1 : 1) * accidental.length;
294
+ return noteIndex + 12 * (Number(octave) + 1) + accidentalValue;
295
+ }
296
+ /**
297
+ *
298
+ * @category MIDI
299
+ */
300
+ function noteName(noteNumber) {
301
+ const noteIndex = mod(noteNumber, 12);
302
+ const octave = Math.floor(noteNumber / 12) - 1;
303
+ return noteNames[noteIndex] + octave;
304
+ }
305
+ /**
306
+ * @category MIDI
307
+ */
308
+ function isWhiteKey(note) {
309
+ const n = typeof note == 'string' ? noteNumber(note) : note;
310
+ return (mod(n, 12) == 0 ||
311
+ mod(n, 12) == 2 ||
312
+ mod(n, 12) == 4 ||
313
+ mod(n, 12) == 5 ||
314
+ mod(n, 12) == 7 ||
315
+ mod(n, 12) == 9 ||
316
+ mod(n, 12) == 11);
317
+ }
243
318
 
244
319
  /**
245
320
  * This hook is user-land implementation of the experimental `useEffectEvent` hook.
@@ -252,13 +327,14 @@ function useCallbackRef(callback, deps = []) {
252
327
  useInsertionEffect(() => {
253
328
  callbackRef.current = callback;
254
329
  });
255
- return useCallback((...args) => { var _a; return (_a = callbackRef.current) === null || _a === undefined ? undefined : _a.call(callbackRef, ...args); }, deps);
330
+ // eslint-disable-next-line react-hooks/exhaustive-deps
331
+ return useCallback((...args) => { var _a; return (_a = callbackRef.current) === null || _a === void 0 ? void 0 : _a.call(callbackRef, ...args); }, deps);
256
332
  }
257
333
 
258
334
  function useEventListener(target, event, handler, options) {
259
335
  const listener = useCallbackRef(handler);
260
336
  useEffect(() => {
261
- const node = typeof target === 'function' ? target() : (target !== null && target !== undefined ? target : document);
337
+ const node = typeof target === 'function' ? target() : (target !== null && target !== void 0 ? target : document);
262
338
  if (!handler || !node)
263
339
  return;
264
340
  node.addEventListener(event, listener, options);
@@ -267,8 +343,8 @@ function useEventListener(target, event, handler, options) {
267
343
  };
268
344
  }, [event, target, options, listener, handler]);
269
345
  return () => {
270
- const node = typeof target === 'function' ? target() : (target !== null && target !== undefined ? target : document);
271
- node === null || node === undefined ? undefined : node.removeEventListener(event, listener, options);
346
+ const node = typeof target === 'function' ? target() : (target !== null && target !== void 0 ? target : document);
347
+ node === null || node === void 0 ? void 0 : node.removeEventListener(event, listener, options);
272
348
  };
273
349
  }
274
350
 
@@ -277,7 +353,7 @@ function useRefCallbackEvent(event, handler, options, deps = []) {
277
353
  let cleanup;
278
354
  return (node) => {
279
355
  if (!node) {
280
- cleanup === null || cleanup === undefined ? undefined : cleanup();
356
+ cleanup === null || cleanup === void 0 ? void 0 : cleanup();
281
357
  return;
282
358
  }
283
359
  node.addEventListener(event, handler, options);
@@ -285,6 +361,7 @@ function useRefCallbackEvent(event, handler, options, deps = []) {
285
361
  node.removeEventListener(event, handler, options);
286
362
  };
287
363
  };
364
+ // eslint-disable-next-line react-hooks/exhaustive-deps
288
365
  }, deps);
289
366
  }
290
367
 
@@ -295,34 +372,17 @@ function removeNoSelect() {
295
372
  document.body.classList.remove('tremolo-no-select');
296
373
  }
297
374
 
298
- const defaultOptions = {
299
- active: '#7998ec',
300
- inactive: '#eee',
301
- bg: '#ccc',
302
- thumb: '#4e76e6',
303
- lineWeight: 6,
304
- thumbWeight: 4,
305
- };
306
375
  /**
307
376
  * simple rotary Knob.
308
377
  */
309
- function Knob(_a) {
310
- var { value, min, max, defaultValue = min, startValue = min, step = 1, skew = 1, size, width = 50, height = 50, options = defaultOptions, draw, bodyNoSelect = true, wheel, enableDoubleClickDefault = true, style, className, onChange, _active, _focus, _hover } = _a, props = __rest(_a, ["value", "min", "max", "defaultValue", "startValue", "step", "skew", "size", "width", "height", "options", "draw", "bodyNoSelect", "wheel", "enableDoubleClickDefault", "style", "className", "onChange", "_active", "_focus", "_hover"]);
378
+ function AnimationKnob(_a) {
379
+ 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"]);
311
380
  // -- state and ref ---
312
381
  // const [privateValue, setPrivateValue] = useState(value);
313
382
  const dragOffsetY = useRef(undefined);
314
383
  // --- interpret props ---
315
- const opts = Object.assign(Object.assign({}, defaultOptions), options);
316
384
  // const percent = normalizeValue(value, min, max, skew)
317
- const isRelativeSize = typeof (size !== null && size !== undefined ? size : width) == 'string';
318
- const adaptSize = (size) => {
319
- if (typeof size == 'string') {
320
- return Number(size.slice(0, -1));
321
- }
322
- else {
323
- return size;
324
- }
325
- };
385
+ const isRelativeSize = typeof (size !== null && size !== void 0 ? size : width) == 'string' || typeof height == 'string';
326
386
  const handleEvent = (event) => {
327
387
  const isTouch = event instanceof TouchEvent;
328
388
  if (isTouch && event.cancelable)
@@ -332,6 +392,8 @@ function Knob(_a) {
332
392
  addNoSelect();
333
393
  const screenY = isTouch ? event.touches[0].screenY : event.screenY;
334
394
  const delta = dragOffsetY.current - screenY;
395
+ if (Math.abs(delta) < 1)
396
+ return;
335
397
  dragOffsetY.current = screenY;
336
398
  const n = normalizeValue(value, min, max, skew);
337
399
  const v = rawValue(n + delta / 100, min, max, skew);
@@ -340,6 +402,24 @@ function Knob(_a) {
340
402
  onChange(v2);
341
403
  }
342
404
  };
405
+ const handleKeyDown = useCallback((event) => {
406
+ if (!keyboard || !onChange)
407
+ return;
408
+ const key = event.key;
409
+ if (['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown'].includes(key)) {
410
+ event.preventDefault();
411
+ const x = key == 'ArrowRight' || key == 'ArrowUp' ? keyboard[1] : -keyboard[1];
412
+ let v;
413
+ if (keyboard[0] == 'normalized') {
414
+ const n = normalizeValue(value, min, max, skew);
415
+ v = rawValue(n + x, min, max, skew);
416
+ }
417
+ else {
418
+ v = value + x;
419
+ }
420
+ onChange(clamp(stepValue(v, step), min, max));
421
+ }
422
+ }, [value, min, max, step, skew, keyboard, onChange]);
343
423
  // --- hooks ---
344
424
  const wheelRefCallback = useRefCallbackEvent('wheel', (event) => {
345
425
  if (!wheel)
@@ -364,24 +444,24 @@ function Knob(_a) {
364
444
  if (bodyNoSelect)
365
445
  removeNoSelect();
366
446
  });
367
- return (jsxs("div", Object.assign({ className: clsx('tremolo-knob', className), ref: (div) => {
447
+ return (jsx("div", Object.assign({ className: clsx('tremolo-animation-knob', className), ref: (div) => {
368
448
  wheelRefCallback(div);
369
449
  touchMoveRefCallback(div);
370
- }, role: "slider", tabIndex: 0, "aria-valuenow": value, "aria-valuemax": max, "aria-valuemin": min, css: css(Object.assign(Object.assign({ display: 'inline-block', width: size !== null && size !== undefined ? size : width, height: size !== null && size !== undefined ? size : height, cursor: 'pointer' }, style), { ':active': Object.assign({}, _active), ':focus': Object.assign({}, _focus), ':hover': Object.assign({}, _hover) })), onPointerDown: (event) => {
450
+ }, 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: (event) => {
371
451
  dragOffsetY.current = event.screenY;
372
452
  handleEvent(event);
373
453
  }, onDoubleClick: () => {
374
454
  if (enableDoubleClickDefault && onChange) {
375
455
  onChange(defaultValue);
376
456
  }
377
- } }, props, { children: [jsx(Global, { styles: {
378
- '.no-select': {
379
- userSelect: 'none',
380
- },
381
- } }), jsx(AnimationCanvas, { width: adaptSize(size !== null && size !== undefined ? size : width), height: adaptSize(size !== null && size !== undefined ? size : height), relativeSize: isRelativeSize,
382
- // TODO
383
- // reduceFlickering={false}
384
- draw: draw ? draw : (ctx, width, height) => {
457
+ }, onKeyDown: handleKeyDown, onContextMenu: (e) => e.preventDefault() }, props, { children: jsx(AnimationCanvas, Object.assign({}, (isRelativeSize
458
+ ? { relativeSize: true }
459
+ : {
460
+ width: size !== null && size !== void 0 ? size : width,
461
+ height: size !== null && size !== void 0 ? size : height,
462
+ }), { draw: draw
463
+ ? draw
464
+ : (ctx, width, height) => {
385
465
  const p = normalizeValue(value, min, max, skew);
386
466
  const startP = normalizeValue(startValue, min, max, skew);
387
467
  const w = width.current, cx = w / 2;
@@ -394,33 +474,172 @@ function Knob(_a) {
394
474
  ctx.lineCap = 'butt';
395
475
  // bg
396
476
  ctx.beginPath();
397
- ctx.fillStyle = opts.bg;
477
+ ctx.fillStyle = bg;
398
478
  ctx.arc(cx, cy, r - 0.5, 0, 2 * Math.PI);
399
479
  ctx.fill();
400
- ctx.lineCap = 'round';
401
- ctx.lineWidth = opts.lineWeight;
480
+ ctx.lineWidth = lineWeight;
402
481
  // inactive rotary
403
482
  ctx.beginPath();
404
- ctx.strokeStyle = opts.inactive;
405
- ctx.arc(cx, cy, r - 0.5 - opts.lineWeight / 2, radian(135), radian(45));
483
+ ctx.strokeStyle = inactiveColor;
484
+ ctx.arc(cx, cy, r - 0.5 - lineWeight / 2, radian(135), radian(45));
406
485
  ctx.stroke();
407
486
  // active rotary
408
487
  ctx.beginPath();
409
- ctx.strokeStyle = opts.active;
410
- ctx.arc(cx, cy, r - 0.5 - opts.lineWeight / 2, radian(135 + 270 * startP), radian(135 + 270 * p), p < startP);
488
+ ctx.strokeStyle = activeColor;
489
+ ctx.arc(cx, cy, r - 0.5 - lineWeight / 2, radian(135 + 270 * startP), radian(135 + 270 * p), p < startP);
411
490
  ctx.stroke();
412
491
  // thumb
492
+ ctx.lineCap = 'round';
413
493
  ctx.beginPath();
414
494
  ctx.moveTo(cx, cy);
415
495
  ctx.lineTo(cx +
416
496
  Math.cos(radian(135 + 270 * p)) *
417
- (r - 0.5 - opts.thumbWeight / 2), cy +
497
+ (r - 0.5 - thumbWeight / 2), cy +
418
498
  Math.sin(radian(135 + 270 * p)) *
419
- (r - 0.5 - opts.thumbWeight / 2));
420
- ctx.strokeStyle = opts.thumb;
421
- ctx.lineWidth = opts.thumbWeight;
499
+ (r - 0.5 - thumbWeight / 2));
500
+ ctx.strokeStyle = thumb;
501
+ ctx.lineWidth = thumbWeight;
422
502
  ctx.stroke();
423
- } })] })));
503
+ } })) })));
504
+ }
505
+
506
+ function DragObserver(props) {
507
+ const { children, onDrag, onDragStart, onDragEnd, as: Component = 'div' } = props, attributes = __rest(props, ["children", "onDrag", "onDragStart", "onDragEnd", "as"]);
508
+ const dragOffsetX = useRef(undefined);
509
+ const dragOffsetY = useRef(undefined);
510
+ const handleEvent = (event) => {
511
+ const isTouch = event instanceof TouchEvent;
512
+ if (isTouch && event.cancelable)
513
+ event.preventDefault();
514
+ const screenX = isTouch ? event.touches[0].screenX : event.screenX;
515
+ const screenY = isTouch ? event.touches[0].screenY : event.screenY;
516
+ let deltaX = 0, deltaY = 0;
517
+ if (dragOffsetX.current) {
518
+ deltaX = screenX - dragOffsetX.current;
519
+ dragOffsetX.current = screenX;
520
+ }
521
+ if (dragOffsetY.current) {
522
+ deltaY = screenY - dragOffsetY.current;
523
+ dragOffsetY.current = screenY;
524
+ }
525
+ if (Math.abs(deltaX) < 1 && Math.abs(deltaY) < 1)
526
+ return;
527
+ if (onDrag)
528
+ onDrag(deltaX, deltaY);
529
+ };
530
+ const touchMoveRefCallback = useRefCallbackEvent('touchmove', handleEvent, { passive: false }, [onDrag]);
531
+ useEventListener(window, 'mousemove', handleEvent);
532
+ useEventListener(window, 'pointerup', () => {
533
+ dragOffsetX.current = undefined;
534
+ dragOffsetY.current = undefined;
535
+ onDragEnd === null || onDragEnd === void 0 ? void 0 : onDragEnd();
536
+ });
537
+ return (jsx(Component, Object.assign({ ref: touchMoveRefCallback, onPointerDown: (event) => {
538
+ dragOffsetX.current = event.screenX;
539
+ dragOffsetY.current = event.screenY;
540
+ handleEvent(event);
541
+ onDragStart === null || onDragStart === void 0 ? void 0 : onDragStart();
542
+ } }, attributes, { children: children })));
543
+ }
544
+
545
+ function Knob(_a) {
546
+ 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"]);
547
+ // -- state and ref ---
548
+ const dragOffsetY = useRef(undefined);
549
+ // --- interpret props ---
550
+ const padding = 8; // %
551
+ const thumbLineWeight = 6; // %
552
+ const thumbLength = 35; // %
553
+ const lineWeight = 3; // px
554
+ const p = normalizeValue(value, min, max, skew);
555
+ const s = normalizeValue(startValue, min, max, skew);
556
+ // --- internal functions ---
557
+ const handleEvent = (event) => {
558
+ if (!onChange || readonly)
559
+ return;
560
+ const isTouch = event instanceof TouchEvent;
561
+ if (isTouch && event.cancelable)
562
+ event.preventDefault();
563
+ if (dragOffsetY.current) {
564
+ if (bodyNoSelect)
565
+ addNoSelect();
566
+ const screenY = isTouch ? event.touches[0].screenY : event.screenY;
567
+ const delta = dragOffsetY.current - screenY;
568
+ if (Math.abs(delta) < 1)
569
+ return;
570
+ dragOffsetY.current = screenY;
571
+ const n = normalizeValue(value, min, max, skew);
572
+ const v = rawValue(n + delta / 100, min, max, skew);
573
+ const v2 = clamp(stepValue(v, step), min, max);
574
+ onChange(v2);
575
+ }
576
+ };
577
+ const handleKeyDown = useCallback((event) => {
578
+ if (!keyboard || !onChange || readonly)
579
+ return;
580
+ const key = event.key;
581
+ if (['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown'].includes(key)) {
582
+ event.preventDefault();
583
+ const x = key == 'ArrowRight' || key == 'ArrowUp' ? keyboard[1] : -keyboard[1];
584
+ let v;
585
+ if (keyboard[0] == 'normalized') {
586
+ const n = normalizeValue(value, min, max, skew);
587
+ v = rawValue(n + x, min, max, skew);
588
+ }
589
+ else {
590
+ v = value + x;
591
+ }
592
+ onChange(clamp(stepValue(v, step), min, max));
593
+ }
594
+ }, [value, min, max, step, skew, keyboard, readonly, onChange]);
595
+ // --- hooks ---
596
+ const wheelRefCallback = useRefCallbackEvent('wheel', (event) => {
597
+ if (!wheel)
598
+ return;
599
+ event.preventDefault();
600
+ const x = event.deltaY > 0 ? -wheel[1] : wheel[1];
601
+ let v;
602
+ if (wheel[0] == 'normalized') {
603
+ const n = normalizeValue(value, min, max, skew);
604
+ v = rawValue(n + x, min, max, skew);
605
+ }
606
+ else {
607
+ v = value + x;
608
+ }
609
+ if (onChange)
610
+ onChange(clamp(stepValue(v, step), min, max));
611
+ }, { passive: false }, [wheel, value, min, max, skew, step, onChange]);
612
+ const touchMoveRefCallback = useRefCallbackEvent('touchmove', handleEvent, { passive: false }, [value, min, max, skew, step, onChange, bodyNoSelect]);
613
+ useEventListener(window, 'mousemove', handleEvent);
614
+ useEventListener(window, 'pointerup', () => {
615
+ dragOffsetY.current = undefined;
616
+ if (bodyNoSelect)
617
+ removeNoSelect();
618
+ });
619
+ const center = size / 2;
620
+ const r1 = -135;
621
+ const r2 = -135 + Math.min(p, s) * 270;
622
+ const r3 = -135 + Math.max(p, s) * 270;
623
+ const r4 = 135;
624
+ const x1 = center + center * Math.cos(radian(r1 - 90));
625
+ const y1 = center + center * Math.sin(radian(r1 - 90));
626
+ const x2 = center + center * Math.cos(radian(r2 - 90));
627
+ const y2 = center + center * Math.sin(radian(r2 - 90));
628
+ const x3 = center + center * Math.cos(radian(r3 - 90));
629
+ const y3 = center + center * Math.sin(radian(r3 - 90));
630
+ const x4 = center + center * Math.cos(radian(r4 - 90));
631
+ const y4 = center + center * Math.sin(radian(r4 - 90));
632
+ return (jsxs("svg", Object.assign({ className: clsx('tremolo-knob', className), ref: (div) => {
633
+ wheelRefCallback(div);
634
+ touchMoveRefCallback(div);
635
+ }, width: size, height: size, tabIndex: 0, "aria-valuenow": value, "aria-valuemin": min, "aria-valuemax": max, "aria-disabled": disabled, "aria-readonly": readonly, onPointerDown: (event) => {
636
+ dragOffsetY.current = event.screenY;
637
+ handleEvent(event);
638
+ }, onDoubleClick: () => {
639
+ if (enableDoubleClickDefault && onChange) {
640
+ onChange(defaultValue);
641
+ }
642
+ }, onKeyDown: handleKeyDown, onContextMenu: (e) => e.preventDefault() }, props, { children: [startValue > min && (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 })), 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 && (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 })), jsxs("svg", { className: clsx('tremolo-knob-thumb', classes === null || classes === void 0 ? void 0 : classes.thumb), children: [jsx("circle", { cx: "50%", cy: "50%", r: `${50 - padding}%`, fill: thumb || 'currentColor' }), 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%" })] })] })));
424
643
  }
425
644
 
426
645
  /**
@@ -437,7 +656,7 @@ function toFixed$1(x, fractionDigits) {
437
656
 
438
657
  /**
439
658
  * @module NumberInput
440
- */
659
+ */
441
660
  function selectUnit(units, value) {
442
661
  if (typeof units == 'string')
443
662
  return [units, 1];
@@ -500,14 +719,13 @@ function parseValue(inputString, units, digit, defaultValue = 0) {
500
719
  * Input with some useful functions for entering numerical values.
501
720
  */
502
721
  function NumberInput(_a) {
503
- var { value, min, max, step, defaultValue = min, units, disabled = false, placeholder, readOnly, tabIndex, typeNumber = false, digit, selectWithFocus = 'none', blurWithEnter = true, className, style, onChange, onFocus, onBlur } = _a, props = __rest(_a, ["value", "min", "max", "step", "defaultValue", "units", "disabled", "placeholder", "readOnly", "tabIndex", "typeNumber", "digit", "selectWithFocus", "blurWithEnter", "className", "style", "onChange", "onFocus", "onBlur"]);
722
+ var { value, min, max, step, defaultValue = min, units, disabled = false, placeholder, readOnly, tabIndex, typeNumber = false, digit, selectWithFocus = 'none', blurWithEnter = true, className, onChange, onFocus, onBlur } = _a, props = __rest(_a, ["value", "min", "max", "step", "defaultValue", "units", "disabled", "placeholder", "readOnly", "tabIndex", "typeNumber", "digit", "selectWithFocus", "blurWithEnter", "className", "onChange", "onFocus", "onBlur"]);
504
723
  const [showValue, setShowValue] = useState(parseValue(String(value), units, digit, defaultValue).formatValue);
505
724
  const calculatedTypeNumber = units ? false : typeNumber;
506
- const { _active, _focus, _hover } = props, rest = __rest(props, ["_active", "_focus", "_hover"]);
507
- return (jsx("input", Object.assign({ className: clsx('tremolo-number-input', className), value: showValue, min: min, max: max, step: step, type: calculatedTypeNumber ? 'number' : 'text', spellCheck: 'false', disabled: disabled, placeholder: placeholder, readOnly: readOnly, tabIndex: tabIndex, css: css(Object.assign(Object.assign({ display: 'inline-block', font: 'inherit', margin: 0, paddingTop: 6, paddingBottom: 6, paddingLeft: 10, paddingRight: 10, borderStyle: 'solid', borderWidth: 1, borderColor: '#bbb', borderRadius: 4, outline: 0, transition: 'all 0.1s linear', appearance: 'textfield', '&:hover': Object.assign({ borderColor: 'var(--tremolo-theme-color)' }, _hover), '&:focus': Object.assign({ borderColor: 'var(--tremolo-theme-color)', boxShadow: '0px 0px 0px 2px rgba(var(--tremolo-theme-color-rgb), 0.1)' }, _focus) }, (_active && { '&:active': Object.assign({}, _active) })), style)), onChange: (event) => {
725
+ return (jsx("input", Object.assign({ className: clsx('tremolo-number-input', className), value: showValue, min: min, max: max, step: step, type: calculatedTypeNumber ? 'number' : 'text', spellCheck: 'false', disabled: disabled, placeholder: placeholder, readOnly: readOnly, tabIndex: tabIndex, onChange: (event) => {
508
726
  const v = event.currentTarget.value;
509
727
  setShowValue(v);
510
- const newValue = clamp(parseValue(v, units, digit, defaultValue).rawValue, min !== null && min !== undefined ? min : -Infinity, max !== null && max !== undefined ? max : Infinity);
728
+ const newValue = clamp(parseValue(v, units, digit, defaultValue).rawValue, min !== null && min !== void 0 ? min : -Infinity, max !== null && max !== void 0 ? max : Infinity);
511
729
  if (onChange)
512
730
  onChange(newValue, event);
513
731
  }, onFocus: (event) => {
@@ -532,9 +750,314 @@ function NumberInput(_a) {
532
750
  if (blurWithEnter && event.key == 'Enter') {
533
751
  event.currentTarget.blur();
534
752
  }
535
- } }, rest)));
753
+ } }, props)));
754
+ }
755
+
756
+ function KeyLabel(_a) {
757
+ var { label, className, wrapperClassName, wrapperStyle, __note, __index, __label } = _a, props = __rest(_a, ["label", "className", "wrapperClassName", "wrapperStyle", "__note", "__index", "__label"]);
758
+ const content = label
759
+ ? label(__note, __index)
760
+ : __label && __label(__note, __index);
761
+ return ((content != undefined || content != null) && (jsx("div", { className: clsx('tremolo-piano-key-label-wrapper', wrapperClassName), style: wrapperStyle, children: jsx("div", Object.assign({ className: clsx('tremolo-piano-key-label', className) }, props, { children: content })) })));
762
+ }
763
+
764
+ const BlackKey = forwardRef((_a, ref) => {
765
+ var { width = 40 * 0.65, height = '60%', className, style, children, __glissando, __position, __note, __disabled, __index, __fill, __width, __playNote, __stopNote, __label } = _a, props = __rest(_a, ["width", "height", "className", "style", "children", "__glissando", "__position", "__note", "__disabled", "__index", "__fill", "__width", "__playNote", "__stopNote", "__label"]);
766
+ const [played, setPlayed] = useState(false);
767
+ useImperativeHandle(ref, () => {
768
+ return {
769
+ play() {
770
+ if (__disabled)
771
+ return;
772
+ setPlayed(true);
773
+ if (__playNote)
774
+ __playNote(__note);
775
+ },
776
+ stop() {
777
+ setPlayed(false);
778
+ if (__stopNote)
779
+ __stopNote(__note);
780
+ },
781
+ played() {
782
+ return played;
783
+ },
784
+ };
785
+ }, [__disabled, __note, __playNote, __stopNote, played]);
786
+ let keyLabelProps = {};
787
+ if (children != undefined) {
788
+ React.Children.forEach(children, (child) => {
789
+ if (React.isValidElement(child)) {
790
+ if (child.type == KeyLabel) {
791
+ keyLabelProps = child.props;
792
+ }
793
+ else {
794
+ throw new Error('only <KeyLabel>');
795
+ }
796
+ }
797
+ else {
798
+ throw new Error('children is an invalid element.');
799
+ }
800
+ });
801
+ }
802
+ return (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: () => {
803
+ if (__disabled)
804
+ return;
805
+ setPlayed(true);
806
+ if (__playNote)
807
+ __playNote(__note);
808
+ }, onPointerEnter: () => {
809
+ if (__disabled)
810
+ return;
811
+ if (__glissando) {
812
+ setPlayed(true);
813
+ if (__playNote)
814
+ __playNote(__note);
815
+ }
816
+ }, onPointerUp: () => {
817
+ if (__disabled)
818
+ return;
819
+ setPlayed(false);
820
+ if (__stopNote)
821
+ __stopNote(__note);
822
+ }, onPointerLeave: () => {
823
+ if (__disabled)
824
+ return;
825
+ setPlayed(false);
826
+ if (__stopNote)
827
+ __stopNote(__note);
828
+ } }, props, { children: jsx(KeyLabel, Object.assign({ __note: __note, __label: __label, __index: __index }, keyLabelProps)) })));
829
+ });
830
+
831
+ const WhiteKey = forwardRef((_a, ref) => {
832
+ var { width = 40, height = '100%', className, style, children, __glissando, __position, __note, __disabled, __index, __fill, __width, __playNote, __stopNote, __label } = _a, props = __rest(_a, ["width", "height", "className", "style", "children", "__glissando", "__position", "__note", "__disabled", "__index", "__fill", "__width", "__playNote", "__stopNote", "__label"]);
833
+ const [played, setPlayed] = useState(false);
834
+ useImperativeHandle(ref, () => {
835
+ return {
836
+ play() {
837
+ if (__disabled)
838
+ return;
839
+ setPlayed(true);
840
+ if (__playNote)
841
+ __playNote(__note);
842
+ },
843
+ stop() {
844
+ setPlayed(false);
845
+ if (__stopNote)
846
+ __stopNote(__note);
847
+ },
848
+ played() {
849
+ return played;
850
+ },
851
+ };
852
+ }, [__disabled, __note, __playNote, __stopNote, played]);
853
+ let keyLabelProps = {};
854
+ if (children != undefined) {
855
+ React.Children.forEach(children, (child) => {
856
+ if (React.isValidElement(child)) {
857
+ if (child.type == KeyLabel) {
858
+ keyLabelProps = child.props;
859
+ }
860
+ else {
861
+ throw new Error('only <KeyLabel>');
862
+ }
863
+ }
864
+ else {
865
+ throw new Error('children is an invalid element.');
866
+ }
867
+ });
868
+ }
869
+ return (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: () => {
870
+ if (__disabled)
871
+ return;
872
+ setPlayed(true);
873
+ if (__playNote)
874
+ __playNote(__note);
875
+ }, onPointerEnter: () => {
876
+ if (__disabled)
877
+ return;
878
+ if (__glissando) {
879
+ setPlayed(true);
880
+ if (__playNote)
881
+ __playNote(__note);
882
+ }
883
+ }, onPointerUp: () => {
884
+ if (__disabled)
885
+ return;
886
+ setPlayed(false);
887
+ if (__stopNote)
888
+ __stopNote(__note);
889
+ }, onPointerLeave: () => {
890
+ if (__disabled)
891
+ return;
892
+ setPlayed(false);
893
+ if (__stopNote)
894
+ __stopNote(__note);
895
+ } }, props, { children: jsx(KeyLabel, Object.assign({ __note: __note, __label: __label, __index: __index }, keyLabelProps)) })));
896
+ });
897
+
898
+ const Shortcuts = {
899
+ HOME_ROW: {
900
+ keys: [
901
+ 'a',
902
+ 'w',
903
+ 's',
904
+ 'e',
905
+ 'd',
906
+ 'f',
907
+ 't',
908
+ 'g',
909
+ 'y',
910
+ 'h',
911
+ 'u',
912
+ 'j',
913
+ 'k',
914
+ 'o',
915
+ 'l',
916
+ 'p',
917
+ ';',
918
+ ],
919
+ },
920
+ };
921
+
922
+ function Piano(_a) {
923
+ var { noteRange, glissando = true, midiMax = 127, keyboardShortcuts, fill = false, height = fill ? '100%' : 160, style, className, playNote, stopNote, label, children } = _a, props = __rest(_a, ["noteRange", "glissando", "midiMax", "keyboardShortcuts", "fill", "height", "style", "className", "playNote", "stopNote", "label", "children"]);
924
+ // -- state and ref ---
925
+ const [pressed, setPressed] = useState(false);
926
+ const [whiteNoteWidth, setWhiteNoteWidth] = useState(-1);
927
+ const keyRefs = useRef([]);
928
+ for (let i = 0; i < noteRange.last - noteRange.first + 1; i++) {
929
+ keyRefs.current[i] = createRef();
930
+ }
931
+ const pianoRef = useRef(null);
932
+ // --- interpret props ---
933
+ const noteRangeArray = Array.from({ length: noteRange.last - noteRange.first + 1 }, (_, i) => i + noteRange.first);
934
+ const whiteNoteCount = noteRangeArray.filter((v) => isWhiteKey(v)).length;
935
+ let whiteKeyProps = {}, blackKeyProps = {};
936
+ if (children != undefined) {
937
+ React.Children.forEach(children, (child) => {
938
+ if (React.isValidElement(child)) {
939
+ if (child.type == WhiteKey) {
940
+ whiteKeyProps = child.props;
941
+ }
942
+ else if (child.type == BlackKey) {
943
+ blackKeyProps = child.props;
944
+ }
945
+ else {
946
+ throw new Error('only <WhiteKey> or <BlackKey>');
947
+ }
948
+ }
949
+ else {
950
+ throw new Error('children is an invalid element.');
951
+ }
952
+ });
953
+ }
954
+ // --- internal functions ---
955
+ function notePosition(note) {
956
+ const pitchPositions = {
957
+ C: 0,
958
+ 'C#': 0.55,
959
+ D: 1,
960
+ 'D#': 1.8,
961
+ E: 2,
962
+ F: 3,
963
+ 'F#': 3.5,
964
+ G: 4,
965
+ 'G#': 4.7,
966
+ A: 5,
967
+ 'A#': 5.85,
968
+ B: 6,
969
+ };
970
+ const toNoteName = (n) => {
971
+ const { letter, accidental } = parseNoteName(noteName(n));
972
+ return (letter + accidental);
973
+ };
974
+ const _noteName = toNoteName(note);
975
+ const firstNoteName = toNoteName(noteRange.first);
976
+ const pos = pitchPositions[_noteName] - pitchPositions[firstNoteName];
977
+ const octave = Math.floor((note - noteRange.first) / 12);
978
+ const octaveOffset = noteNames.indexOf(firstNoteName) > noteNames.indexOf(_noteName) ? 1 : 0;
979
+ const length = whiteNoteWidth + 3;
980
+ return pos * length + (octave + octaveOffset) * 7 * length;
981
+ }
982
+ // --- hooks ---
983
+ useEffect(() => {
984
+ if (fill && pianoRef.current) {
985
+ const parent = pianoRef.current.parentElement;
986
+ if (!parent)
987
+ throw new Error("doesn't have a parent element.");
988
+ const resizeObserver = new ResizeObserver(() => {
989
+ const w = pianoRef.current.clientWidth;
990
+ setWhiteNoteWidth(w / whiteNoteCount - 3);
991
+ });
992
+ resizeObserver.observe(parent);
993
+ }
994
+ else {
995
+ setWhiteNoteWidth((whiteKeyProps === null || whiteKeyProps === void 0 ? void 0 : whiteKeyProps.width) || 40);
996
+ }
997
+ }, [fill, whiteKeyProps === null || whiteKeyProps === void 0 ? void 0 : whiteKeyProps.width, whiteNoteCount]);
998
+ useEventListener(window, 'keydown', (e) => {
999
+ var _a, _b, _c, _d;
1000
+ if (e.repeat)
1001
+ return;
1002
+ if (!keyboardShortcuts)
1003
+ return;
1004
+ const index = keyboardShortcuts.keys.indexOf(e.key);
1005
+ if (index != -1) {
1006
+ if (!((_b = (_a = keyRefs.current[index]) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.played())) {
1007
+ (_d = (_c = keyRefs.current[index]) === null || _c === void 0 ? void 0 : _c.current) === null || _d === void 0 ? void 0 : _d.play();
1008
+ }
1009
+ }
1010
+ });
1011
+ useEventListener(window, 'keyup', (e) => {
1012
+ var _a, _b;
1013
+ if (e.repeat)
1014
+ return;
1015
+ if (!keyboardShortcuts)
1016
+ return;
1017
+ const index = keyboardShortcuts.keys.indexOf(e.key);
1018
+ if (index != -1) {
1019
+ (_b = (_a = keyRefs.current[index]) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.stop();
1020
+ }
1021
+ });
1022
+ return (jsx("div", Object.assign({ ref: pianoRef, className: clsx('tremolo-piano', className), style: Object.assign({ width: fill ? '100%' : undefined, height: height }, style), onPointerDown: (e) => {
1023
+ if (props.onPointerDown)
1024
+ props.onPointerDown(e);
1025
+ setPressed(true);
1026
+ }, onPointerUp: (e) => {
1027
+ if (props.onPointerUp)
1028
+ props.onPointerUp(e);
1029
+ setPressed(false);
1030
+ } }, props, { children: noteRangeArray.map((note, index) => {
1031
+ return isWhiteKey(note) ? (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, __playNote: playNote, __stopNote: stopNote, __label: label }, whiteKeyProps), note)) : (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, __playNote: playNote, __stopNote: stopNote, __label: label }, blackKeyProps), note));
1032
+ }) })));
536
1033
  }
537
1034
 
1035
+ const minContext = createContext(0);
1036
+ const maxContext = createContext(0);
1037
+ const stepContext = createContext(0);
1038
+ const skewContext = createContext(0);
1039
+ const verticalContext = createContext(false);
1040
+ const reverseContext = createContext(false);
1041
+ const disabledContext = createContext(false);
1042
+ function SliderValuesProvider({ min: _min, max: _max, step: _step, skew: _skew, vertical: _vertical, reverse: _reverse, disabled: _disabled, children, }) {
1043
+ const [min] = useState(_min);
1044
+ const [max] = useState(_max);
1045
+ const [step] = useState(_step);
1046
+ const [skew] = useState(_skew);
1047
+ const [vertical] = useState(_vertical);
1048
+ const [reverse] = useState(_reverse);
1049
+ const [disabled] = useState(_disabled);
1050
+ return (jsx(minContext.Provider, { value: min, children: jsx(maxContext.Provider, { value: max, children: jsx(stepContext.Provider, { value: step, children: jsx(skewContext.Provider, { value: skew, children: jsx(verticalContext.Provider, { value: vertical, children: jsx(reverseContext.Provider, { value: reverse, children: jsx(disabledContext.Provider, { value: disabled, children: children }) }) }) }) }) }) }));
1051
+ }
1052
+ const useMin = () => useContext(minContext);
1053
+ const useMax = () => useContext(maxContext);
1054
+ const useStep = () => useContext(stepContext);
1055
+ const useSkew = () => useContext(skewContext);
1056
+ const useVertical = () => useContext(verticalContext);
1057
+ // export const useSetVertical = () => useContext(setVerticalContext)
1058
+ const useReverse = () => useContext(reverseContext);
1059
+ const useDisabled = () => useContext(disabledContext);
1060
+
538
1061
  /**
539
1062
  * clamp value between min and max
540
1063
  * @category Math
@@ -556,105 +1079,119 @@ function decimalPart(x) {
556
1079
 
557
1080
  /**
558
1081
  * @module Slider
559
- */
560
- function isHorizontal(d) {
561
- return d == 'right' || d == 'left';
562
- }
563
- function isVertical(d) {
564
- return d == 'up' || d == 'down';
565
- }
566
- function isReversed(d) {
567
- return d == 'left' || d == 'up';
568
- }
569
- function gradientDirection(d) {
570
- if (d == 'left' || d == 'right')
571
- return d;
572
- if (d == 'up')
573
- return 'top';
574
- if (d == 'down')
575
- return 'bottom';
576
- }
577
- function parseScaleOrderList(scale, min, max, step) {
1082
+ */
1083
+ function generateOptionsList(options, min, max, step) {
578
1084
  var _a;
579
- let scalesList = [];
580
- if (!scale) ;
581
- else if (typeof scale[0] == 'object') {
582
- scalesList = scale;
1085
+ const optionsList = [];
1086
+ const per = options[0] == 'step' ? step : options[0];
1087
+ const count = Math.floor(max / per) - Math.ceil(min / per) + 1;
1088
+ for (let i = 0; i < count; i++) {
1089
+ const value = toFixed(per * (Math.ceil(min / per) + i), (_a = decimalPart(per)) === null || _a === void 0 ? void 0 : _a.length);
1090
+ optionsList.push({ value: value, type: options[1] });
583
1091
  }
584
- else {
585
- const per = scale[0] == 'step' ? step : scale[0];
586
- const count = Math.floor(max / per) - Math.ceil(min / per) + 1;
587
- for (let i = 0; i < count; i++) {
588
- const at = toFixed(per * (Math.ceil(min / per) + i), (_a = decimalPart(per)) === null || _a === undefined ? undefined : _a.length);
589
- scalesList.push({ at: at, type: scale[1] });
590
- }
591
- }
592
- return scalesList;
1092
+ return optionsList;
1093
+ }
1094
+
1095
+ function ScaleOption(_a) {
1096
+ var { value, type = 'mark-number', label, thickness = 1, length = '0.5rem', gap = 2, labelWidth, classes, styles, className, style } = _a, props = __rest(_a, ["value", "type", "label", "thickness", "length", "gap", "labelWidth", "classes", "styles", "className", "style"]);
1097
+ const min = useMin();
1098
+ const max = useMax();
1099
+ const skew = useSkew();
1100
+ const vertical = useVertical();
1101
+ const reverse = useReverse();
1102
+ const calcPercent = useCallback((rawValue) => {
1103
+ return toFixed$2(xor(vertical, reverse)
1104
+ ? 100 - normalizeValue(rawValue, min, max, skew) * 100
1105
+ : normalizeValue(rawValue, min, max, skew) * 100);
1106
+ }, [vertical, reverse, max, min, skew]);
1107
+ return (jsxs("div", Object.assign({ className: clsx('tremolo-slider-scale-option', className), style: Object.assign({ left: !vertical ? `${calcPercent(value)}%` : undefined, top: vertical ? `${calcPercent(value)}%` : undefined }, style), "data-vertical": vertical }, props, { children: [type != 'number' && (jsx("div", { className: clsx('tremolo-slider-scale-option-mark', classes === null || classes === void 0 ? void 0 : classes.mark), style: Object.assign({ width: !vertical ? thickness : length, height: vertical ? thickness : length, marginBottom: !vertical ? gap : undefined, marginRight: vertical ? gap : undefined }, styles === null || styles === void 0 ? void 0 : styles.mark), "data-vertical": vertical })), type != 'mark' && (jsx("div", { className: clsx('tremolo-slider-scale-option-label', classes === null || classes === void 0 ? void 0 : classes.label), style: Object.assign({ width: labelWidth }, styles === null || styles === void 0 ? void 0 : styles.label), children: label || value }))] })));
1108
+ }
1109
+
1110
+ function Scale(_a) {
1111
+ var { gap = 6, options, children, className, style } = _a, props = __rest(_a, ["gap", "options", "children", "className", "style"]);
1112
+ const min = useMin();
1113
+ const max = useMax();
1114
+ const step = useStep();
1115
+ const vertical = useVertical();
1116
+ const reverse = useReverse();
1117
+ const optionsList = options
1118
+ ? generateOptionsList(options, min, max, step)
1119
+ : [];
1120
+ if (xor(vertical, reverse))
1121
+ optionsList.reverse();
1122
+ return (jsx("div", Object.assign({ className: clsx('tremolo-slider-scale', className), style: Object.assign({ marginLeft: vertical ? gap : undefined, marginTop: !vertical ? gap : undefined }, style) }, props, { children: options
1123
+ ? optionsList.map(({ value, type }, index) => {
1124
+ return jsx(ScaleOption, { value: value, type: type }, index);
1125
+ })
1126
+ : children })));
593
1127
  }
594
1128
 
595
- const SliderThumb = forwardRef(({ size, width = 22, height = 22, color = '#4e76e6', hoverColor = color, disabledColor = '#5d6478', disabledHoverColor = disabledColor, zIndex = 100, children, className, style, __disabled, __css, }, ref) => {
1129
+ const defaultThumbSize = 22;
1130
+ const SliderThumb = forwardRef(({ size, width = defaultThumbSize, height = defaultThumbSize, children, className, style, __percent, }, ref) => {
596
1131
  const wrapperRef = useRef(null);
1132
+ const vertical = useVertical();
1133
+ const disabled = useDisabled();
597
1134
  useImperativeHandle(ref, () => {
598
1135
  return {
599
1136
  focus() {
600
1137
  var _a;
601
- (_a = wrapperRef.current) === null || _a === undefined ? undefined : _a.focus();
1138
+ (_a = wrapperRef.current) === null || _a === void 0 ? void 0 : _a.focus();
602
1139
  },
603
1140
  blur() {
604
1141
  var _a;
605
- (_a = wrapperRef.current) === null || _a === undefined ? undefined : _a.blur();
1142
+ (_a = wrapperRef.current) === null || _a === void 0 ? void 0 : _a.blur();
606
1143
  },
607
1144
  };
608
1145
  }, []);
609
- return (jsx("div", { className: "tremolo-slider-thumb-wrapper", css: css(Object.assign({ position: 'absolute', translate: '-50% -50%', zIndex: zIndex }, __css)), children: children ? (children) : (
1146
+ return (jsx("div", { className: "tremolo-slider-thumb-wrapper", style: {
1147
+ top: vertical ? `${__percent}%` : '50%',
1148
+ left: !vertical ? `${__percent}%` : '50%',
1149
+ }, children: children ? (children) : (
610
1150
  // default slider thumb
611
1151
  jsx("div", { ref: wrapperRef, className: clsx('tremolo-slider-thumb', className),
612
1152
  // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
613
- tabIndex: 0, css: css(Object.assign({ background: __disabled ? disabledColor : color, width: size !== null && size !== undefined ? size : width, height: size !== null && size !== undefined ? size : height, borderRadius: '50%', outline: 'none', '&:hover': {
614
- background: __disabled ? disabledHoverColor : hoverColor
615
- }, '&:focus': !__disabled && {
616
- boxShadow: '0px 0px 0px 3px rgba(var(--tremolo-theme-color-rgb), 0.2)',
617
- } }, style)) })) }));
1153
+ 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) })) }));
618
1154
  });
619
1155
 
620
- function SliderTrack({ length = 140, thickness = 10, activeBg = '#7998ec', inactiveBg = '#eee', activeHoverBg = '#6387e9', inactiveHoverBg = '#e0e0e0', disabledActiveBg = '#858890', disabledInactiveBg = '#ddd', disabledActiveHoverBg = disabledActiveBg, disabledInactiveHoverBg = disabledInactiveBg, children, className, style, __thumb, __direction, __disabled, __percent, }) {
621
- const active = __disabled ? disabledActiveBg : activeBg;
622
- const inactive = __disabled ? disabledInactiveBg : inactiveBg;
623
- const activeHover = __disabled ? disabledActiveHoverBg : activeHoverBg;
624
- const inactiveHover = __disabled ? disabledInactiveHoverBg : inactiveHoverBg;
625
- return (jsxs("div", { className: clsx('tremolo-slider-track', className), css: css(Object.assign({ position: 'relative', background: `linear-gradient(to ${gradientDirection(__direction)}, ${active} ${__percent}%, ${inactive} ${__percent}%)`, borderRadius: styleHelper(thickness, '/', 2), width: isHorizontal(__direction) ? length : thickness, height: isHorizontal(__direction) ? thickness : length, ':hover': {
626
- background: `linear-gradient(to ${gradientDirection(__direction)}, ${activeHover} ${__percent}%, ${inactiveHover} ${__percent}%)`
627
- } }, style)), children: [children, __thumb] }));
1156
+ const defaultLength = 140;
1157
+ const defaultThickness = 10;
1158
+ function SliderTrack({ length = defaultLength, thickness = defaultThickness, active, inactive, children, className, style, __thumb, __percent, }) {
1159
+ const vertical = useVertical();
1160
+ const reverse = useReverse();
1161
+ const disabled = useDisabled();
1162
+ const direction = vertical ? 'bottom' : 'right';
1163
+ const colors = {
1164
+ '--active': active,
1165
+ '--inactive': inactive,
1166
+ };
1167
+ return (jsxs("div", { className: clsx('tremolo-slider-track', className), "aria-disabled": disabled, style: Object.assign(Object.assign(Object.assign({}, colors), { background: xor(vertical, reverse)
1168
+ ? `linear-gradient(to ${direction}, var(--inactive, #eee) ${__percent}%, var(--active, #7998ec) ${__percent}%)`
1169
+ : `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] }));
628
1170
  }
629
1171
 
630
1172
  /**
631
1173
  * Customizable slider
632
1174
  */
633
1175
  const Slider = forwardRef((_a, ref) => {
634
- var _b, _c, _d, _e, _f, _g, _h;
635
- var { value, min, max, step = 1, skew = 1, direction = 'right', scale, scaleOption, className, style, bodyNoSelect = true, wheel = ['raw', 1], keyboard = ['raw', 1], disabled = false, readonly = false, onChange, children } = _a; __rest(_a, ["value", "min", "max", "step", "skew", "direction", "scale", "scaleOption", "className", "style", "bodyNoSelect", "wheel", "keyboard", "disabled", "readonly", "onChange", "children"]);
1176
+ var _b, _c, _d;
1177
+ 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"]);
636
1178
  // -- state and ref ---
637
1179
  const trackElementRef = useRef(null);
638
1180
  const thumbRef = useRef(null);
639
1181
  const thumbDragged = useRef(false);
640
- const percent = toFixed$2(normalizeValue(value, min, max, skew) * 100);
641
- const percentRev = isReversed(direction) ? toFixed$2(100 - percent) : percent;
642
- const calcPercent = (v) => {
643
- return toFixed$2(isReversed(direction)
644
- ? 100 - normalizeValue(v, min, max, skew) * 100
645
- : normalizeValue(v, min, max, skew) * 100);
646
- };
647
- if (wheel.length != 2) {
648
- throw new Error(`Slider.wheel expect ['normalized' | 'raw', number]`);
649
- }
650
- if (keyboard.length != 2) {
651
- throw new Error(`Slider.keyboard expect ['normalized' | 'raw', number]`);
652
- }
653
- const scalesList = parseScaleOrderList(scale, min, max, step);
654
- if (isReversed(direction))
655
- scalesList.reverse();
656
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
657
- let trackProps, thumbProps;
1182
+ // --- interpret props ---
1183
+ const p = toFixed$2(normalizeValue(value, min, max, skew) * 100);
1184
+ const rev = toFixed$2(100 - p);
1185
+ // NOTE
1186
+ // normal -> normal (right)
1187
+ // vertical -> rev (up)
1188
+ // reverse -> rev (left)
1189
+ // vertical & reverse -> normal (down)
1190
+ const displayReversed = useMemo(() => xor(vertical, reverse), [vertical, reverse]);
1191
+ const percent = displayReversed ? rev : p;
1192
+ let trackProps = {};
1193
+ let thumbProps = {};
1194
+ let scaleComponent = undefined;
658
1195
  if (children != undefined) {
659
1196
  React.Children.forEach(children, (child) => {
660
1197
  if (React.isValidElement(child)) {
@@ -664,6 +1201,9 @@ const Slider = forwardRef((_a, ref) => {
664
1201
  else if (child.type == SliderTrack) {
665
1202
  trackProps = child.props;
666
1203
  }
1204
+ else if (child.type == Scale) {
1205
+ scaleComponent = child;
1206
+ }
667
1207
  else {
668
1208
  throw new Error('only <SliderThumb> or <SliderTrack>');
669
1209
  }
@@ -675,8 +1215,12 @@ const Slider = forwardRef((_a, ref) => {
675
1215
  }
676
1216
  // --- internal functions ---
677
1217
  const handleValue = (event) => {
678
- if (!trackElementRef.current || !thumbDragged.current || !onChange || readonly)
1218
+ if (!trackElementRef.current ||
1219
+ !thumbDragged.current ||
1220
+ !onChange ||
1221
+ readonly) {
679
1222
  return;
1223
+ }
680
1224
  const isTouch = event instanceof TouchEvent;
681
1225
  if (isTouch && event.cancelable)
682
1226
  event.preventDefault();
@@ -685,10 +1229,10 @@ const Slider = forwardRef((_a, ref) => {
685
1229
  const { left: x1, top: y1, right: x2, bottom: y2, } = trackElementRef.current.getBoundingClientRect();
686
1230
  const mouseX = isTouch ? event.touches[0].clientX : event.clientX;
687
1231
  const mouseY = isTouch ? event.touches[0].clientY : event.clientY;
688
- const n = isHorizontal(direction)
689
- ? normalizeValue(mouseX, x1, x2)
690
- : normalizeValue(mouseY, y1, y2);
691
- const v = rawValue(isReversed(direction) ? 1 - n : n, min, max, skew);
1232
+ const n = vertical
1233
+ ? normalizeValue(mouseY, y1, y2)
1234
+ : normalizeValue(mouseX, x1, x2);
1235
+ const v = rawValue(displayReversed ? 1 - n : n, min, max, skew);
692
1236
  onChange(clamp(stepValue(v, step), min, max));
693
1237
  };
694
1238
  const handleKeyDown = useCallback((event) => {
@@ -697,8 +1241,8 @@ const Slider = forwardRef((_a, ref) => {
697
1241
  const key = event.key;
698
1242
  if (['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown'].includes(key)) {
699
1243
  event.preventDefault();
700
- let x = (key == 'ArrowRight' || key == 'ArrowUp') ? keyboard[1] : -keyboard[1];
701
- if (direction == 'down' || direction == 'left')
1244
+ let x = key == 'ArrowRight' || key == 'ArrowUp' ? keyboard[1] : -keyboard[1];
1245
+ if (reverse)
702
1246
  x *= -1;
703
1247
  let v;
704
1248
  if (keyboard[0] == 'normalized') {
@@ -710,15 +1254,23 @@ const Slider = forwardRef((_a, ref) => {
710
1254
  }
711
1255
  onChange(clamp(stepValue(v, step), min, max));
712
1256
  }
713
- }, [value, min, max, step, skew, direction, keyboard, onChange, readonly]);
1257
+ }, [value, min, max, step, skew, reverse, keyboard, onChange, readonly]);
714
1258
  // --- hooks ---
715
- const touchMoveRefCallback = useRefCallbackEvent('touchmove', handleValue, { passive: false }, [min, max, skew, step, direction, onChange, readonly]);
1259
+ const touchMoveRefCallback = useRefCallbackEvent('touchmove', handleValue, { passive: false }, [min, max, skew, step, vertical, reverse, onChange, readonly]);
716
1260
  const wheelRefCallback = useRefCallbackEvent('wheel', (event) => {
717
1261
  if (!wheel || !onChange || readonly)
718
1262
  return;
719
1263
  event.preventDefault();
720
- let x = event.deltaY > 0 ? wheel[1] : -wheel[1];
721
- if (isReversed(direction) || isHorizontal(direction))
1264
+ let x;
1265
+ if (!vertical && event.deltaX != 0) {
1266
+ x = event.deltaX > 0 ? wheel[1] : -wheel[1];
1267
+ }
1268
+ else {
1269
+ if (event.deltaY == 0)
1270
+ return;
1271
+ x = event.deltaY > 0 ? -wheel[1] : wheel[1];
1272
+ }
1273
+ if (vertical && reverse)
722
1274
  x *= -1;
723
1275
  let v;
724
1276
  if (wheel[0] == 'normalized') {
@@ -744,65 +1296,227 @@ const Slider = forwardRef((_a, ref) => {
744
1296
  return {
745
1297
  focus() {
746
1298
  var _a;
747
- (_a = thumbRef.current) === null || _a === undefined ? undefined : _a.focus();
1299
+ (_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.focus();
1300
+ },
1301
+ blur() {
1302
+ var _a;
1303
+ (_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.blur();
1304
+ },
1305
+ };
1306
+ }, []);
1307
+ return (jsx(SliderValuesProvider, { min: min, max: max, step: step, skew: skew, vertical: vertical, reverse: reverse, disabled: disabled, children: jsx("div", Object.assign({ className: clsx('tremolo-slider', className), ref: (div) => {
1308
+ wheelRefCallback(div);
1309
+ touchMoveRefCallback(div);
1310
+ }, 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) => {
1311
+ thumbDragged.current = true;
1312
+ handleValue(event);
1313
+ }, 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: jsxs("div", { style: {
1314
+ display: 'flex',
1315
+ flexDirection: vertical ? 'row' : 'column',
1316
+ }, children: [jsx("div", { className: "tremolo-slider-track-wrapper", ref: trackElementRef, children: jsx(SliderTrack, Object.assign({ __percent: percent, __thumb: jsx(SliderThumb, Object.assign({ ref: thumbRef, __percent: percent }, thumbProps)) }, trackProps)) }), scaleComponent] }) })) }));
1317
+ });
1318
+
1319
+ function WheelObserver(props) {
1320
+ const { children, onWheel, as: Component = 'div' } = props, attributes = __rest(props, ["children", "onWheel", "as"]);
1321
+ const wheelRefCallback = useRefCallbackEvent('wheel', (event) => {
1322
+ if (onWheel)
1323
+ onWheel(event);
1324
+ }, { passive: false }, [onWheel]);
1325
+ return (jsx(Component, Object.assign({ ref: wheelRefCallback }, attributes, { children: children })));
1326
+ }
1327
+
1328
+ function XYPadArea({ width = 120, height = 120, bg = '#eee', children, className, style, __thumb, }) {
1329
+ return (jsxs("div", { className: clsx('tremolo-xy-pad-area', className), style: Object.assign({ position: 'relative', width: width, height: height, background: bg }, style), children: [children, __thumb] }));
1330
+ }
1331
+
1332
+ const XYPadThumb = forwardRef(({ size, width = 22, height = 22, children, wrapperClassName, wrapperStyle, className, style, __disabled, __css, }, ref) => {
1333
+ const wrapperRef = useRef(null);
1334
+ useImperativeHandle(ref, () => {
1335
+ return {
1336
+ focus() {
1337
+ var _a;
1338
+ (_a = wrapperRef.current) === null || _a === void 0 ? void 0 : _a.focus();
1339
+ },
1340
+ blur() {
1341
+ var _a;
1342
+ (_a = wrapperRef.current) === null || _a === void 0 ? void 0 : _a.blur();
1343
+ },
1344
+ };
1345
+ }, []);
1346
+ return (jsx("div", { className: clsx('tremolo-xy-pad-thumb-wrapper', wrapperClassName), style: Object.assign(Object.assign({}, __css), wrapperStyle), children: children ? (children) : (
1347
+ // default thumb
1348
+ jsx("div", { ref: wrapperRef, className: clsx('tremolo-xy-pad-thumb', className),
1349
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
1350
+ 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) })) }));
1351
+ });
1352
+
1353
+ const defaultValueOptions = {
1354
+ skew: 1,
1355
+ step: 1,
1356
+ reverse: false,
1357
+ wheel: ['raw', 1],
1358
+ keyboard: ['raw', 1],
1359
+ };
1360
+ /**
1361
+ * Simple XYPad
1362
+ */
1363
+ const XYPad = forwardRef((_a, ref) => {
1364
+ var _b, _c, _d;
1365
+ 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"]);
1366
+ const x = useMemo(() => {
1367
+ return Object.assign(Object.assign({}, defaultValueOptions), _x);
1368
+ }, [_x]);
1369
+ const y = useMemo(() => {
1370
+ return Object.assign(Object.assign({}, defaultValueOptions), _y);
1371
+ }, [_y]);
1372
+ // -- state and ref ---
1373
+ const areaElementRef = useRef(null);
1374
+ const thumbRef = useRef(null);
1375
+ const thumbDragged = useRef(false);
1376
+ // --- interpret props ---
1377
+ const nx = normalizeValue(x.value, x.min, x.max, x.skew);
1378
+ const ny = normalizeValue(y.value, y.min, y.max, y.skew);
1379
+ const percentX = toFixed$2((x.reverse ? 1 - nx : nx) * 100);
1380
+ const percentY = toFixed$2((y.reverse ? 1 - ny : ny) * 100);
1381
+ let areaProps = {}, thumbProps = {};
1382
+ if (children != undefined) {
1383
+ React.Children.forEach(children, (child) => {
1384
+ if (React.isValidElement(child)) {
1385
+ if (child.type == XYPadThumb) {
1386
+ thumbProps = child.props;
1387
+ }
1388
+ else if (child.type == XYPadArea) {
1389
+ areaProps = child.props;
1390
+ }
1391
+ else {
1392
+ throw new Error('only <XYPadThumb> or <XYPadArea>');
1393
+ }
1394
+ }
1395
+ else {
1396
+ throw new Error('children is an invalid element.');
1397
+ }
1398
+ });
1399
+ }
1400
+ // --- internal functions ---
1401
+ const handleValue = (event) => {
1402
+ if (!areaElementRef.current ||
1403
+ !thumbDragged.current ||
1404
+ !onChange ||
1405
+ readonly)
1406
+ return;
1407
+ const isTouch = event instanceof TouchEvent;
1408
+ if (isTouch && event.cancelable)
1409
+ event.preventDefault();
1410
+ if (bodyNoSelect)
1411
+ addNoSelect();
1412
+ const { left: x1, top: y1, right: x2, bottom: y2, } = areaElementRef.current.getBoundingClientRect();
1413
+ const mouseX = isTouch ? event.touches[0].clientX : event.clientX;
1414
+ const mouseY = isTouch ? event.touches[0].clientY : event.clientY;
1415
+ const nx = normalizeValue(mouseX, x1, x2);
1416
+ const ny = normalizeValue(mouseY, y1, y2);
1417
+ const vx = rawValue(x.reverse ? 1 - nx : nx, x.min, x.max, x.skew);
1418
+ const vy = rawValue(y.reverse ? 1 - ny : ny, y.min, y.max, y.skew);
1419
+ const newX = clamp(stepValue(vx, x.step), x.min, x.max);
1420
+ const newY = clamp(stepValue(vy, y.step), y.min, y.max);
1421
+ onChange(newX, newY);
1422
+ };
1423
+ const handleKeyDown = useCallback((event) => {
1424
+ if (!onChange || readonly)
1425
+ return;
1426
+ const key = event.key;
1427
+ if (['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown'].includes(key)) {
1428
+ const isHorizontal = key == 'ArrowRight' || key == 'ArrowLeft';
1429
+ const target = isHorizontal ? x : y;
1430
+ if (!target.keyboard)
1431
+ return;
1432
+ event.preventDefault();
1433
+ let delta = target.keyboard[1];
1434
+ if (key == 'ArrowLeft' || key == 'ArrowUp')
1435
+ delta *= -1;
1436
+ if (target.reverse)
1437
+ delta *= -1;
1438
+ let v;
1439
+ if (target.keyboard[0] == 'normalized') {
1440
+ const n = normalizeValue(target.value, target.min, target.max, target.skew);
1441
+ v = rawValue(n + delta, target.min, target.max, target.skew);
1442
+ }
1443
+ else {
1444
+ v = target.value + delta;
1445
+ }
1446
+ const newValue = clamp(stepValue(v, target.step), target.min, target.max);
1447
+ if (isHorizontal) {
1448
+ onChange(newValue, y.value);
1449
+ }
1450
+ else {
1451
+ onChange(x.value, newValue);
1452
+ }
1453
+ }
1454
+ }, [x, y, onChange, readonly]);
1455
+ // --- hooks ---
1456
+ const touchMoveRefCallback = useRefCallbackEvent('touchmove', handleValue, { passive: false }, [x, onChange, readonly]);
1457
+ const wheelRefCallback = useRefCallbackEvent('wheel', (event) => {
1458
+ if (!onChange || readonly)
1459
+ return;
1460
+ const target = event.shiftKey ? x : y;
1461
+ if (!target.wheel)
1462
+ return;
1463
+ event.preventDefault();
1464
+ let delta = target.wheel[1];
1465
+ if (event.deltaY < 0)
1466
+ delta *= -1;
1467
+ if (target.reverse)
1468
+ delta *= -1;
1469
+ let v;
1470
+ if (target.wheel[0] == 'normalized') {
1471
+ const n = normalizeValue(target.value, target.min, target.max, target.skew);
1472
+ v = rawValue(n + delta, target.min, target.max, target.skew);
1473
+ }
1474
+ else {
1475
+ v = target.value + delta;
1476
+ }
1477
+ const newValue = clamp(stepValue(v, target.step), target.min, target.max);
1478
+ if (event.shiftKey) {
1479
+ onChange(newValue, y.value);
1480
+ }
1481
+ else {
1482
+ onChange(x.value, newValue);
1483
+ }
1484
+ }, {
1485
+ passive: false,
1486
+ }, [x, y]);
1487
+ useEventListener(window, 'mousemove', (event) => {
1488
+ handleValue(event);
1489
+ });
1490
+ useEventListener(window, 'mouseup', () => {
1491
+ thumbDragged.current = false;
1492
+ if (bodyNoSelect)
1493
+ removeNoSelect();
1494
+ });
1495
+ useImperativeHandle(ref, () => {
1496
+ return {
1497
+ focus() {
1498
+ var _a;
1499
+ (_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.focus();
748
1500
  },
749
1501
  blur() {
750
1502
  var _a;
751
- (_a = thumbRef.current) === null || _a === undefined ? undefined : _a.blur();
1503
+ (_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.blur();
752
1504
  },
753
1505
  };
754
1506
  }, []);
755
- return (jsxs("div", { className: clsx('tremolo-slider', className), ref: (div) => {
1507
+ return (
1508
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions
1509
+ jsx("div", Object.assign({ className: clsx('tremolo-xy-pad', className), ref: (div) => {
756
1510
  wheelRefCallback(div);
757
1511
  touchMoveRefCallback(div);
758
- }, tabIndex: -1, role: "slider", "aria-valuenow": value, "aria-valuemin": min, "aria-valuemax": max, "aria-orientation": isHorizontal(direction) ? 'horizontal' : 'vertical', "aria-disabled": disabled, "aria-readonly": readonly, css: css(Object.assign({ display: 'inline-block', margin: `calc(${styleHelper((_b = thumbProps === null || thumbProps === undefined ? undefined : thumbProps.size) !== null && _b !== undefined ? _b : 22)} / 2)`, cursor: readonly ? 'not-allowed' : 'pointer', outline: 0 }, style)), onPointerDown: (event) => {
1512
+ }, 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) => {
759
1513
  thumbDragged.current = true;
760
1514
  handleValue(event);
761
- }, onKeyDown: handleKeyDown, onFocus: (_c = thumbRef.current) === null || _c === undefined ? undefined : _c.focus, onBlur: (_d = thumbRef.current) === null || _d === undefined ? undefined : _d.blur, children: [jsx(Global, { styles: {
762
- '.no-select': {
763
- userSelect: 'none',
764
- },
765
- } }), jsxs("div", { css: css({
766
- display: 'flex',
767
- flexDirection: isHorizontal(direction) ? 'column' : 'row',
768
- }), children: [jsx("div", { className: "tremolo-slider-track-wrapper", ref: trackElementRef, children: jsx(SliderTrack, Object.assign({ __direction: direction, __disabled: disabled, __percent: percent, __thumb: jsx(SliderThumb, Object.assign({ ref: thumbRef, __disabled: disabled, __css: {
769
- top: isHorizontal(direction) ? '50%' : `${percentRev}%`,
770
- left: isHorizontal(direction) ? `${percentRev}%` : '50%',
771
- } }, thumbProps)) }, trackProps)) }), scale && (jsx("div", { className: "tremolo-slider-scale-wrapper", css: css(Object.assign({ display: 'block', position: 'relative', marginLeft: isVertical(direction)
772
- ? ((_e = scaleOption === null || scaleOption === undefined ? undefined : scaleOption.gap) !== null && _e !== undefined ? _e : `calc((22px - ${styleHelper((_f = trackProps === null || trackProps === undefined ? undefined : trackProps.thickness) !== null && _f !== undefined ? _f : 10)}) / 2)`)
773
- : undefined, marginTop: isHorizontal(direction)
774
- ? ((_g = scaleOption === null || scaleOption === undefined ? undefined : scaleOption.gap) !== null && _g !== undefined ? _g : `calc((22px - ${styleHelper((_h = trackProps === null || trackProps === undefined ? undefined : trackProps.thickness) !== null && _h !== undefined ? _h : 10)}) / 2)`)
775
- : undefined }, scaleOption === null || scaleOption === undefined ? undefined : scaleOption.style)), children: scalesList.map((item, index) => {
776
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
777
- return (jsxs("div", { css: css({
778
- display: 'flex',
779
- flexDirection: isHorizontal(direction) ? 'column' : 'row',
780
- justifyContent: 'center',
781
- alignItems: 'center',
782
- position: 'absolute',
783
- left: isHorizontal(direction) ? `${calcPercent(item.at)}%` : undefined,
784
- top: isVertical(direction) ? `${calcPercent(item.at)}%` : undefined,
785
- translate: isHorizontal(direction) ? '-50% 0' : '0 -50%',
786
- zIndex: 10,
787
- }), children: [((_b = (_a = item.type) !== null && _a !== undefined ? _a : scaleOption === null || scaleOption === undefined ? undefined : scaleOption.defaultType) !== null && _b !== undefined ? _b : 'mark-number') !=
788
- 'number' && (jsx("div", { className: "tremolo-slider-scale-mark", css: css({
789
- width: isHorizontal(direction)
790
- ? ((_d = (_c = item.style) === null || _c === undefined ? undefined : _c.thickness) !== null && _d !== undefined ? _d : 1)
791
- : ((_f = (_e = item.style) === null || _e === undefined ? undefined : _e.length) !== null && _f !== undefined ? _f : '0.5rem'),
792
- height: isVertical(direction)
793
- ? ((_h = (_g = item.style) === null || _g === undefined ? undefined : _g.thickness) !== null && _h !== undefined ? _h : 1)
794
- : ((_k = (_j = item.style) === null || _j === undefined ? undefined : _j.length) !== null && _k !== undefined ? _k : '0.5rem'),
795
- backgroundColor: (_o = (_m = (_l = item.style) === null || _l === undefined ? undefined : _l.markColor) !== null && _m !== undefined ? _m : scaleOption === null || scaleOption === undefined ? undefined : scaleOption.markColor) !== null && _o !== undefined ? _o : '#222',
796
- marginBottom: isHorizontal(direction) ? 2 : undefined,
797
- marginRight: isVertical(direction) ? 2 : undefined,
798
- }) })), ((_q = (_p = item.type) !== null && _p !== undefined ? _p : scaleOption === null || scaleOption === undefined ? undefined : scaleOption.defaultType) !== null && _q !== undefined ? _q : 'mark-number') !=
799
- 'mark' && (jsx("div", { className: "tremolo-slider-scale-number", css: css({
800
- color: (_s = (_r = item.style) === null || _r === undefined ? undefined : _r.labelColor) !== null && _s !== undefined ? _s : scaleOption === null || scaleOption === undefined ? undefined : scaleOption.labelColor,
801
- width: scaleOption === null || scaleOption === undefined ? undefined : scaleOption.labelWidth,
802
- textAlign: 'right',
803
- }), children: (_t = item.text) !== null && _t !== undefined ? _t : item.at }))] }, index));
804
- }) }))] })] }));
1515
+ }, 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: jsx("div", { className: "tremolo-xy-pad-area-wrapper", ref: areaElementRef, children: jsx(XYPadArea, Object.assign({ __thumb: jsx(XYPadThumb, Object.assign({ ref: thumbRef, __disabled: disabled, __css: {
1516
+ top: `${percentY}%`,
1517
+ left: `${percentX}%`,
1518
+ } }, thumbProps)) }, areaProps)) }) })));
805
1519
  });
806
1520
 
807
- export { AnimationCanvas, Knob, NumberInput, Slider, SliderThumb, SliderTrack, gradientDirection };
1521
+ export { AnimationCanvas, AnimationKnob, BlackKey, DragObserver, KeyLabel, Knob, NumberInput, Piano, Scale, ScaleOption, Shortcuts, Slider, SliderThumb, SliderTrack, SliderValuesProvider, WheelObserver, WhiteKey, XYPad, XYPadArea, XYPadThumb, defaultLength, defaultThickness, defaultThumbSize, useDisabled, useMax, useMin, useReverse, useSkew, useStep, useVertical };
808
1522
  //# sourceMappingURL=index.js.map