@tremolo-ui/react 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/index.js +196 -93
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/types/components/AnimationCanvas/index.d.ts +3 -4
  5. package/dist/cjs/types/components/AnimationCanvas/index.d.ts.map +1 -1
  6. package/dist/cjs/types/components/Knob/index.d.ts +6 -7
  7. package/dist/cjs/types/components/Knob/index.d.ts.map +1 -1
  8. package/dist/cjs/types/components/NumberInput/index.d.ts +10 -7
  9. package/dist/cjs/types/components/NumberInput/index.d.ts.map +1 -1
  10. package/dist/cjs/types/components/Piano/BlackKey.d.ts +4 -0
  11. package/dist/cjs/types/components/Piano/BlackKey.d.ts.map +1 -0
  12. package/dist/cjs/types/components/Piano/KeyLabel.d.ts +20 -0
  13. package/dist/cjs/types/components/Piano/KeyLabel.d.ts.map +1 -0
  14. package/dist/cjs/types/components/Piano/WhiteKey.d.ts +4 -0
  15. package/dist/cjs/types/components/Piano/WhiteKey.d.ts.map +1 -0
  16. package/dist/cjs/types/components/Piano/index.d.ts +35 -0
  17. package/dist/cjs/types/components/Piano/index.d.ts.map +1 -0
  18. package/dist/cjs/types/components/Piano/key.d.ts +39 -0
  19. package/dist/cjs/types/components/Piano/key.d.ts.map +1 -0
  20. package/dist/cjs/types/components/Piano/keyboardShortcuts.d.ts +12 -0
  21. package/dist/cjs/types/components/Piano/keyboardShortcuts.d.ts.map +1 -0
  22. package/dist/cjs/types/components/Slider/Thumb.d.ts +17 -2
  23. package/dist/cjs/types/components/Slider/Thumb.d.ts.map +1 -1
  24. package/dist/cjs/types/components/Slider/Track.d.ts +21 -2
  25. package/dist/cjs/types/components/Slider/Track.d.ts.map +1 -1
  26. package/dist/cjs/types/components/Slider/index.d.ts +18 -10
  27. package/dist/cjs/types/components/Slider/index.d.ts.map +1 -1
  28. package/dist/cjs/types/components/_util/index.d.ts +4 -0
  29. package/dist/cjs/types/components/_util/index.d.ts.map +1 -0
  30. package/dist/esm/index.js +197 -94
  31. package/dist/esm/index.js.map +1 -1
  32. package/dist/esm/types/components/AnimationCanvas/index.d.ts +3 -4
  33. package/dist/esm/types/components/AnimationCanvas/index.d.ts.map +1 -1
  34. package/dist/esm/types/components/Knob/index.d.ts +6 -7
  35. package/dist/esm/types/components/Knob/index.d.ts.map +1 -1
  36. package/dist/esm/types/components/NumberInput/index.d.ts +10 -7
  37. package/dist/esm/types/components/NumberInput/index.d.ts.map +1 -1
  38. package/dist/esm/types/components/Piano/BlackKey.d.ts +4 -0
  39. package/dist/esm/types/components/Piano/BlackKey.d.ts.map +1 -0
  40. package/dist/esm/types/components/Piano/KeyLabel.d.ts +20 -0
  41. package/dist/esm/types/components/Piano/KeyLabel.d.ts.map +1 -0
  42. package/dist/esm/types/components/Piano/WhiteKey.d.ts +4 -0
  43. package/dist/esm/types/components/Piano/WhiteKey.d.ts.map +1 -0
  44. package/dist/esm/types/components/Piano/index.d.ts +35 -0
  45. package/dist/esm/types/components/Piano/index.d.ts.map +1 -0
  46. package/dist/esm/types/components/Piano/key.d.ts +39 -0
  47. package/dist/esm/types/components/Piano/key.d.ts.map +1 -0
  48. package/dist/esm/types/components/Piano/keyboardShortcuts.d.ts +12 -0
  49. package/dist/esm/types/components/Piano/keyboardShortcuts.d.ts.map +1 -0
  50. package/dist/esm/types/components/Slider/Thumb.d.ts +17 -2
  51. package/dist/esm/types/components/Slider/Thumb.d.ts.map +1 -1
  52. package/dist/esm/types/components/Slider/Track.d.ts +21 -2
  53. package/dist/esm/types/components/Slider/Track.d.ts.map +1 -1
  54. package/dist/esm/types/components/Slider/index.d.ts +18 -10
  55. package/dist/esm/types/components/Slider/index.d.ts.map +1 -1
  56. package/dist/esm/types/components/_util/index.d.ts +4 -0
  57. package/dist/esm/types/components/_util/index.d.ts.map +1 -0
  58. package/dist/styles/{variables.css → index.css} +4 -0
  59. package/package.json +12 -16
  60. package/src/components/AnimationCanvas/index.tsx +3 -7
  61. package/src/components/Knob/index.tsx +16 -20
  62. package/src/components/NumberInput/index.tsx +19 -12
  63. package/src/components/Piano/BlackKey.tsx +108 -0
  64. package/src/components/Piano/KeyLabel.tsx +69 -0
  65. package/src/components/Piano/WhiteKey.tsx +109 -0
  66. package/src/components/Piano/index.tsx +222 -0
  67. package/src/components/Piano/key.ts +43 -0
  68. package/src/components/Piano/keyboardShortcuts.ts +12 -0
  69. package/src/components/Slider/Thumb.tsx +56 -15
  70. package/src/components/Slider/Track.tsx +62 -3
  71. package/src/components/Slider/index.tsx +125 -89
  72. package/src/components/_util/index.ts +11 -0
  73. package/src/styles/{variables.css → index.css} +4 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 mimoz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/cjs/index.js CHANGED
@@ -96,7 +96,7 @@ function AnimationCanvas(_a) {
96
96
  if (!parent)
97
97
  throw new Error("Canvas doesn't have a parent element.");
98
98
  const memoCanvas = memoCanvasRef.current;
99
- const memoContext = memoCanvas === null || memoCanvas === void 0 ? void 0 : memoCanvas.getContext('2d', options);
99
+ const memoContext = memoCanvas === null || memoCanvas === undefined ? undefined : memoCanvas.getContext('2d', options);
100
100
  const resizeObserver = new ResizeObserver(() => {
101
101
  // console.log('resize')
102
102
  // Prevents loss of some context when the canvas is resized
@@ -152,15 +152,27 @@ function AnimationCanvas(_a) {
152
152
  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, css: react.css(style), ref: canvasRef }, props)), relativeSize && reduceFlickering && (jsxRuntime.jsx("canvas", { style: { display: 'none' }, width: relativeSize ? 0 : width, height: relativeSize ? 0 : height, ref: memoCanvasRef }))] }));
153
153
  }
154
154
 
155
+ /**
156
+ * clamp value between min and max
157
+ * @category Math
158
+ */
155
159
  function clamp(value, min, max) {
156
160
  return Math.max(min, Math.min(value, max));
157
161
  }
162
+ /**
163
+ * Normalize the value from 0 to 1
164
+ * @category Math
165
+ */
158
166
  function normalizeValue(rawValue, min, max, skew = 1) {
159
167
  if (min >= max)
160
168
  throw new RangeError('requirements: min < max');
161
169
  const v = clamp((rawValue - min) / (max - min), 0, 1);
162
170
  return Math.pow(v, skew);
163
171
  }
172
+ /**
173
+ * Convert normalized values back to raw values.
174
+ * @category Math
175
+ */
164
176
  function rawValue(normalizedValue, min, max, skew = 1) {
165
177
  if (min >= max)
166
178
  throw new RangeError('requirements: min < max');
@@ -169,22 +181,38 @@ function rawValue(normalizedValue, min, max, skew = 1) {
169
181
  : Math.exp(Math.log(clamp(normalizedValue, 0, 1)) / skew);
170
182
  return min + v * (max - min);
171
183
  }
184
+ /**
185
+ *
186
+ * @category Math
187
+ */
172
188
  function stepValue(value, step) {
173
189
  var _a;
174
190
  if (step <= 0)
175
191
  throw new RangeError('requirements: step > 0');
176
192
  const quotient = Math.floor(value / step);
177
- const decimalDigits = (_a = decimalPart$1(step)) === null || _a === void 0 ? void 0 : _a.length;
193
+ const decimalDigits = (_a = decimalPart$1(step)) === null || _a === undefined ? undefined : _a.length;
178
194
  const v = toFixed$2(quotient * step, decimalDigits);
179
195
  const next = toFixed$2((quotient + 1) * step, decimalDigits);
180
196
  return Math.abs(value - v) < Math.abs(value - next) ? v : next;
181
197
  }
198
+ /**
199
+ *
200
+ * @category Math
201
+ */
182
202
  function toFixed$2(x, fractionDigits) {
183
203
  return Number(x.toFixed(fractionDigits));
184
204
  }
205
+ /**
206
+ *
207
+ * @category Math
208
+ */
185
209
  function decimalPart$1(x) {
186
210
  return String(x).split('.')[1];
187
211
  }
212
+ /**
213
+ *
214
+ * @category Math
215
+ */
188
216
  function radian(degree) {
189
217
  return (Math.PI * degree) / 180;
190
218
  }
@@ -226,13 +254,13 @@ function useCallbackRef(callback, deps = []) {
226
254
  React.useInsertionEffect(() => {
227
255
  callbackRef.current = callback;
228
256
  });
229
- return React.useCallback((...args) => { var _a; return (_a = callbackRef.current) === null || _a === void 0 ? void 0 : _a.call(callbackRef, ...args); }, deps);
257
+ return React.useCallback((...args) => { var _a; return (_a = callbackRef.current) === null || _a === undefined ? undefined : _a.call(callbackRef, ...args); }, deps);
230
258
  }
231
259
 
232
260
  function useEventListener(target, event, handler, options) {
233
261
  const listener = useCallbackRef(handler);
234
262
  React.useEffect(() => {
235
- const node = typeof target === 'function' ? target() : (target !== null && target !== void 0 ? target : document);
263
+ const node = typeof target === 'function' ? target() : (target !== null && target !== undefined ? target : document);
236
264
  if (!handler || !node)
237
265
  return;
238
266
  node.addEventListener(event, listener, options);
@@ -241,8 +269,8 @@ function useEventListener(target, event, handler, options) {
241
269
  };
242
270
  }, [event, target, options, listener, handler]);
243
271
  return () => {
244
- const node = typeof target === 'function' ? target() : (target !== null && target !== void 0 ? target : document);
245
- node === null || node === void 0 ? void 0 : node.removeEventListener(event, listener, options);
272
+ const node = typeof target === 'function' ? target() : (target !== null && target !== undefined ? target : document);
273
+ node === null || node === undefined ? undefined : node.removeEventListener(event, listener, options);
246
274
  };
247
275
  }
248
276
 
@@ -251,7 +279,7 @@ function useRefCallbackEvent(event, handler, options, deps = []) {
251
279
  let cleanup;
252
280
  return (node) => {
253
281
  if (!node) {
254
- cleanup === null || cleanup === void 0 ? void 0 : cleanup();
282
+ cleanup === null || cleanup === undefined ? undefined : cleanup();
255
283
  return;
256
284
  }
257
285
  node.addEventListener(event, handler, options);
@@ -262,6 +290,13 @@ function useRefCallbackEvent(event, handler, options, deps = []) {
262
290
  }, deps);
263
291
  }
264
292
 
293
+ function addNoSelect() {
294
+ document.body.classList.add('tremolo-no-select');
295
+ }
296
+ function removeNoSelect() {
297
+ document.body.classList.remove('tremolo-no-select');
298
+ }
299
+
265
300
  const defaultOptions = {
266
301
  active: '#7998ec',
267
302
  inactive: '#eee',
@@ -274,14 +309,14 @@ const defaultOptions = {
274
309
  * simple rotary Knob.
275
310
  */
276
311
  function Knob(_a) {
277
- var { value, min, max, defaultValue = min, startValue = min, step = 1, skew = 1, size, width = 50, height = 50, options = defaultOptions, draw, bodyNoSelect = true, enableWheel, 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", "enableWheel", "enableDoubleClickDefault", "style", "className", "onChange", "_active", "_focus", "_hover"]);
312
+ 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"]);
278
313
  // -- state and ref ---
279
314
  // const [privateValue, setPrivateValue] = useState(value);
280
315
  const dragOffsetY = React.useRef(undefined);
281
316
  // --- interpret props ---
282
317
  const opts = Object.assign(Object.assign({}, defaultOptions), options);
283
318
  // const percent = normalizeValue(value, min, max, skew)
284
- const isRelativeSize = typeof (size !== null && size !== void 0 ? size : width) == 'string';
319
+ const isRelativeSize = typeof (size !== null && size !== undefined ? size : width) == 'string';
285
320
  const adaptSize = (size) => {
286
321
  if (typeof size == 'string') {
287
322
  return Number(size.slice(0, -1));
@@ -296,7 +331,7 @@ function Knob(_a) {
296
331
  event.preventDefault();
297
332
  if (dragOffsetY.current) {
298
333
  if (bodyNoSelect)
299
- document.body.classList.add('no-select');
334
+ addNoSelect();
300
335
  const screenY = isTouch ? event.touches[0].screenY : event.screenY;
301
336
  const delta = dragOffsetY.current - screenY;
302
337
  dragOffsetY.current = screenY;
@@ -309,12 +344,12 @@ function Knob(_a) {
309
344
  };
310
345
  // --- hooks ---
311
346
  const wheelRefCallback = useRefCallbackEvent('wheel', (event) => {
312
- if (!enableWheel)
347
+ if (!wheel)
313
348
  return;
314
349
  event.preventDefault();
315
- const x = event.deltaY > 0 ? -enableWheel[1] : enableWheel[1];
350
+ const x = event.deltaY > 0 ? -wheel[1] : wheel[1];
316
351
  let v;
317
- if (enableWheel[0] == 'normalized') {
352
+ if (wheel[0] == 'normalized') {
318
353
  const n = normalizeValue(value, min, max, skew);
319
354
  v = rawValue(n + x, min, max, skew);
320
355
  }
@@ -323,18 +358,18 @@ function Knob(_a) {
323
358
  }
324
359
  if (onChange)
325
360
  onChange(clamp(stepValue(v, step), min, max));
326
- }, { passive: false }, [enableWheel, value, min, max, skew, step, onChange]);
361
+ }, { passive: false }, [wheel, value, min, max, skew, step, onChange]);
327
362
  const touchMoveRefCallback = useRefCallbackEvent('touchmove', handleEvent, { passive: false }, [value, min, max, skew, step, onChange, bodyNoSelect]);
328
363
  useEventListener(window, 'mousemove', handleEvent);
329
364
  useEventListener(window, 'pointerup', () => {
330
365
  dragOffsetY.current = undefined;
331
366
  if (bodyNoSelect)
332
- document.body.classList.remove('no-select');
367
+ removeNoSelect();
333
368
  });
334
369
  return (jsxRuntime.jsxs("div", Object.assign({ className: clsx('tremolo-knob', className), ref: (div) => {
335
370
  wheelRefCallback(div);
336
371
  touchMoveRefCallback(div);
337
- }, role: "slider", tabIndex: 0, "aria-valuenow": value, "aria-valuemax": max, "aria-valuemin": min, css: react.css(Object.assign(Object.assign({ display: 'block', width: size !== null && size !== void 0 ? size : width, height: size !== null && size !== void 0 ? size : height, cursor: 'pointer' }, style), { ':active': Object.assign({}, _active), ':focus': Object.assign({}, _focus), ':hover': Object.assign({}, _hover) })), onPointerDown: (event) => {
372
+ }, role: "slider", tabIndex: 0, "aria-valuenow": value, "aria-valuemax": max, "aria-valuemin": min, css: react.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) => {
338
373
  dragOffsetY.current = event.screenY;
339
374
  handleEvent(event);
340
375
  }, onDoubleClick: () => {
@@ -345,7 +380,7 @@ function Knob(_a) {
345
380
  '.no-select': {
346
381
  userSelect: 'none',
347
382
  },
348
- } }), jsxRuntime.jsx(AnimationCanvas, { width: adaptSize(size !== null && size !== void 0 ? size : width), height: adaptSize(size !== null && size !== void 0 ? size : height), relativeSize: isRelativeSize,
383
+ } }), jsxRuntime.jsx(AnimationCanvas, { width: adaptSize(size !== null && size !== undefined ? size : width), height: adaptSize(size !== null && size !== undefined ? size : height), relativeSize: isRelativeSize,
349
384
  // TODO
350
385
  // reduceFlickering={false}
351
386
  draw: draw ? draw : (ctx, width, height) => {
@@ -390,10 +425,21 @@ function Knob(_a) {
390
425
  } })] })));
391
426
  }
392
427
 
428
+ /**
429
+ * clamp value between min and max
430
+ * @category Math
431
+ */
432
+ /**
433
+ *
434
+ * @category Math
435
+ */
393
436
  function toFixed$1(x, fractionDigits) {
394
437
  return Number(x.toFixed(fractionDigits));
395
438
  }
396
439
 
440
+ /**
441
+ * @module NumberInput
442
+ */
397
443
  function selectUnit(units, value) {
398
444
  if (typeof units == 'string')
399
445
  return [units, 1];
@@ -456,14 +502,14 @@ function parseValue(inputString, units, digit, defaultValue = 0) {
456
502
  * Input with some useful functions for entering numerical values.
457
503
  */
458
504
  function NumberInput(_a) {
459
- var { value, min, max, step, defaultValue = min, units, disabled = false, placeholder, readOnly, tabIndex, strict = false, digit, selectWithFocus = 'none', blurWithEnter = true, className, style, onChange, onFocus, onBlur } = _a, pseudo = __rest(_a, ["value", "min", "max", "step", "defaultValue", "units", "disabled", "placeholder", "readOnly", "tabIndex", "strict", "digit", "selectWithFocus", "blurWithEnter", "className", "style", "onChange", "onFocus", "onBlur"]);
505
+ 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"]);
460
506
  const [showValue, setShowValue] = React.useState(parseValue(String(value), units, digit, defaultValue).formatValue);
461
- const calculatedStrict = units ? false : strict;
462
- const { _active, _focus, _hover } = pseudo;
463
- return (jsxRuntime.jsx("input", { className: clsx('tremolo-number-input', className), value: showValue, min: min, max: max, step: step, type: calculatedStrict ? 'number' : 'text', spellCheck: 'false', disabled: disabled, placeholder: placeholder, readOnly: readOnly, tabIndex: tabIndex, css: react.css(Object.assign(Object.assign({ display: '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) => {
507
+ const calculatedTypeNumber = units ? false : typeNumber;
508
+ const { _active, _focus, _hover } = props, rest = __rest(props, ["_active", "_focus", "_hover"]);
509
+ return (jsxRuntime.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: react.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) => {
464
510
  const v = event.currentTarget.value;
465
511
  setShowValue(v);
466
- const newValue = clamp(parseValue(v, units, digit, defaultValue).rawValue, min !== null && min !== void 0 ? min : -Infinity, max !== null && max !== void 0 ? max : Infinity);
512
+ const newValue = clamp(parseValue(v, units, digit, defaultValue).rawValue, min !== null && min !== undefined ? min : -Infinity, max !== null && max !== undefined ? max : Infinity);
467
513
  if (onChange)
468
514
  onChange(newValue, event);
469
515
  }, onFocus: (event) => {
@@ -488,16 +534,31 @@ function NumberInput(_a) {
488
534
  if (blurWithEnter && event.key == 'Enter') {
489
535
  event.currentTarget.blur();
490
536
  }
491
- } }));
537
+ } }, rest)));
492
538
  }
493
539
 
540
+ /**
541
+ * clamp value between min and max
542
+ * @category Math
543
+ */
544
+ /**
545
+ *
546
+ * @category Math
547
+ */
494
548
  function toFixed(x, fractionDigits) {
495
549
  return Number(x.toFixed(fractionDigits));
496
550
  }
551
+ /**
552
+ *
553
+ * @category Math
554
+ */
497
555
  function decimalPart(x) {
498
556
  return String(x).split('.')[1];
499
557
  }
500
558
 
559
+ /**
560
+ * @module Slider
561
+ */
501
562
  function isHorizontal(d) {
502
563
  return d == 'right' || d == 'left';
503
564
  }
@@ -526,46 +587,76 @@ function parseScaleOrderList(scale, min, max, step) {
526
587
  const per = scale[0] == 'step' ? step : scale[0];
527
588
  const count = Math.floor(max / per) - Math.ceil(min / per) + 1;
528
589
  for (let i = 0; i < count; i++) {
529
- const at = toFixed(per * (Math.ceil(min / per) + i), (_a = decimalPart(per)) === null || _a === void 0 ? void 0 : _a.length);
590
+ const at = toFixed(per * (Math.ceil(min / per) + i), (_a = decimalPart(per)) === null || _a === undefined ? undefined : _a.length);
530
591
  scalesList.push({ at: at, type: scale[1] });
531
592
  }
532
593
  }
533
594
  return scalesList;
534
595
  }
535
596
 
536
- function SliderThumb({ color, children, style, __css, }) {
537
- return (jsxRuntime.jsx("div", { className: "tremolo-slider-thumb-wrapper", css: react.css(Object.assign({ width: 'fit-content', height: 'fit-content', position: 'absolute', translate: '-50% -50%', zIndex: 100 }, __css)), children: children ? (children) : (
597
+ const SliderThumb = React.forwardRef(({ size, width = 22, height = 22, color = '#4e76e6', hoverColor = color, disabledColor = '#5d6478', disabledHoverColor = disabledColor, zIndex = 100, children, className, style, __disabled, __css, }, ref) => {
598
+ const wrapperRef = React.useRef(null);
599
+ React.useImperativeHandle(ref, () => {
600
+ return {
601
+ focus() {
602
+ var _a;
603
+ (_a = wrapperRef.current) === null || _a === undefined ? undefined : _a.focus();
604
+ },
605
+ blur() {
606
+ var _a;
607
+ (_a = wrapperRef.current) === null || _a === undefined ? undefined : _a.blur();
608
+ },
609
+ };
610
+ }, []);
611
+ return (jsxRuntime.jsx("div", { className: "tremolo-slider-thumb-wrapper", css: react.css(Object.assign({ position: 'absolute', translate: '-50% -50%', zIndex: zIndex }, __css)), children: children ? (children) : (
538
612
  // default slider thumb
539
- jsxRuntime.jsx("div", { className: "tremolo-slider-thumb",
613
+ jsxRuntime.jsx("div", { ref: wrapperRef, className: clsx('tremolo-slider-thumb', className),
540
614
  // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
541
- tabIndex: 0, css: react.css(Object.assign({ background: color, width: '1.4rem', height: '1.4rem', borderRadius: '50%', '&:hover': {
542
- background: `color-mix(in srgb, ${color} 95%, black)`
543
- }, '&:active': {
615
+ tabIndex: 0, css: react.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': {
616
+ background: __disabled ? disabledHoverColor : hoverColor
617
+ }, '&:focus': !__disabled && {
544
618
  boxShadow: '0px 0px 0px 3px rgba(var(--tremolo-theme-color-rgb), 0.2)',
545
619
  } }, style)) })) }));
546
- }
620
+ });
547
621
 
548
- function SliderTrack({ children, style, __css, }) {
549
- return (jsxRuntime.jsx("div", { className: "tremolo-slider-track", css: react.css(Object.assign(Object.assign({}, __css), style)), children: children }));
622
+ 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, }) {
623
+ const active = __disabled ? disabledActiveBg : activeBg;
624
+ const inactive = __disabled ? disabledInactiveBg : inactiveBg;
625
+ const activeHover = __disabled ? disabledActiveHoverBg : activeHoverBg;
626
+ const inactiveHover = __disabled ? disabledInactiveHoverBg : inactiveHoverBg;
627
+ return (jsxRuntime.jsxs("div", { className: clsx('tremolo-slider-track', className), css: react.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': {
628
+ background: `linear-gradient(to ${gradientDirection(__direction)}, ${activeHover} ${__percent}%, ${inactiveHover} ${__percent}%)`
629
+ } }, style)), children: [children, __thumb] }));
550
630
  }
551
631
 
552
632
  /**
553
633
  * Customizable slider
554
634
  */
555
- function Slider(_a) {
556
- var _b, _c;
557
- var { value, min, max, step = 1, skew = 1, length = 140, thickness = 10, direction = 'right', scale, scaleOption, color = '#4e76e6', bg = '#eee', className, style, bodyNoSelect = true, enableWheel, onChange, children } = _a; __rest(_a, ["value", "min", "max", "step", "skew", "length", "thickness", "direction", "scale", "scaleOption", "color", "bg", "className", "style", "bodyNoSelect", "enableWheel", "onChange", "children"]);
635
+ const Slider = React.forwardRef((_a, ref) => {
636
+ var _b, _c, _d, _e, _f, _g, _h;
637
+ 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"]);
558
638
  // -- state and ref ---
559
639
  const trackElementRef = React.useRef(null);
560
- const percent = normalizeValue(value, min, max, skew) * 100;
561
- const percentRev = isReversed(direction) ? 100 - percent : percent;
640
+ const thumbRef = React.useRef(null);
641
+ const thumbDragged = React.useRef(false);
642
+ const percent = toFixed$2(normalizeValue(value, min, max, skew) * 100);
643
+ const percentRev = isReversed(direction) ? toFixed$2(100 - percent) : percent;
644
+ const calcPercent = (v) => {
645
+ return toFixed$2(isReversed(direction)
646
+ ? 100 - normalizeValue(v, min, max, skew) * 100
647
+ : normalizeValue(v, min, max, skew) * 100);
648
+ };
649
+ if (wheel.length != 2) {
650
+ throw new Error(`Slider.wheel expect ['normalized' | 'raw', number]`);
651
+ }
652
+ if (keyboard.length != 2) {
653
+ throw new Error(`Slider.keyboard expect ['normalized' | 'raw', number]`);
654
+ }
562
655
  const scalesList = parseScaleOrderList(scale, min, max, step);
563
656
  if (isReversed(direction))
564
657
  scalesList.reverse();
565
658
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
566
- let trackProps;
567
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
568
- let thumbProps;
659
+ let trackProps, thumbProps;
569
660
  if (children != undefined) {
570
661
  React.Children.forEach(children, (child) => {
571
662
  if (React.isValidElement(child)) {
@@ -584,15 +675,15 @@ function Slider(_a) {
584
675
  }
585
676
  });
586
677
  }
587
- const thumbDragged = React.useRef(false);
678
+ // --- internal functions ---
588
679
  const handleValue = (event) => {
589
- if (!trackElementRef.current || !thumbDragged.current)
680
+ if (!trackElementRef.current || !thumbDragged.current || !onChange || readonly)
590
681
  return;
591
682
  const isTouch = event instanceof TouchEvent;
592
683
  if (isTouch && event.cancelable)
593
684
  event.preventDefault();
594
685
  if (bodyNoSelect)
595
- document.body.classList.add('no-select');
686
+ addNoSelect();
596
687
  const { left: x1, top: y1, right: x2, bottom: y2, } = trackElementRef.current.getBoundingClientRect();
597
688
  const mouseX = isTouch ? event.touches[0].clientX : event.clientX;
598
689
  const mouseY = isTouch ? event.touches[0].clientY : event.clientY;
@@ -600,70 +691,90 @@ function Slider(_a) {
600
691
  ? normalizeValue(mouseX, x1, x2)
601
692
  : normalizeValue(mouseY, y1, y2);
602
693
  const v = rawValue(isReversed(direction) ? 1 - n : n, min, max, skew);
603
- const v2 = clamp(stepValue(v, step), min, max);
604
- if (onChange)
605
- onChange(v2);
694
+ onChange(clamp(stepValue(v, step), min, max));
606
695
  };
696
+ const handleKeyDown = React.useCallback((event) => {
697
+ if (!keyboard || !onChange || readonly)
698
+ return;
699
+ const key = event.key;
700
+ if (['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown'].includes(key)) {
701
+ event.preventDefault();
702
+ let x = (key == 'ArrowRight' || key == 'ArrowUp') ? keyboard[1] : -keyboard[1];
703
+ if (direction == 'down' || direction == 'left')
704
+ x *= -1;
705
+ let v;
706
+ if (keyboard[0] == 'normalized') {
707
+ const n = normalizeValue(value, min, max, skew);
708
+ v = rawValue(n + x, min, max, skew);
709
+ }
710
+ else {
711
+ v = value + x;
712
+ }
713
+ onChange(clamp(stepValue(v, step), min, max));
714
+ }
715
+ }, [value, min, max, step, skew, direction, keyboard, onChange, readonly]);
607
716
  // --- hooks ---
608
- const touchMoveRefCallback = useRefCallbackEvent('touchmove', handleValue, { passive: false }, [min, max, skew, step, direction, onChange]);
717
+ const touchMoveRefCallback = useRefCallbackEvent('touchmove', handleValue, { passive: false }, [min, max, skew, step, direction, onChange, readonly]);
609
718
  const wheelRefCallback = useRefCallbackEvent('wheel', (event) => {
610
- if (!enableWheel)
719
+ if (!wheel || !onChange || readonly)
611
720
  return;
612
721
  event.preventDefault();
613
- let x = event.deltaY > 0 ? enableWheel[1] : -enableWheel[1];
722
+ let x = event.deltaY > 0 ? wheel[1] : -wheel[1];
614
723
  if (isReversed(direction) || isHorizontal(direction))
615
724
  x *= -1;
616
725
  let v;
617
- if (enableWheel[0] == 'normalized') {
726
+ if (wheel[0] == 'normalized') {
618
727
  const n = normalizeValue(value, min, max, skew);
619
728
  v = rawValue(n + x, min, max, skew);
620
729
  }
621
730
  else {
622
731
  v = value + x;
623
732
  }
624
- if (onChange)
625
- onChange(clamp(stepValue(v, step), min, max));
733
+ onChange(clamp(stepValue(v, step), min, max));
626
734
  }, {
627
735
  passive: false,
628
- }, [enableWheel, value, min, max, skew, step]);
736
+ }, [wheel, value, min, max, skew, step]);
629
737
  useEventListener(window, 'mousemove', (event) => {
630
738
  handleValue(event);
631
739
  });
632
740
  useEventListener(window, 'mouseup', () => {
633
741
  thumbDragged.current = false;
634
742
  if (bodyNoSelect)
635
- document.body.classList.remove('no-select');
743
+ removeNoSelect();
636
744
  });
745
+ React.useImperativeHandle(ref, () => {
746
+ return {
747
+ focus() {
748
+ var _a;
749
+ (_a = thumbRef.current) === null || _a === undefined ? undefined : _a.focus();
750
+ },
751
+ blur() {
752
+ var _a;
753
+ (_a = thumbRef.current) === null || _a === undefined ? undefined : _a.blur();
754
+ },
755
+ };
756
+ }, []);
637
757
  return (jsxRuntime.jsxs("div", { className: clsx('tremolo-slider', className), ref: (div) => {
638
758
  wheelRefCallback(div);
639
759
  touchMoveRefCallback(div);
640
- }, tabIndex: -1, role: "slider", "aria-valuenow": value, "aria-valuemax": max, "aria-valuemin": min, "aria-orientation": isHorizontal(direction) ? 'horizontal' : 'vertical', css: react.css(Object.assign({ display: 'inline-block', boxSizing: 'border-box', margin: '0.7rem', padding: 0, cursor: 'pointer' }, style)), onPointerDown: (event) => {
760
+ }, 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: react.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) => {
641
761
  thumbDragged.current = true;
642
762
  handleValue(event);
643
- }, children: [jsxRuntime.jsx(react.Global, { styles: {
763
+ }, onKeyDown: handleKeyDown, onFocus: (_c = thumbRef.current) === null || _c === undefined ? undefined : _c.focus, onBlur: (_d = thumbRef.current) === null || _d === undefined ? undefined : _d.blur, children: [jsxRuntime.jsx(react.Global, { styles: {
644
764
  '.no-select': {
645
765
  userSelect: 'none',
646
766
  },
647
767
  } }), jsxRuntime.jsxs("div", { css: react.css({
648
768
  display: 'flex',
649
769
  flexDirection: isHorizontal(direction) ? 'column' : 'row',
650
- }), children: [jsxRuntime.jsxs("div", { className: "tremolo-slider-track-wrapper", ref: trackElementRef, css: react.css({ position: 'relative' }), children: [jsxRuntime.jsx(SliderTrack, Object.assign({ __css: {
651
- background: `linear-gradient(to ${gradientDirection(direction)}, ${color} ${percent}%, ${bg} ${percent}%)`,
652
- borderRadius: styleHelper(thickness, '/', 2),
653
- borderStyle: 'solid',
654
- borderColor: '#ccc',
655
- borderWidth: 2,
656
- width: isHorizontal(direction) ? length : thickness,
657
- height: isHorizontal(direction) ? thickness : length,
658
- zIndex: 1,
659
- } }, trackProps)), jsxRuntime.jsx(SliderThumb, Object.assign({ color: color, __css: {
770
+ }), children: [jsxRuntime.jsx("div", { className: "tremolo-slider-track-wrapper", ref: trackElementRef, children: jsxRuntime.jsx(SliderTrack, Object.assign({ __direction: direction, __disabled: disabled, __percent: percent, __thumb: jsxRuntime.jsx(SliderThumb, Object.assign({ ref: thumbRef, __disabled: disabled, __css: {
660
771
  top: isHorizontal(direction) ? '50%' : `${percentRev}%`,
661
772
  left: isHorizontal(direction) ? `${percentRev}%` : '50%',
662
- } }, thumbProps))] }), scale && (jsxRuntime.jsx("div", { className: "tremolo-slider-scale-wrapper", css: react.css(Object.assign({ position: 'relative', width: isHorizontal(direction) ? length : undefined, height: isVertical(direction) ? length : undefined, marginLeft: isHorizontal(direction)
663
- ? 2
664
- : ((_b = scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.gap) !== null && _b !== void 0 ? _b : `calc((1.4rem - ${styleHelper(thickness)}) / 2)`), marginTop: isVertical(direction)
665
- ? 2
666
- : ((_c = scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.gap) !== null && _c !== void 0 ? _c : `calc((1.4rem - ${styleHelper(thickness)}) / 2)`) }, scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.style)), children: scalesList.map((item, index) => {
773
+ } }, thumbProps)) }, trackProps)) }), scale && (jsxRuntime.jsx("div", { className: "tremolo-slider-scale-wrapper", css: react.css(Object.assign({ display: 'block', position: 'relative', marginLeft: isVertical(direction)
774
+ ? ((_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)`)
775
+ : undefined, marginTop: isHorizontal(direction)
776
+ ? ((_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)`)
777
+ : undefined }, scaleOption === null || scaleOption === undefined ? undefined : scaleOption.style)), children: scalesList.map((item, index) => {
667
778
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
668
779
  return (jsxRuntime.jsxs("div", { css: react.css({
669
780
  display: 'flex',
@@ -671,37 +782,29 @@ function Slider(_a) {
671
782
  justifyContent: 'center',
672
783
  alignItems: 'center',
673
784
  position: 'absolute',
674
- left: isHorizontal(direction)
675
- ? `${isReversed(direction)
676
- ? 100 - normalizeValue(item.at, min, max, skew) * 100
677
- : normalizeValue(item.at, min, max, skew) * 100}%`
678
- : undefined,
679
- top: isVertical(direction)
680
- ? `${isReversed(direction)
681
- ? 100 - normalizeValue(item.at, min, max, skew) * 100
682
- : normalizeValue(item.at, min, max, skew) * 100}%`
683
- : undefined,
785
+ left: isHorizontal(direction) ? `${calcPercent(item.at)}%` : undefined,
786
+ top: isVertical(direction) ? `${calcPercent(item.at)}%` : undefined,
684
787
  translate: isHorizontal(direction) ? '-50% 0' : '0 -50%',
685
788
  zIndex: 10,
686
- }), children: [((_b = (_a = item.type) !== null && _a !== void 0 ? _a : scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.defaultType) !== null && _b !== void 0 ? _b : 'mark-number') !=
789
+ }), children: [((_b = (_a = item.type) !== null && _a !== undefined ? _a : scaleOption === null || scaleOption === undefined ? undefined : scaleOption.defaultType) !== null && _b !== undefined ? _b : 'mark-number') !=
687
790
  'number' && (jsxRuntime.jsx("div", { className: "tremolo-slider-scale-mark", css: react.css({
688
791
  width: isHorizontal(direction)
689
- ? ((_d = (_c = item.style) === null || _c === void 0 ? void 0 : _c.thickness) !== null && _d !== void 0 ? _d : 1)
690
- : ((_f = (_e = item.style) === null || _e === void 0 ? void 0 : _e.length) !== null && _f !== void 0 ? _f : '0.5rem'),
792
+ ? ((_d = (_c = item.style) === null || _c === undefined ? undefined : _c.thickness) !== null && _d !== undefined ? _d : 1)
793
+ : ((_f = (_e = item.style) === null || _e === undefined ? undefined : _e.length) !== null && _f !== undefined ? _f : '0.5rem'),
691
794
  height: isVertical(direction)
692
- ? ((_h = (_g = item.style) === null || _g === void 0 ? void 0 : _g.thickness) !== null && _h !== void 0 ? _h : 1)
693
- : ((_k = (_j = item.style) === null || _j === void 0 ? void 0 : _j.length) !== null && _k !== void 0 ? _k : '0.5rem'),
694
- backgroundColor: (_o = (_m = (_l = item.style) === null || _l === void 0 ? void 0 : _l.markColor) !== null && _m !== void 0 ? _m : scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.markColor) !== null && _o !== void 0 ? _o : '#222',
795
+ ? ((_h = (_g = item.style) === null || _g === undefined ? undefined : _g.thickness) !== null && _h !== undefined ? _h : 1)
796
+ : ((_k = (_j = item.style) === null || _j === undefined ? undefined : _j.length) !== null && _k !== undefined ? _k : '0.5rem'),
797
+ 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',
695
798
  marginBottom: isHorizontal(direction) ? 2 : undefined,
696
799
  marginRight: isVertical(direction) ? 2 : undefined,
697
- }) })), ((_q = (_p = item.type) !== null && _p !== void 0 ? _p : scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.defaultType) !== null && _q !== void 0 ? _q : 'mark-number') !=
800
+ }) })), ((_q = (_p = item.type) !== null && _p !== undefined ? _p : scaleOption === null || scaleOption === undefined ? undefined : scaleOption.defaultType) !== null && _q !== undefined ? _q : 'mark-number') !=
698
801
  'mark' && (jsxRuntime.jsx("div", { className: "tremolo-slider-scale-number", css: react.css({
699
- color: (_s = (_r = item.style) === null || _r === void 0 ? void 0 : _r.labelColor) !== null && _s !== void 0 ? _s : scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.labelColor,
700
- width: scaleOption === null || scaleOption === void 0 ? void 0 : scaleOption.labelWidth,
802
+ color: (_s = (_r = item.style) === null || _r === undefined ? undefined : _r.labelColor) !== null && _s !== undefined ? _s : scaleOption === null || scaleOption === undefined ? undefined : scaleOption.labelColor,
803
+ width: scaleOption === null || scaleOption === undefined ? undefined : scaleOption.labelWidth,
701
804
  textAlign: 'right',
702
- }), children: (_t = item.text) !== null && _t !== void 0 ? _t : item.at }))] }, index));
805
+ }), children: (_t = item.text) !== null && _t !== undefined ? _t : item.at }))] }, index));
703
806
  }) }))] })] }));
704
- }
807
+ });
705
808
 
706
809
  exports.AnimationCanvas = AnimationCanvas;
707
810
  exports.Knob = Knob;