@tremolo-ui/react 0.0.11 → 0.1.0-alpha.0
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.
- package/dist/cjs/index.js +711 -395
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/AnimationCanvas/index.d.ts +2 -2
- package/dist/cjs/types/components/AnimationCanvas/index.d.ts.map +1 -1
- package/dist/cjs/types/components/AnimationKnob/index.d.ts +10 -3
- package/dist/cjs/types/components/AnimationKnob/index.d.ts.map +1 -1
- package/dist/cjs/types/components/Knob/index.d.ts +25 -3
- package/dist/cjs/types/components/Knob/index.d.ts.map +1 -1
- package/dist/cjs/types/components/NumberInput/DecrementStepper.d.ts +2 -0
- package/dist/cjs/types/components/NumberInput/DecrementStepper.d.ts.map +1 -1
- package/dist/cjs/types/components/NumberInput/IncrementStepper.d.ts +2 -0
- package/dist/cjs/types/components/NumberInput/IncrementStepper.d.ts.map +1 -1
- package/dist/cjs/types/components/NumberInput/InternalInput.d.ts +18 -0
- package/dist/cjs/types/components/NumberInput/InternalInput.d.ts.map +1 -0
- package/dist/cjs/types/components/NumberInput/Stepper.d.ts +2 -0
- package/dist/cjs/types/components/NumberInput/Stepper.d.ts.map +1 -1
- package/dist/cjs/types/components/NumberInput/context.d.ts +3 -2
- package/dist/cjs/types/components/NumberInput/context.d.ts.map +1 -1
- package/dist/cjs/types/components/NumberInput/index.d.ts +12 -3
- package/dist/cjs/types/components/NumberInput/index.d.ts.map +1 -1
- package/dist/cjs/types/components/Piano/KeyLabel.d.ts +1 -3
- package/dist/cjs/types/components/Piano/KeyLabel.d.ts.map +1 -1
- package/dist/cjs/types/components/Piano/context.d.ts +24 -0
- package/dist/cjs/types/components/Piano/context.d.ts.map +1 -0
- package/dist/cjs/types/components/Piano/index.d.ts +15 -12
- package/dist/cjs/types/components/Piano/index.d.ts.map +1 -1
- package/dist/cjs/types/components/Piano/key.d.ts +14 -19
- package/dist/cjs/types/components/Piano/key.d.ts.map +1 -1
- package/dist/cjs/types/components/Piano/keyboardShortcuts.d.ts +2 -2
- package/dist/cjs/types/components/Slider/Scale.d.ts +1 -3
- package/dist/cjs/types/components/Slider/Scale.d.ts.map +1 -1
- package/dist/cjs/types/components/Slider/Thumb.d.ts +1 -0
- package/dist/cjs/types/components/Slider/Thumb.d.ts.map +1 -1
- package/dist/cjs/types/components/Slider/Track.d.ts +2 -2
- package/dist/cjs/types/components/Slider/Track.d.ts.map +1 -1
- package/dist/cjs/types/components/Slider/index.d.ts +5 -1
- package/dist/cjs/types/components/Slider/index.d.ts.map +1 -1
- package/dist/cjs/types/components/WheelObserver/index.d.ts +3 -3
- package/dist/cjs/types/components/WheelObserver/index.d.ts.map +1 -1
- package/dist/cjs/types/components/XYPad/Area.d.ts +3 -3
- package/dist/cjs/types/components/XYPad/Area.d.ts.map +1 -1
- package/dist/cjs/types/components/XYPad/Thumb.d.ts +1 -0
- package/dist/cjs/types/components/XYPad/Thumb.d.ts.map +1 -1
- package/dist/cjs/types/components/XYPad/index.d.ts +6 -1
- package/dist/cjs/types/components/XYPad/index.d.ts.map +1 -1
- package/dist/cjs/types/hooks/useDrag.d.ts.map +1 -1
- package/dist/cjs/types/hooks/useDragWithElement.d.ts +16 -0
- package/dist/cjs/types/hooks/useDragWithElement.d.ts.map +1 -0
- package/dist/cjs/types/hooks/useInterval.d.ts +2 -0
- package/dist/cjs/types/hooks/useInterval.d.ts.map +1 -0
- package/dist/cjs/types/hooks/useLongPress.d.ts +2 -0
- package/dist/cjs/types/hooks/useLongPress.d.ts.map +1 -0
- package/dist/cjs/types/index.d.ts +5 -0
- package/dist/cjs/types/index.d.ts.map +1 -1
- package/dist/esm/index.js +706 -396
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/AnimationCanvas/index.d.ts +2 -2
- package/dist/esm/types/components/AnimationCanvas/index.d.ts.map +1 -1
- package/dist/esm/types/components/AnimationKnob/index.d.ts +10 -3
- package/dist/esm/types/components/AnimationKnob/index.d.ts.map +1 -1
- package/dist/esm/types/components/Knob/index.d.ts +25 -3
- package/dist/esm/types/components/Knob/index.d.ts.map +1 -1
- package/dist/esm/types/components/NumberInput/DecrementStepper.d.ts +2 -0
- package/dist/esm/types/components/NumberInput/DecrementStepper.d.ts.map +1 -1
- package/dist/esm/types/components/NumberInput/IncrementStepper.d.ts +2 -0
- package/dist/esm/types/components/NumberInput/IncrementStepper.d.ts.map +1 -1
- package/dist/esm/types/components/NumberInput/InternalInput.d.ts +18 -0
- package/dist/esm/types/components/NumberInput/InternalInput.d.ts.map +1 -0
- package/dist/esm/types/components/NumberInput/Stepper.d.ts +2 -0
- package/dist/esm/types/components/NumberInput/Stepper.d.ts.map +1 -1
- package/dist/esm/types/components/NumberInput/context.d.ts +3 -2
- package/dist/esm/types/components/NumberInput/context.d.ts.map +1 -1
- package/dist/esm/types/components/NumberInput/index.d.ts +12 -3
- package/dist/esm/types/components/NumberInput/index.d.ts.map +1 -1
- package/dist/esm/types/components/Piano/KeyLabel.d.ts +1 -3
- package/dist/esm/types/components/Piano/KeyLabel.d.ts.map +1 -1
- package/dist/esm/types/components/Piano/context.d.ts +24 -0
- package/dist/esm/types/components/Piano/context.d.ts.map +1 -0
- package/dist/esm/types/components/Piano/index.d.ts +15 -12
- package/dist/esm/types/components/Piano/index.d.ts.map +1 -1
- package/dist/esm/types/components/Piano/key.d.ts +14 -19
- package/dist/esm/types/components/Piano/key.d.ts.map +1 -1
- package/dist/esm/types/components/Piano/keyboardShortcuts.d.ts +2 -2
- package/dist/esm/types/components/Slider/Scale.d.ts +1 -3
- package/dist/esm/types/components/Slider/Scale.d.ts.map +1 -1
- package/dist/esm/types/components/Slider/Thumb.d.ts +1 -0
- package/dist/esm/types/components/Slider/Thumb.d.ts.map +1 -1
- package/dist/esm/types/components/Slider/Track.d.ts +2 -2
- package/dist/esm/types/components/Slider/Track.d.ts.map +1 -1
- package/dist/esm/types/components/Slider/index.d.ts +5 -1
- package/dist/esm/types/components/Slider/index.d.ts.map +1 -1
- package/dist/esm/types/components/WheelObserver/index.d.ts +3 -3
- package/dist/esm/types/components/WheelObserver/index.d.ts.map +1 -1
- package/dist/esm/types/components/XYPad/Area.d.ts +3 -3
- package/dist/esm/types/components/XYPad/Area.d.ts.map +1 -1
- package/dist/esm/types/components/XYPad/Thumb.d.ts +1 -0
- package/dist/esm/types/components/XYPad/Thumb.d.ts.map +1 -1
- package/dist/esm/types/components/XYPad/index.d.ts +6 -1
- package/dist/esm/types/components/XYPad/index.d.ts.map +1 -1
- package/dist/esm/types/hooks/useDrag.d.ts.map +1 -1
- package/dist/esm/types/hooks/useDragWithElement.d.ts +16 -0
- package/dist/esm/types/hooks/useDragWithElement.d.ts.map +1 -0
- package/dist/esm/types/hooks/useInterval.d.ts +2 -0
- package/dist/esm/types/hooks/useInterval.d.ts.map +1 -0
- package/dist/esm/types/hooks/useLongPress.d.ts +2 -0
- package/dist/esm/types/hooks/useLongPress.d.ts.map +1 -0
- package/dist/esm/types/index.d.ts +5 -0
- package/dist/esm/types/index.d.ts.map +1 -1
- package/dist/styles/index.css +117 -33
- package/package.json +3 -2
- package/src/components/AnimationCanvas/index.tsx +14 -6
- package/src/components/AnimationKnob/index.tsx +216 -187
- package/src/components/Knob/index.tsx +244 -171
- package/src/components/NumberInput/DecrementStepper.tsx +9 -2
- package/src/components/NumberInput/IncrementStepper.tsx +9 -2
- package/src/components/NumberInput/InternalInput.tsx +219 -0
- package/src/components/NumberInput/Stepper.tsx +2 -0
- package/src/components/NumberInput/context.tsx +20 -8
- package/src/components/NumberInput/index.css +76 -22
- package/src/components/NumberInput/index.tsx +94 -162
- package/src/components/Piano/KeyLabel.tsx +9 -5
- package/src/components/Piano/context.tsx +108 -0
- package/src/components/Piano/index.css +24 -6
- package/src/components/Piano/index.tsx +192 -122
- package/src/components/Piano/key.tsx +215 -0
- package/src/components/Piano/keyboardShortcuts.ts +2 -2
- package/src/components/Slider/Scale.tsx +1 -4
- package/src/components/Slider/Thumb.tsx +4 -0
- package/src/components/Slider/Track.tsx +5 -2
- package/src/components/Slider/index.css +5 -2
- package/src/components/Slider/index.tsx +84 -71
- package/src/components/WheelObserver/index.tsx +4 -2
- package/src/components/XYPad/Area.tsx +7 -6
- package/src/components/XYPad/Thumb.tsx +6 -0
- package/src/components/XYPad/index.css +12 -2
- package/src/components/XYPad/index.tsx +116 -90
- package/src/hooks/useDrag.ts +6 -7
- package/src/hooks/useDragWithElement.ts +76 -0
- package/src/hooks/useInterval.ts +27 -0
- package/src/hooks/useLongPress.ts +31 -0
- package/src/index.ts +6 -0
- package/src/styles/index.css +0 -1
- package/dist/cjs/types/components/Piano/BlackKey.d.ts +0 -5
- package/dist/cjs/types/components/Piano/BlackKey.d.ts.map +0 -1
- package/dist/cjs/types/components/Piano/WhiteKey.d.ts +0 -7
- package/dist/cjs/types/components/Piano/WhiteKey.d.ts.map +0 -1
- package/dist/esm/types/components/Piano/BlackKey.d.ts +0 -5
- package/dist/esm/types/components/Piano/BlackKey.d.ts.map +0 -1
- package/dist/esm/types/components/Piano/WhiteKey.d.ts +0 -7
- package/dist/esm/types/components/Piano/WhiteKey.d.ts.map +0 -1
- package/src/components/Piano/BlackKey.tsx +0 -118
- package/src/components/Piano/WhiteKey.tsx +0 -120
- package/src/components/Piano/key.ts +0 -46
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import React, { useRef, useCallback, useEffect, useInsertionEffect, useMemo,
|
|
2
|
+
import React, { useRef, useCallback, useEffect, useInsertionEffect, useMemo, forwardRef, useImperativeHandle, createContext, useContext, useState, createRef } from 'react';
|
|
3
3
|
|
|
4
4
|
/******************************************************************************
|
|
5
5
|
Copyright (c) Microsoft Corporation.
|
|
@@ -67,7 +67,7 @@ function setDprConfig(canvas, context, width, height, dpr) {
|
|
|
67
67
|
canvas.style.height = `${height}px`;
|
|
68
68
|
}
|
|
69
69
|
function AnimationCanvas(_a) {
|
|
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"]);
|
|
70
|
+
var { width = 100, height = 100, relativeSize = false, reduceFlickering = true, options, init, draw, className, onContextMenu = (event) => event.preventDefault() } = _a, props = __rest(_a, ["width", "height", "relativeSize", "reduceFlickering", "options", "init", "draw", "className", "onContextMenu"]);
|
|
71
71
|
const canvasRef = useRef(null);
|
|
72
72
|
const memoCanvasRef = useRef(null);
|
|
73
73
|
const reqIdRef = useRef(-1);
|
|
@@ -116,7 +116,11 @@ function AnimationCanvas(_a) {
|
|
|
116
116
|
for (const prop of drawingState) {
|
|
117
117
|
context[prop] = contextMemo[prop];
|
|
118
118
|
}
|
|
119
|
-
if (reduceFlickering &&
|
|
119
|
+
if (reduceFlickering &&
|
|
120
|
+
memoContext &&
|
|
121
|
+
memoCanvas &&
|
|
122
|
+
memoCanvas.width > 0 &&
|
|
123
|
+
memoCanvas.height > 0) {
|
|
120
124
|
context.drawImage(memoContext.canvas, 0, 0);
|
|
121
125
|
}
|
|
122
126
|
}
|
|
@@ -148,7 +152,7 @@ function AnimationCanvas(_a) {
|
|
|
148
152
|
};
|
|
149
153
|
}
|
|
150
154
|
}, [loop, init, options, reduceFlickering, relativeSize]);
|
|
151
|
-
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 }))] }));
|
|
155
|
+
return (jsxs(Fragment, { children: [jsx("canvas", Object.assign({ className: clsx('tremolo-animation-canvas', className), width: relativeSize ? 0 : width, height: relativeSize ? 0 : height, ref: canvasRef, onContextMenu: onContextMenu }, props)), relativeSize && reduceFlickering && (jsx("canvas", { style: { display: 'none' }, ref: memoCanvasRef }))] }));
|
|
152
156
|
}
|
|
153
157
|
|
|
154
158
|
/**
|
|
@@ -256,7 +260,7 @@ function xor(a = false, b = false) {
|
|
|
256
260
|
/**
|
|
257
261
|
* @category MIDI
|
|
258
262
|
*/
|
|
259
|
-
const
|
|
263
|
+
const noteKeys = [
|
|
260
264
|
'C',
|
|
261
265
|
'C#',
|
|
262
266
|
'D',
|
|
@@ -290,18 +294,16 @@ function parseNoteName(noteName) {
|
|
|
290
294
|
*/
|
|
291
295
|
function noteNumber(noteName) {
|
|
292
296
|
const { letter, accidental, octave } = parseNoteName(noteName);
|
|
293
|
-
const noteIndex =
|
|
297
|
+
const noteIndex = noteKeys.indexOf(letter.toLocaleUpperCase());
|
|
294
298
|
const accidentalValue = (accidental[0] == 'b' ? -1 : 1) * accidental.length;
|
|
295
299
|
return noteIndex + 12 * (Number(octave) + 1) + accidentalValue;
|
|
296
300
|
}
|
|
297
301
|
/**
|
|
298
|
-
* Convert noteNumber to
|
|
302
|
+
* Convert noteNumber to noteKey
|
|
299
303
|
* @category MIDI
|
|
300
304
|
*/
|
|
301
|
-
function
|
|
302
|
-
|
|
303
|
-
const octave = Math.floor(noteNumber / 12) - 1;
|
|
304
|
-
return noteNames[noteIndex] + octave;
|
|
305
|
+
function noteKey(noteNumber) {
|
|
306
|
+
return noteKeys[mod(noteNumber, 12)];
|
|
305
307
|
}
|
|
306
308
|
/**
|
|
307
309
|
* @param note noteNumber: 0 ~ 127 or noteName e.g. 'C3'
|
|
@@ -317,6 +319,13 @@ function isWhiteKey(note) {
|
|
|
317
319
|
mod(n, 12) == 9 ||
|
|
318
320
|
mod(n, 12) == 11);
|
|
319
321
|
}
|
|
322
|
+
/**
|
|
323
|
+
* @param note noteNumber: 0 ~ 127 or noteName e.g. 'C3'
|
|
324
|
+
* @category MIDI
|
|
325
|
+
*/
|
|
326
|
+
function isBlackKey(note) {
|
|
327
|
+
return !isWhiteKey(note);
|
|
328
|
+
}
|
|
320
329
|
|
|
321
330
|
/**
|
|
322
331
|
* This hook is user-land implementation of the experimental `useEffectEvent` hook.
|
|
@@ -397,8 +406,7 @@ function useDrag({ threshold = 1, onDrag, onDragStart, onDragEnd, }) {
|
|
|
397
406
|
}
|
|
398
407
|
if (Math.abs(deltaX) < threshold && Math.abs(deltaY) < threshold)
|
|
399
408
|
return;
|
|
400
|
-
|
|
401
|
-
onDrag(screenX - dragStartX.current, screenY - dragStartY.current, deltaX, deltaY);
|
|
409
|
+
onDrag(screenX - dragStartX.current, screenY - dragStartY.current, deltaX, deltaY);
|
|
402
410
|
}, [threshold, onDrag]);
|
|
403
411
|
const refHandler = useRefCallbackEvent('touchmove', handleDrag, { passive: false }, [onDrag]);
|
|
404
412
|
const pointerDownHandler = useCallback((event) => {
|
|
@@ -424,13 +432,14 @@ function removeNoSelect() {
|
|
|
424
432
|
}
|
|
425
433
|
|
|
426
434
|
/**
|
|
427
|
-
*
|
|
435
|
+
* Animatable rotary knob.
|
|
428
436
|
* @category AnimationKnob
|
|
429
437
|
*/
|
|
430
|
-
|
|
438
|
+
const AnimationKnob = forwardRef((_a, ref) => {
|
|
431
439
|
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"]);
|
|
432
440
|
// -- state and ref ---
|
|
433
441
|
const valueRef = useRef(0);
|
|
442
|
+
const elmRef = useRef(null);
|
|
434
443
|
// --- interpret props ---
|
|
435
444
|
const isRelativeSize = typeof (size !== null && size !== void 0 ? size : width) == 'string' || typeof height == 'string';
|
|
436
445
|
const onDrag = useCallback((_x, y) => {
|
|
@@ -487,14 +496,27 @@ function AnimationKnob(_a) {
|
|
|
487
496
|
if (onChange)
|
|
488
497
|
onChange(clamp(stepValue(v, step), min, max));
|
|
489
498
|
}, { passive: false }, [wheel, value, min, max, skew, step, onChange]);
|
|
499
|
+
useImperativeHandle(ref, () => {
|
|
500
|
+
return {
|
|
501
|
+
focus() {
|
|
502
|
+
var _a;
|
|
503
|
+
(_a = elmRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
504
|
+
},
|
|
505
|
+
blur() {
|
|
506
|
+
var _a;
|
|
507
|
+
(_a = elmRef.current) === null || _a === void 0 ? void 0 : _a.blur();
|
|
508
|
+
},
|
|
509
|
+
};
|
|
510
|
+
}, []);
|
|
490
511
|
return (jsx("div", Object.assign({ className: clsx('tremolo-animation-knob', className), ref: (div) => {
|
|
512
|
+
elmRef.current = div;
|
|
491
513
|
wheelRefCallback(div);
|
|
492
514
|
touchMoveRefCallback(div);
|
|
493
515
|
}, role: "slider", tabIndex: 0, "aria-valuenow": value, "aria-valuemax": max, "aria-valuemin": min, style: Object.assign({ display: 'inline-block', width: size !== null && size !== void 0 ? size : width, height: size !== null && size !== void 0 ? size : height, cursor: 'pointer', WebkitTapHighlightColor: 'transparent' }, style), onPointerDown: pointerDownHandler, onDoubleClick: () => {
|
|
494
516
|
if (enableDoubleClickDefault && onChange) {
|
|
495
517
|
onChange(defaultValue);
|
|
496
518
|
}
|
|
497
|
-
}, onKeyDown: handleKeyDown
|
|
519
|
+
}, onKeyDown: handleKeyDown }, props, { children: jsx(AnimationCanvas, Object.assign({}, (isRelativeSize
|
|
498
520
|
? { relativeSize: true }
|
|
499
521
|
: {
|
|
500
522
|
width: size !== null && size !== void 0 ? size : width,
|
|
@@ -541,7 +563,7 @@ function AnimationKnob(_a) {
|
|
|
541
563
|
ctx.lineWidth = thumbWeight;
|
|
542
564
|
ctx.stroke();
|
|
543
565
|
} })) })));
|
|
544
|
-
}
|
|
566
|
+
});
|
|
545
567
|
|
|
546
568
|
/** @category DragObserver */
|
|
547
569
|
function DragObserver(props) {
|
|
@@ -555,15 +577,20 @@ function DragObserver(props) {
|
|
|
555
577
|
return (jsx(Component, Object.assign({ ref: refCallback, onPointerDown: pointerDownHandler }, attributes, { children: children })));
|
|
556
578
|
}
|
|
557
579
|
|
|
558
|
-
/**
|
|
559
|
-
|
|
560
|
-
|
|
580
|
+
/**
|
|
581
|
+
* Interactive rotary knob component implemented in SVG.
|
|
582
|
+
*
|
|
583
|
+
* @category Knob
|
|
584
|
+
*/
|
|
585
|
+
const Knob = forwardRef((_a, ref) => {
|
|
586
|
+
var { value, min, max, step = 1, skew = 1, defaultValue = min, startValue = min, size = 50, bodyNoSelect = true, wheel = ['raw', 1], keyboard = ['raw', 1], enableDoubleClickDefault = true, disabled = false, readonly = false, activeLine, inactiveLine, thumb, thumbLine, onChange, onKeyDown, onPointerDown, onDoubleClick, className, classes } = _a, props = __rest(_a, ["value", "min", "max", "step", "skew", "defaultValue", "startValue", "size", "bodyNoSelect", "wheel", "keyboard", "enableDoubleClickDefault", "disabled", "readonly", "activeLine", "inactiveLine", "thumb", "thumbLine", "onChange", "onKeyDown", "onPointerDown", "onDoubleClick", "className", "classes"]);
|
|
561
587
|
const valueRef = useRef(0);
|
|
588
|
+
const elmRef = useRef(null);
|
|
562
589
|
// --- interpret props ---
|
|
563
590
|
const padding = 8; // %
|
|
564
591
|
const thumbLineWeight = 6; // %
|
|
565
|
-
const
|
|
566
|
-
const lineWeight =
|
|
592
|
+
const thumbLineLength = 35; // %
|
|
593
|
+
const lineWeight = size * 0.06; // px
|
|
567
594
|
const p = normalizeValue(value, min, max, skew);
|
|
568
595
|
const s = normalizeValue(startValue, min, max, skew);
|
|
569
596
|
// --- internal functions ---
|
|
@@ -574,6 +601,17 @@ function Knob(_a) {
|
|
|
574
601
|
const v2 = clamp(stepValue(v, step), min, max);
|
|
575
602
|
onChange(v2);
|
|
576
603
|
}, [max, min, onChange, readonly, skew, step]);
|
|
604
|
+
const updateValueByEvent = useCallback((eventType, x) => {
|
|
605
|
+
let newValue;
|
|
606
|
+
if (eventType == 'normalized') {
|
|
607
|
+
const n = normalizeValue(value, min, max, skew);
|
|
608
|
+
newValue = rawValue(n + x, min, max, skew);
|
|
609
|
+
}
|
|
610
|
+
else {
|
|
611
|
+
newValue = value + x;
|
|
612
|
+
}
|
|
613
|
+
return clamp(stepValue(newValue, step), min, max);
|
|
614
|
+
}, [max, min, skew, step, value]);
|
|
577
615
|
const handleKeyDown = useCallback((event) => {
|
|
578
616
|
if (!keyboard || !onChange || readonly)
|
|
579
617
|
return;
|
|
@@ -581,17 +619,9 @@ function Knob(_a) {
|
|
|
581
619
|
if (['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown'].includes(key)) {
|
|
582
620
|
event.preventDefault();
|
|
583
621
|
const x = key == 'ArrowRight' || key == 'ArrowUp' ? keyboard[1] : -keyboard[1];
|
|
584
|
-
|
|
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));
|
|
622
|
+
onChange(updateValueByEvent(keyboard[0], x));
|
|
593
623
|
}
|
|
594
|
-
}, [
|
|
624
|
+
}, [keyboard, onChange, readonly, updateValueByEvent]);
|
|
595
625
|
// --- hooks ---
|
|
596
626
|
const [touchMoveRefCallback, pointerDownHandler] = useDrag({
|
|
597
627
|
onDrag: onDrag,
|
|
@@ -606,21 +636,26 @@ function Knob(_a) {
|
|
|
606
636
|
},
|
|
607
637
|
});
|
|
608
638
|
const wheelRefCallback = useRefCallbackEvent('wheel', (event) => {
|
|
609
|
-
if (!wheel)
|
|
639
|
+
if (!wheel || readonly)
|
|
610
640
|
return;
|
|
611
641
|
event.preventDefault();
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
642
|
+
if (!onChange || event.deltaY == 0)
|
|
643
|
+
return;
|
|
644
|
+
const x = Math.sign(event.deltaY) * -wheel[1];
|
|
645
|
+
onChange(updateValueByEvent(wheel[0], x));
|
|
646
|
+
}, { passive: false }, [wheel, onChange, readonly, updateValueByEvent]);
|
|
647
|
+
useImperativeHandle(ref, () => {
|
|
648
|
+
return {
|
|
649
|
+
focus() {
|
|
650
|
+
var _a;
|
|
651
|
+
(_a = elmRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
652
|
+
},
|
|
653
|
+
blur() {
|
|
654
|
+
var _a;
|
|
655
|
+
(_a = elmRef.current) === null || _a === void 0 ? void 0 : _a.blur();
|
|
656
|
+
},
|
|
657
|
+
};
|
|
658
|
+
}, []);
|
|
624
659
|
const center = size / 2;
|
|
625
660
|
const r1 = -135;
|
|
626
661
|
const r2 = -135 + Math.min(p, s) * 270;
|
|
@@ -635,13 +670,62 @@ function Knob(_a) {
|
|
|
635
670
|
const x4 = center + center * Math.cos(radian(r4 - 90));
|
|
636
671
|
const y4 = center + center * Math.sin(radian(r4 - 90));
|
|
637
672
|
return (jsxs("svg", Object.assign({ className: clsx('tremolo-knob', className), ref: (div) => {
|
|
673
|
+
elmRef.current = div;
|
|
638
674
|
wheelRefCallback(div);
|
|
639
675
|
touchMoveRefCallback(div);
|
|
640
|
-
}, width: size, height: size, tabIndex: 0, "aria-valuenow": value, "aria-valuemin": min, "aria-valuemax": max, "aria-disabled": disabled, "aria-readonly": readonly, onPointerDown:
|
|
676
|
+
}, width: size, height: size, tabIndex: 0, role: "slider", "aria-valuenow": value, "aria-valuemin": min, "aria-valuemax": max, "aria-disabled": disabled, "aria-readonly": readonly, onPointerDown: (event) => {
|
|
677
|
+
pointerDownHandler(event);
|
|
678
|
+
onPointerDown === null || onPointerDown === void 0 ? void 0 : onPointerDown(event);
|
|
679
|
+
}, onDoubleClick: (event) => {
|
|
641
680
|
if (enableDoubleClickDefault && onChange) {
|
|
642
681
|
onChange(defaultValue);
|
|
643
682
|
}
|
|
644
|
-
|
|
683
|
+
onDoubleClick === null || onDoubleClick === void 0 ? void 0 : onDoubleClick(event);
|
|
684
|
+
}, onKeyDown: (event) => {
|
|
685
|
+
handleKeyDown(event);
|
|
686
|
+
onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event);
|
|
687
|
+
} }, 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: `${thumbLineLength}%`, stroke: thumbLine || 'currentColor', strokeWidth: `${thumbLineWeight}%`,
|
|
688
|
+
// NOTE
|
|
689
|
+
// https://bugs.webkit.org/show_bug.cgi?id=201854
|
|
690
|
+
// https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/transform-origin#browser_compatibility
|
|
691
|
+
style: {
|
|
692
|
+
transform: `rotate(${ -135 + p * 270}deg)`,
|
|
693
|
+
transformOrigin: '50% 50%',
|
|
694
|
+
} })] })] })));
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
const createStoreImpl = (createState) => {
|
|
698
|
+
let state;
|
|
699
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
700
|
+
const setState = (partial, replace) => {
|
|
701
|
+
const nextState = typeof partial === "function" ? partial(state) : partial;
|
|
702
|
+
if (!Object.is(nextState, state)) {
|
|
703
|
+
const previousState = state;
|
|
704
|
+
state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState);
|
|
705
|
+
listeners.forEach((listener) => listener(state, previousState));
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
const getState = () => state;
|
|
709
|
+
const getInitialState = () => initialState;
|
|
710
|
+
const subscribe = (listener) => {
|
|
711
|
+
listeners.add(listener);
|
|
712
|
+
return () => listeners.delete(listener);
|
|
713
|
+
};
|
|
714
|
+
const api = { setState, getState, getInitialState, subscribe };
|
|
715
|
+
const initialState = state = createState(setState, getState, api);
|
|
716
|
+
return api;
|
|
717
|
+
};
|
|
718
|
+
const createStore = (createState) => createState ? createStoreImpl(createState) : createStoreImpl;
|
|
719
|
+
|
|
720
|
+
const identity = (arg) => arg;
|
|
721
|
+
function useStore(api, selector = identity) {
|
|
722
|
+
const slice = React.useSyncExternalStore(
|
|
723
|
+
api.subscribe,
|
|
724
|
+
() => selector(api.getState()),
|
|
725
|
+
() => selector(api.getInitialState())
|
|
726
|
+
);
|
|
727
|
+
React.useDebugValue(slice);
|
|
728
|
+
return slice;
|
|
645
729
|
}
|
|
646
730
|
|
|
647
731
|
/**
|
|
@@ -690,40 +774,6 @@ function parseValue(inputString, units, digit) {
|
|
|
690
774
|
};
|
|
691
775
|
}
|
|
692
776
|
|
|
693
|
-
const createStoreImpl = (createState) => {
|
|
694
|
-
let state;
|
|
695
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
696
|
-
const setState = (partial, replace) => {
|
|
697
|
-
const nextState = typeof partial === "function" ? partial(state) : partial;
|
|
698
|
-
if (!Object.is(nextState, state)) {
|
|
699
|
-
const previousState = state;
|
|
700
|
-
state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState);
|
|
701
|
-
listeners.forEach((listener) => listener(state, previousState));
|
|
702
|
-
}
|
|
703
|
-
};
|
|
704
|
-
const getState = () => state;
|
|
705
|
-
const getInitialState = () => initialState;
|
|
706
|
-
const subscribe = (listener) => {
|
|
707
|
-
listeners.add(listener);
|
|
708
|
-
return () => listeners.delete(listener);
|
|
709
|
-
};
|
|
710
|
-
const api = { setState, getState, getInitialState, subscribe };
|
|
711
|
-
const initialState = state = createState(setState, getState, api);
|
|
712
|
-
return api;
|
|
713
|
-
};
|
|
714
|
-
const createStore = (createState) => createState ? createStoreImpl(createState) : createStoreImpl;
|
|
715
|
-
|
|
716
|
-
const identity = (arg) => arg;
|
|
717
|
-
function useStore(api, selector = identity) {
|
|
718
|
-
const slice = React.useSyncExternalStore(
|
|
719
|
-
api.subscribe,
|
|
720
|
-
() => selector(api.getState()),
|
|
721
|
-
() => selector(api.getInitialState())
|
|
722
|
-
);
|
|
723
|
-
React.useDebugValue(slice);
|
|
724
|
-
return slice;
|
|
725
|
-
}
|
|
726
|
-
|
|
727
777
|
function safeClamp(value, min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER) {
|
|
728
778
|
return clamp(value, min, max);
|
|
729
779
|
}
|
|
@@ -734,6 +784,7 @@ const createNumberInputStore = (initProps) => {
|
|
|
734
784
|
min: 0,
|
|
735
785
|
max: 0,
|
|
736
786
|
step: 1,
|
|
787
|
+
readonly: false,
|
|
737
788
|
keepWithinRange: true,
|
|
738
789
|
onChange: (_v, _t) => { },
|
|
739
790
|
};
|
|
@@ -742,8 +793,10 @@ const createNumberInputStore = (initProps) => {
|
|
|
742
793
|
valueAsNumber: parseValue(String((initProps === null || initProps === void 0 ? void 0 : initProps.value) || ''), initProps === null || initProps === void 0 ? void 0 : initProps.units, initProps === null || initProps === void 0 ? void 0 : initProps.digit).rawValue,
|
|
743
794
|
}), { increment: () => set((state) => {
|
|
744
795
|
var _a, _b;
|
|
745
|
-
|
|
746
|
-
|
|
796
|
+
if (state.readonly)
|
|
797
|
+
return {};
|
|
798
|
+
const current = parseValue(state.value, state.units, state.digit).rawValue;
|
|
799
|
+
let next = current + ((_a = state.step) !== null && _a !== void 0 ? _a : 1);
|
|
747
800
|
if (state.keepWithinRange) {
|
|
748
801
|
next = safeClamp(next, state.min, state.max);
|
|
749
802
|
}
|
|
@@ -755,8 +808,10 @@ const createNumberInputStore = (initProps) => {
|
|
|
755
808
|
};
|
|
756
809
|
}), decrement: () => set((state) => {
|
|
757
810
|
var _a, _b;
|
|
758
|
-
|
|
759
|
-
|
|
811
|
+
if (state.readonly)
|
|
812
|
+
return {};
|
|
813
|
+
const current = parseValue(state.value, state.units, state.digit).rawValue;
|
|
814
|
+
let next = current - ((_a = state.step) !== null && _a !== void 0 ? _a : 1);
|
|
760
815
|
if (state.keepWithinRange) {
|
|
761
816
|
next = safeClamp(next, state.min, state.max);
|
|
762
817
|
}
|
|
@@ -767,6 +822,8 @@ const createNumberInputStore = (initProps) => {
|
|
|
767
822
|
valueAsNumber: next,
|
|
768
823
|
};
|
|
769
824
|
}), change: (value) => set((state) => {
|
|
825
|
+
if (state.readonly)
|
|
826
|
+
return {};
|
|
770
827
|
const v = value !== null && value !== void 0 ? value : '';
|
|
771
828
|
return {
|
|
772
829
|
value: v,
|
|
@@ -803,36 +860,17 @@ function useNumberInputContext(selector) {
|
|
|
803
860
|
return useStore(store, selector);
|
|
804
861
|
}
|
|
805
862
|
|
|
806
|
-
|
|
807
|
-
var {
|
|
808
|
-
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
function IncrementStepper(_a) {
|
|
812
|
-
var { size = 12, children, className } = _a, props = __rest(_a, ["size", "children", "className"]);
|
|
813
|
-
const max = useNumberInputContext((s) => s.max);
|
|
814
|
-
const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
|
|
815
|
-
const keepWithinRange = useNumberInputContext((s) => s.keepWithinRange);
|
|
816
|
-
const increment = useNumberInputContext((s) => s.increment);
|
|
817
|
-
// TODO: add long press event
|
|
818
|
-
return (jsx("div", Object.assign({ className: clsx('tremolo-number-input-increment-stepper', className), role: "button", tabIndex: -1, "aria-disabled": keepWithinRange && valueAsNumber >= (max !== null && max !== void 0 ? max : Number.MAX_SAFE_INTEGER), onPointerDown: increment }, props, { children: children || (jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("polyline", { points: "18 15 12 9 6 15" }) })) })));
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
function DecrementStepper(_a) {
|
|
822
|
-
var { size = 12, children, className } = _a, props = __rest(_a, ["size", "children", "className"]);
|
|
823
|
-
const min = useNumberInputContext((s) => s.min);
|
|
824
|
-
const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
|
|
825
|
-
const keepWithinRange = useNumberInputContext((s) => s.keepWithinRange);
|
|
826
|
-
const decrement = useNumberInputContext((s) => s.decrement);
|
|
827
|
-
// TODO: add long press event
|
|
828
|
-
return (jsx("div", Object.assign({ className: clsx('tremolo-number-input-decrement-stepper', className), role: "button", tabIndex: -1, "aria-disabled": keepWithinRange && valueAsNumber <= (min !== null && min !== void 0 ? min : Number.MIN_SAFE_INTEGER), onPointerDown: decrement }, props, { children: children || (jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("polyline", { points: "6 9 12 15 18 9" }) })) })));
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
function InternalInput(_a) {
|
|
832
|
-
var { min, max, step, defaultValue, units, disabled, digit, selectWithFocus, blurOnEnter, keepWithinRange, clampValueOnBlur, wheel, keyboard, wrapperClassName, className, onChange, onFocus, onBlur, children: _children } = _a, props = __rest(_a, ["min", "max", "step", "defaultValue", "units", "disabled", "digit", "selectWithFocus", "blurOnEnter", "keepWithinRange", "clampValueOnBlur", "wheel", "keyboard", "wrapperClassName", "className", "onChange", "onFocus", "onBlur", "children"]);
|
|
863
|
+
const InternalInput = forwardRef((_a, ref) => {
|
|
864
|
+
var { disabled, readonly, selectWithFocus, blurOnEnter, clampValueOnBlur, wheel, keyboard, className, onChange, onFocus, onBlur, onKeyDown, children: _children } = _a, props = __rest(_a, ["disabled", "readonly", "selectWithFocus", "blurOnEnter", "clampValueOnBlur", "wheel", "keyboard", "className", "onChange", "onFocus", "onBlur", "onKeyDown", "children"]);
|
|
865
|
+
const elmRef = useRef(null);
|
|
833
866
|
const [editing, setEditing] = useState(false);
|
|
834
867
|
const value = useNumberInputContext((s) => s.value);
|
|
835
868
|
const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
|
|
869
|
+
const min = useNumberInputContext((s) => s.min);
|
|
870
|
+
const max = useNumberInputContext((s) => s.max);
|
|
871
|
+
const step = useNumberInputContext((s) => s.step);
|
|
872
|
+
const units = useNumberInputContext((s) => s.units);
|
|
873
|
+
const digit = useNumberInputContext((s) => s.digit);
|
|
836
874
|
const change = useNumberInputContext((s) => s.change);
|
|
837
875
|
const error = useMemo(() => {
|
|
838
876
|
if (editing)
|
|
@@ -840,11 +878,71 @@ function InternalInput(_a) {
|
|
|
840
878
|
const v = valueAsNumber;
|
|
841
879
|
return (min !== null && min !== void 0 ? min : -Infinity) > v || v > (max !== null && max !== void 0 ? max : Infinity);
|
|
842
880
|
}, [editing, max, min, valueAsNumber]);
|
|
843
|
-
|
|
881
|
+
const updateValueByEvent = useCallback((eventType, x) => {
|
|
882
|
+
let newValue;
|
|
883
|
+
if (eventType == 'normalized') {
|
|
884
|
+
if (!min || !max) {
|
|
885
|
+
throw new Error('required parameter: min, max'); // TODO
|
|
886
|
+
}
|
|
887
|
+
const n = normalizeValue(valueAsNumber, min, max);
|
|
888
|
+
newValue = rawValue(n + x, min, max);
|
|
889
|
+
return clamp(stepValue(newValue, step), min, max);
|
|
890
|
+
}
|
|
891
|
+
else {
|
|
892
|
+
newValue = valueAsNumber + x;
|
|
893
|
+
return stepValue(newValue, step);
|
|
894
|
+
}
|
|
895
|
+
}, [max, min, step, valueAsNumber]);
|
|
896
|
+
const handleKeyDown = useCallback((event) => {
|
|
897
|
+
if (!keyboard || !onChange || readonly)
|
|
898
|
+
return;
|
|
899
|
+
const key = event.key;
|
|
900
|
+
if (!['ArrowUp', 'ArrowDown'].includes(key))
|
|
901
|
+
return;
|
|
902
|
+
event.preventDefault();
|
|
903
|
+
const x = key == 'ArrowUp' ? keyboard[1] : -keyboard[1];
|
|
904
|
+
const parsed = parseValue(String(updateValueByEvent(keyboard[0], x)));
|
|
905
|
+
console.log('key control');
|
|
906
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(parsed.rawValue, parsed.formatValue);
|
|
907
|
+
}, [keyboard, readonly, onChange, updateValueByEvent]);
|
|
908
|
+
const wheelRefCallback = useRefCallbackEvent('wheel', (event) => {
|
|
909
|
+
if (!wheel || readonly)
|
|
910
|
+
return;
|
|
911
|
+
event.preventDefault();
|
|
912
|
+
if (!onChange || event.deltaY == 0)
|
|
913
|
+
return;
|
|
914
|
+
const x = Math.sign(event.deltaY) * -wheel[1];
|
|
915
|
+
const parsed = parseValue(String(updateValueByEvent(wheel[0], x)));
|
|
916
|
+
console.log('wheel control');
|
|
917
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(parsed.rawValue, parsed.formatValue);
|
|
918
|
+
}, { passive: false }, [wheel, readonly, onChange, updateValueByEvent]);
|
|
919
|
+
useImperativeHandle(ref, () => {
|
|
920
|
+
return {
|
|
921
|
+
focus() {
|
|
922
|
+
var _a;
|
|
923
|
+
(_a = elmRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
924
|
+
},
|
|
925
|
+
blur() {
|
|
926
|
+
var _a;
|
|
927
|
+
(_a = elmRef.current) === null || _a === void 0 ? void 0 : _a.blur();
|
|
928
|
+
},
|
|
929
|
+
};
|
|
930
|
+
}, []);
|
|
931
|
+
return (jsx("input", Object.assign({ ref: (input) => {
|
|
932
|
+
elmRef.current = input;
|
|
933
|
+
wheelRefCallback(input);
|
|
934
|
+
}, className: clsx('tremolo-number-input', className), value: value, min: min, max: max, step: step, type: "text", "aria-disabled": disabled, "aria-readonly": readonly, tabIndex: -1, "data-error": error, onChange: (event) => {
|
|
935
|
+
if (readonly)
|
|
936
|
+
return;
|
|
844
937
|
const v = event.currentTarget.value;
|
|
938
|
+
const cursorPos = event.target.selectionStart;
|
|
845
939
|
change(v);
|
|
846
940
|
const valueAsNumber = parseValue(v, units, digit).rawValue;
|
|
847
941
|
onChange === null || onChange === void 0 ? void 0 : onChange(valueAsNumber, v);
|
|
942
|
+
if (cursorPos) {
|
|
943
|
+
event.target.selectionStart = cursorPos;
|
|
944
|
+
event.target.selectionEnd = cursorPos;
|
|
945
|
+
}
|
|
848
946
|
}, onFocus: (event) => {
|
|
849
947
|
setEditing(true);
|
|
850
948
|
const v = event.currentTarget.value;
|
|
@@ -873,53 +971,190 @@ function InternalInput(_a) {
|
|
|
873
971
|
if (blurOnEnter && event.key == 'Enter') {
|
|
874
972
|
event.currentTarget.blur();
|
|
875
973
|
}
|
|
974
|
+
handleKeyDown(event);
|
|
975
|
+
onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event);
|
|
876
976
|
} }, props)));
|
|
977
|
+
});
|
|
978
|
+
|
|
979
|
+
/** @category NumberInput */
|
|
980
|
+
function Stepper(_a) {
|
|
981
|
+
var { dynamic = true, children, className } = _a, props = __rest(_a, ["dynamic", "children", "className"]);
|
|
982
|
+
return (jsx("div", Object.assign({ className: clsx('tremolo-number-input-stepper', className), "data-dynamic": dynamic }, props, { children: children })));
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
function useInterval(callback, delay) {
|
|
986
|
+
const savedCallback = useRef(callback);
|
|
987
|
+
// Remember the latest callback if it changes.
|
|
988
|
+
useEffect(() => {
|
|
989
|
+
savedCallback.current = callback;
|
|
990
|
+
}, [callback]);
|
|
991
|
+
// Set up the interval.
|
|
992
|
+
useEffect(() => {
|
|
993
|
+
// Don't schedule if no delay is specified.
|
|
994
|
+
// Note: 0 is a valid value for delay.
|
|
995
|
+
if (delay === null) {
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
const id = setInterval(() => {
|
|
999
|
+
savedCallback.current();
|
|
1000
|
+
}, delay);
|
|
1001
|
+
return () => {
|
|
1002
|
+
clearInterval(id);
|
|
1003
|
+
};
|
|
1004
|
+
}, [delay]);
|
|
877
1005
|
}
|
|
1006
|
+
|
|
1007
|
+
function useLongPress(callback, initialDelay = 500, interval = 40) {
|
|
1008
|
+
const [pressed, setPressed] = useState(false);
|
|
1009
|
+
const [delay, setDelay] = useState(initialDelay);
|
|
1010
|
+
useInterval(() => {
|
|
1011
|
+
callback();
|
|
1012
|
+
setDelay(interval);
|
|
1013
|
+
}, pressed ? delay : null);
|
|
1014
|
+
useEventListener(globalThis.window, 'pointerup', () => {
|
|
1015
|
+
setPressed(false);
|
|
1016
|
+
setDelay(initialDelay);
|
|
1017
|
+
});
|
|
1018
|
+
return useCallback(() => {
|
|
1019
|
+
callback();
|
|
1020
|
+
setPressed(true);
|
|
1021
|
+
}, [callback]);
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
/** @category NumberInput */
|
|
1025
|
+
function IncrementStepper(_a) {
|
|
1026
|
+
var { size = 12, children, className } = _a, props = __rest(_a, ["size", "children", "className"]);
|
|
1027
|
+
const max = useNumberInputContext((s) => s.max);
|
|
1028
|
+
const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
|
|
1029
|
+
const readonly = useNumberInputContext((s) => s.readonly);
|
|
1030
|
+
const keepWithinRange = useNumberInputContext((s) => s.keepWithinRange);
|
|
1031
|
+
const increment = useNumberInputContext((s) => s.increment);
|
|
1032
|
+
const press = useLongPress(increment);
|
|
1033
|
+
return (
|
|
1034
|
+
// eslint-disable-next-line jsx-a11y/role-supports-aria-props
|
|
1035
|
+
jsx("div", Object.assign({ className: clsx('tremolo-number-input-increment-stepper', className), role: "button", tabIndex: -1, "aria-disabled": keepWithinRange && valueAsNumber >= (max !== null && max !== void 0 ? max : Number.MAX_SAFE_INTEGER), "aria-readonly": readonly, onPointerDown: press }, props, { children: children || (jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("polyline", { points: "18 15 12 9 6 15" }) })) })));
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
/** @category NumberInput */
|
|
1039
|
+
function DecrementStepper(_a) {
|
|
1040
|
+
var { size = 12, children, className } = _a, props = __rest(_a, ["size", "children", "className"]);
|
|
1041
|
+
const min = useNumberInputContext((s) => s.min);
|
|
1042
|
+
const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
|
|
1043
|
+
const readonly = useNumberInputContext((s) => s.readonly);
|
|
1044
|
+
const keepWithinRange = useNumberInputContext((s) => s.keepWithinRange);
|
|
1045
|
+
const decrement = useNumberInputContext((s) => s.decrement);
|
|
1046
|
+
const press = useLongPress(decrement);
|
|
1047
|
+
return (
|
|
1048
|
+
// eslint-disable-next-line jsx-a11y/role-supports-aria-props
|
|
1049
|
+
jsx("div", Object.assign({ className: clsx('tremolo-number-input-decrement-stepper', className), role: "button", tabIndex: -1, "aria-disabled": keepWithinRange && valueAsNumber <= (min !== null && min !== void 0 ? min : Number.MIN_SAFE_INTEGER), "aria-readonly": readonly, onPointerDown: press }, props, { children: children || (jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("polyline", { points: "6 9 12 15 18 9" }) })) })));
|
|
1050
|
+
}
|
|
1051
|
+
|
|
878
1052
|
/**
|
|
879
1053
|
* Input with some useful functions for entering numerical values.
|
|
880
1054
|
* @category NumberInput
|
|
881
1055
|
*/
|
|
882
|
-
|
|
883
|
-
var { value, min, max, step = 1,
|
|
884
|
-
|
|
1056
|
+
const NumberInput = forwardRef((_a, ref) => {
|
|
1057
|
+
var { value, min, max, step = 1, units, readonly = false, disabled = false, digit, variant = 'outline', selectWithFocus = 'none', blurOnEnter = true, keepWithinRange = true, clampValueOnBlur = true, wheel = ['raw', 1], keyboard = ['raw', 1], activeColor, wrapperClassName, className, onChange, onFocus, onBlur, children } = _a, props = __rest(_a, ["value", "min", "max", "step", "units", "readonly", "disabled", "digit", "variant", "selectWithFocus", "blurOnEnter", "keepWithinRange", "clampValueOnBlur", "wheel", "keyboard", "activeColor", "wrapperClassName", "className", "onChange", "onFocus", "onBlur", "children"]);
|
|
1058
|
+
const colors = {
|
|
1059
|
+
'--active-color': activeColor,
|
|
1060
|
+
};
|
|
1061
|
+
return (jsx(NumberInputProvider, { value: String(value), min: min, max: max, step: step, units: units, digit: digit, readonly: readonly, keepWithinRange: keepWithinRange, onChange: onChange, children: jsxs("div", { className: clsx('tremolo-number-input-wrapper', wrapperClassName),
|
|
885
1062
|
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
|
886
|
-
tabIndex: 0, "data-stepper": !!children, children: [jsx(InternalInput, Object.assign({
|
|
1063
|
+
tabIndex: 0, style: Object.assign({}, colors), "data-stepper": !!children, "data-variant": variant, children: [jsx(InternalInput, Object.assign({ ref: ref, readonly: readonly, disabled: disabled, selectWithFocus: selectWithFocus, blurOnEnter: blurOnEnter, clampValueOnBlur: clampValueOnBlur, wheel: wheel, keyboard: keyboard, className: className, onChange: onChange, onFocus: onFocus, onBlur: onBlur }, props)), children] }) }));
|
|
1064
|
+
});
|
|
1065
|
+
|
|
1066
|
+
/**
|
|
1067
|
+
* @returns [refCallback, pointerDownHandler]
|
|
1068
|
+
*/
|
|
1069
|
+
function useDragWithElement({ baseElementRef, onDrag, onDragStart, onDragEnd, }) {
|
|
1070
|
+
const dragged = useRef(false);
|
|
1071
|
+
const normalizedX = useRef(0);
|
|
1072
|
+
const normalizedY = useRef(0);
|
|
1073
|
+
const handleDrag = useCallback((event) => {
|
|
1074
|
+
if (!baseElementRef.current || !dragged.current) {
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
const isTouch = event instanceof TouchEvent;
|
|
1078
|
+
if (isTouch && event.cancelable)
|
|
1079
|
+
event.preventDefault();
|
|
1080
|
+
const { left, top, right, bottom } = baseElementRef.current.getBoundingClientRect();
|
|
1081
|
+
const mouseX = isTouch ? event.touches[0].clientX : event.clientX;
|
|
1082
|
+
const mouseY = isTouch ? event.touches[0].clientY : event.clientY;
|
|
1083
|
+
const nx = normalizeValue(mouseX, left, right);
|
|
1084
|
+
const ny = normalizeValue(mouseY, top, bottom);
|
|
1085
|
+
normalizedX.current = nx;
|
|
1086
|
+
normalizedY.current = ny;
|
|
1087
|
+
onDrag(nx, ny);
|
|
1088
|
+
}, [onDrag, baseElementRef]);
|
|
1089
|
+
const refHandler = useRefCallbackEvent('touchmove', handleDrag, { passive: false }, [onDrag]);
|
|
1090
|
+
const pointerDownHandler = useCallback((event) => {
|
|
1091
|
+
dragged.current = true;
|
|
1092
|
+
handleDrag(event);
|
|
1093
|
+
onDragStart === null || onDragStart === void 0 ? void 0 : onDragStart(normalizedX.current, normalizedY.current);
|
|
1094
|
+
}, [handleDrag, onDragStart]);
|
|
1095
|
+
useEventListener(globalThis.window, 'pointermove', handleDrag);
|
|
1096
|
+
useEventListener(globalThis.window, 'pointerup', () => {
|
|
1097
|
+
if (!dragged.current)
|
|
1098
|
+
return;
|
|
1099
|
+
dragged.current = false;
|
|
1100
|
+
onDragEnd === null || onDragEnd === void 0 ? void 0 : onDragEnd(normalizedX.current, normalizedY.current);
|
|
1101
|
+
});
|
|
1102
|
+
return [refHandler, pointerDownHandler];
|
|
887
1103
|
}
|
|
888
1104
|
|
|
889
1105
|
/** @category Piano */
|
|
890
1106
|
function KeyLabel(_a) {
|
|
891
|
-
var { label, className, wrapperClassName, wrapperStyle, __note,
|
|
1107
|
+
var { label, className, wrapperClassName, wrapperStyle, __note, __label } = _a, props = __rest(_a, ["label", "className", "wrapperClassName", "wrapperStyle", "__note", "__label"]);
|
|
1108
|
+
const noteRange = usePianoContext((s) => s.noteRange);
|
|
1109
|
+
const noteRangeArray = getNoteRangeArray(noteRange);
|
|
1110
|
+
const index = noteRangeArray.indexOf(__note);
|
|
892
1111
|
const content = label
|
|
893
|
-
? label(__note,
|
|
894
|
-
: __label && __label(__note,
|
|
1112
|
+
? label(__note, index)
|
|
1113
|
+
: __label && __label(__note, index);
|
|
895
1114
|
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 })) })));
|
|
896
1115
|
}
|
|
897
1116
|
|
|
898
1117
|
/** @category Piano */
|
|
899
1118
|
const defaultWhiteKeyWidth = 40;
|
|
900
1119
|
/** @category Piano */
|
|
901
|
-
const
|
|
902
|
-
|
|
1120
|
+
const defaultBlackKeyWidth = 26;
|
|
1121
|
+
const KeyImpl = forwardRef((_a, ref) => {
|
|
1122
|
+
var { keyType, noteNumber, width, height, bg, color, activeBg, activeColor, className, style, children, __width } = _a, props = __rest(_a, ["keyType", "noteNumber", "width", "height", "bg", "color", "activeBg", "activeColor", "className", "style", "children", "__width"]);
|
|
903
1123
|
const [played, setPlayed] = useState(false);
|
|
1124
|
+
const midiMax = usePianoContext((s) => s.midiMax);
|
|
1125
|
+
const fill = usePianoContext((s) => s.fill);
|
|
1126
|
+
// const glissando = usePianoContext(s => s.glissando)
|
|
1127
|
+
const onPlayNote = usePianoContext((s) => s.onPlayNote);
|
|
1128
|
+
const onStopNote = usePianoContext((s) => s.onStopNote);
|
|
1129
|
+
const label = usePianoContext((s) => s.label);
|
|
1130
|
+
const notePosition = usePianoContext((s) => s.notePosition);
|
|
1131
|
+
const position = notePosition(noteNumber);
|
|
1132
|
+
const disabled = noteNumber > midiMax;
|
|
1133
|
+
const colors = {
|
|
1134
|
+
'--bg': bg,
|
|
1135
|
+
'--color': color,
|
|
1136
|
+
'--active-bg': activeBg,
|
|
1137
|
+
'--active-color': activeColor,
|
|
1138
|
+
};
|
|
904
1139
|
useImperativeHandle(ref, () => {
|
|
905
1140
|
return {
|
|
906
1141
|
play() {
|
|
907
|
-
if (
|
|
1142
|
+
if (disabled)
|
|
908
1143
|
return;
|
|
909
1144
|
setPlayed(true);
|
|
910
|
-
if (
|
|
911
|
-
|
|
1145
|
+
if (onPlayNote)
|
|
1146
|
+
onPlayNote(noteNumber);
|
|
912
1147
|
},
|
|
913
1148
|
stop() {
|
|
914
1149
|
setPlayed(false);
|
|
915
|
-
if (
|
|
916
|
-
|
|
1150
|
+
if (onStopNote)
|
|
1151
|
+
onStopNote(noteNumber);
|
|
917
1152
|
},
|
|
918
1153
|
played() {
|
|
919
1154
|
return played;
|
|
920
1155
|
},
|
|
921
1156
|
};
|
|
922
|
-
}, [
|
|
1157
|
+
}, [disabled, noteNumber, onPlayNote, onStopNote, played]);
|
|
923
1158
|
let keyLabelProps = {};
|
|
924
1159
|
if (children != undefined) {
|
|
925
1160
|
React.Children.forEach(children, (child) => {
|
|
@@ -936,109 +1171,85 @@ const WhiteKey = forwardRef((_a, ref) => {
|
|
|
936
1171
|
}
|
|
937
1172
|
});
|
|
938
1173
|
}
|
|
939
|
-
return (jsx("div", Object.assign({ className: clsx(`tremolo-piano-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
}, onPointerEnter: () => {
|
|
946
|
-
if (__disabled)
|
|
947
|
-
return;
|
|
948
|
-
if (__glissando) {
|
|
949
|
-
setPlayed(true);
|
|
950
|
-
if (__onPlayNote)
|
|
951
|
-
__onPlayNote(__note);
|
|
952
|
-
}
|
|
953
|
-
}, onPointerUp: () => {
|
|
954
|
-
if (__disabled)
|
|
955
|
-
return;
|
|
956
|
-
setPlayed(false);
|
|
957
|
-
if (__onStopNote)
|
|
958
|
-
__onStopNote(__note);
|
|
959
|
-
}, onPointerLeave: () => {
|
|
960
|
-
if (__disabled)
|
|
961
|
-
return;
|
|
962
|
-
if (__glissando) {
|
|
963
|
-
setPlayed(false);
|
|
964
|
-
if (__onStopNote)
|
|
965
|
-
__onStopNote(__note);
|
|
966
|
-
}
|
|
967
|
-
} }, props, { children: jsx(KeyLabel, Object.assign({ __note: __note, __label: __label, __index: __index }, keyLabelProps)) })));
|
|
1174
|
+
return (jsx("div", Object.assign({ className: clsx(`tremolo-piano-${keyType}-key`, className), "aria-disabled": disabled, "data-active": played, style: Object.assign(Object.assign(Object.assign({}, colors), { left: position, width: fill ? __width : width, height: height }), style) }, props, { children: jsx(KeyLabel, Object.assign({ label: label, __note: noteNumber }, keyLabelProps)) })));
|
|
1175
|
+
});
|
|
1176
|
+
/** @category Piano */
|
|
1177
|
+
const WhiteKey = forwardRef((_a, ref) => {
|
|
1178
|
+
var { width = defaultWhiteKeyWidth, height = '100%', onContextMenu = (e) => e.preventDefault() } = _a, props = __rest(_a, ["width", "height", "onContextMenu"]);
|
|
1179
|
+
return (jsx(KeyImpl, Object.assign({ ref: ref, keyType: "white", width: width, height: height, onContextMenu: onContextMenu }, props)));
|
|
968
1180
|
});
|
|
969
|
-
|
|
970
1181
|
/** @category Piano */
|
|
971
1182
|
const BlackKey = forwardRef((_a, ref) => {
|
|
972
|
-
var { width =
|
|
973
|
-
|
|
974
|
-
useImperativeHandle(ref, () => {
|
|
975
|
-
return {
|
|
976
|
-
play() {
|
|
977
|
-
if (__disabled)
|
|
978
|
-
return;
|
|
979
|
-
setPlayed(true);
|
|
980
|
-
if (__onPlayNote)
|
|
981
|
-
__onPlayNote(__note);
|
|
982
|
-
},
|
|
983
|
-
stop() {
|
|
984
|
-
setPlayed(false);
|
|
985
|
-
if (__onStopNote)
|
|
986
|
-
__onStopNote(__note);
|
|
987
|
-
},
|
|
988
|
-
played() {
|
|
989
|
-
return played;
|
|
990
|
-
},
|
|
991
|
-
};
|
|
992
|
-
}, [__disabled, __note, __onPlayNote, __onStopNote, played]);
|
|
993
|
-
let keyLabelProps = {};
|
|
994
|
-
if (children != undefined) {
|
|
995
|
-
React.Children.forEach(children, (child) => {
|
|
996
|
-
if (React.isValidElement(child)) {
|
|
997
|
-
if (child.type == KeyLabel) {
|
|
998
|
-
keyLabelProps = child.props;
|
|
999
|
-
}
|
|
1000
|
-
else {
|
|
1001
|
-
throw new Error('only <KeyLabel>');
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
else {
|
|
1005
|
-
throw new Error('children is an invalid element.');
|
|
1006
|
-
}
|
|
1007
|
-
});
|
|
1008
|
-
}
|
|
1009
|
-
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: () => {
|
|
1010
|
-
if (__disabled)
|
|
1011
|
-
return;
|
|
1012
|
-
setPlayed(true);
|
|
1013
|
-
if (__onPlayNote)
|
|
1014
|
-
__onPlayNote(__note);
|
|
1015
|
-
}, onPointerEnter: () => {
|
|
1016
|
-
if (__disabled)
|
|
1017
|
-
return;
|
|
1018
|
-
if (__glissando) {
|
|
1019
|
-
setPlayed(true);
|
|
1020
|
-
if (__onPlayNote)
|
|
1021
|
-
__onPlayNote(__note);
|
|
1022
|
-
}
|
|
1023
|
-
}, onPointerUp: () => {
|
|
1024
|
-
if (__disabled)
|
|
1025
|
-
return;
|
|
1026
|
-
setPlayed(false);
|
|
1027
|
-
if (__onStopNote)
|
|
1028
|
-
__onStopNote(__note);
|
|
1029
|
-
}, onPointerLeave: () => {
|
|
1030
|
-
if (__disabled)
|
|
1031
|
-
return;
|
|
1032
|
-
if (__glissando) {
|
|
1033
|
-
setPlayed(false);
|
|
1034
|
-
if (__onStopNote)
|
|
1035
|
-
__onStopNote(__note);
|
|
1036
|
-
}
|
|
1037
|
-
} }, props, { children: jsx(KeyLabel, Object.assign({ __note: __note, __label: __label, __index: __index }, keyLabelProps)) })));
|
|
1183
|
+
var { width = defaultBlackKeyWidth, height = '60%', onContextMenu = (e) => e.preventDefault() } = _a, props = __rest(_a, ["width", "height", "onContextMenu"]);
|
|
1184
|
+
return (jsx(KeyImpl, Object.assign({ ref: ref, keyType: "black", width: width, height: height, onContextMenu: onContextMenu }, props)));
|
|
1038
1185
|
});
|
|
1039
1186
|
|
|
1187
|
+
const createPianoStore = (initProps) => {
|
|
1188
|
+
const noteRange = (initProps === null || initProps === void 0 ? void 0 : initProps.noteRange) || { first: 0};
|
|
1189
|
+
const DEFAULT_PROPS = {
|
|
1190
|
+
noteRange: { first: 0, last: 127 },
|
|
1191
|
+
glissando: true,
|
|
1192
|
+
midiMax: 127,
|
|
1193
|
+
fill: false,
|
|
1194
|
+
label: () => undefined,
|
|
1195
|
+
onPlayNote: () => { },
|
|
1196
|
+
onStopNote: () => { },
|
|
1197
|
+
};
|
|
1198
|
+
return createStore()(() => (Object.assign(Object.assign(Object.assign({}, DEFAULT_PROPS), initProps), { notePosition: (note) => {
|
|
1199
|
+
const pitchPositions = {
|
|
1200
|
+
C: 0,
|
|
1201
|
+
'C#': 1,
|
|
1202
|
+
D: 1,
|
|
1203
|
+
'D#': 2,
|
|
1204
|
+
E: 2,
|
|
1205
|
+
F: 3,
|
|
1206
|
+
'F#': 4,
|
|
1207
|
+
G: 4,
|
|
1208
|
+
'G#': 5,
|
|
1209
|
+
A: 5,
|
|
1210
|
+
'A#': 6,
|
|
1211
|
+
B: 6,
|
|
1212
|
+
};
|
|
1213
|
+
const whiteNoteWidth = defaultWhiteKeyWidth;
|
|
1214
|
+
const blackNoteWidth = defaultBlackKeyWidth;
|
|
1215
|
+
const padding = 1;
|
|
1216
|
+
const targetNoteKey = noteKey(note);
|
|
1217
|
+
const firstNoteKey = noteKey(noteRange.first);
|
|
1218
|
+
const octave = Math.floor((note - noteRange.first) / 12);
|
|
1219
|
+
const octaveOffset = noteKeys.indexOf(firstNoteKey) > noteKeys.indexOf(targetNoteKey) ? 1 : 0;
|
|
1220
|
+
const w = whiteNoteWidth + padding;
|
|
1221
|
+
const pos = pitchPositions[targetNoteKey] - pitchPositions[firstNoteKey];
|
|
1222
|
+
const blackKeyOffset = isBlackKey(note) ? blackNoteWidth / 2 : 0;
|
|
1223
|
+
return pos * w + (octave + octaveOffset) * 7 * w - blackKeyOffset;
|
|
1224
|
+
} })));
|
|
1225
|
+
};
|
|
1226
|
+
const PianoContext = createContext(null);
|
|
1227
|
+
function PianoProvider(_a) {
|
|
1228
|
+
var { children } = _a, props = __rest(_a, ["children"]);
|
|
1229
|
+
const storeRef = useRef(null);
|
|
1230
|
+
if (!storeRef.current) {
|
|
1231
|
+
storeRef.current = createPianoStore(props);
|
|
1232
|
+
}
|
|
1233
|
+
useEffect(() => {
|
|
1234
|
+
if (storeRef.current) {
|
|
1235
|
+
storeRef.current.setState(props);
|
|
1236
|
+
}
|
|
1237
|
+
else {
|
|
1238
|
+
storeRef.current = createPianoStore(props);
|
|
1239
|
+
}
|
|
1240
|
+
}, [props]);
|
|
1241
|
+
return (jsx(PianoContext.Provider, { value: storeRef.current, children: children }));
|
|
1242
|
+
}
|
|
1243
|
+
/** @category Piano */
|
|
1244
|
+
function usePianoContext(selector) {
|
|
1245
|
+
const store = useContext(PianoContext);
|
|
1246
|
+
if (!store)
|
|
1247
|
+
throw new Error('Missing PianoContext.Provider in the tree');
|
|
1248
|
+
return useStore(store, selector);
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1040
1251
|
/** @category Piano */
|
|
1041
|
-
const
|
|
1252
|
+
const SHORTCUTS = {
|
|
1042
1253
|
HOME_ROW: {
|
|
1043
1254
|
keys: [
|
|
1044
1255
|
'a',
|
|
@@ -1062,67 +1273,121 @@ const Shortcuts = {
|
|
|
1062
1273
|
},
|
|
1063
1274
|
};
|
|
1064
1275
|
|
|
1276
|
+
/**
|
|
1277
|
+
* Piano component
|
|
1278
|
+
*
|
|
1279
|
+
* TODO:
|
|
1280
|
+
* - add scale highlight
|
|
1281
|
+
*/
|
|
1065
1282
|
/** @category Piano */
|
|
1283
|
+
function getNoteRangeArray(noteRange) {
|
|
1284
|
+
return Array.from({ length: noteRange.last - noteRange.first + 1 }, (_, i) => i + noteRange.first);
|
|
1285
|
+
}
|
|
1286
|
+
/**
|
|
1287
|
+
* Customizable piano component.
|
|
1288
|
+
* @category Piano
|
|
1289
|
+
*/
|
|
1066
1290
|
function Piano(_a) {
|
|
1067
|
-
var { noteRange, glissando = true, midiMax = 127, keyboardShortcuts, fill = false, height = fill ? '100%' : 160, style, className, onPlayNote, onStopNote, label, children } = _a, props = __rest(_a, ["noteRange", "glissando", "midiMax", "keyboardShortcuts", "fill", "height", "style", "className", "onPlayNote", "onStopNote", "label", "children"]);
|
|
1291
|
+
var { noteRange, glissando = true, midiMax = 127, keyboardShortcuts, fill = false, height = fill ? '100%' : 160, whiteNoteWidth: _whiteNoteWidth = defaultWhiteKeyWidth, style, className, onPlayNote, onStopNote, label, children, onPointerDown } = _a, props = __rest(_a, ["noteRange", "glissando", "midiMax", "keyboardShortcuts", "fill", "height", "whiteNoteWidth", "style", "className", "onPlayNote", "onStopNote", "label", "children", "onPointerDown"]);
|
|
1068
1292
|
// -- state and ref ---
|
|
1069
|
-
const [
|
|
1070
|
-
const [whiteNoteWidth, setWhiteNoteWidth] = useState(-1);
|
|
1293
|
+
const [whiteNoteWidth, setWhiteNoteWidth] = useState(_whiteNoteWidth);
|
|
1071
1294
|
const keyRefs = useRef([]);
|
|
1072
1295
|
for (let i = 0; i < noteRange.last - noteRange.first + 1; i++) {
|
|
1073
1296
|
keyRefs.current[i] = createRef();
|
|
1074
1297
|
}
|
|
1075
1298
|
const pianoRef = useRef(null);
|
|
1299
|
+
const hitKeyIndex = useRef(-1);
|
|
1076
1300
|
// --- interpret props ---
|
|
1077
|
-
const noteRangeArray =
|
|
1078
|
-
const
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1301
|
+
const noteRangeArray = getNoteRangeArray(noteRange);
|
|
1302
|
+
const whiteNotes = noteRangeArray.filter((v) => isWhiteKey(v));
|
|
1303
|
+
const blackNotes = noteRangeArray.filter((v) => isBlackKey(v));
|
|
1304
|
+
const whiteNoteCount = whiteNotes.length;
|
|
1305
|
+
const padding = 1;
|
|
1306
|
+
const staticWidth = (whiteNoteWidth + padding) * whiteNoteCount;
|
|
1307
|
+
// const blackNoteShiftPercent = 0.3
|
|
1308
|
+
// TODO: if fill setWhiteNoteWidth
|
|
1309
|
+
// useMemo
|
|
1310
|
+
// __width
|
|
1311
|
+
const childrenWithProps = useMemo(() => {
|
|
1312
|
+
return (children &&
|
|
1313
|
+
React.Children.map(children, (child, index) => {
|
|
1314
|
+
if (React.isValidElement(child)) {
|
|
1315
|
+
const props = { ref: keyRefs.current[index] };
|
|
1316
|
+
return React.cloneElement(child, props);
|
|
1091
1317
|
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
}
|
|
1096
|
-
});
|
|
1097
|
-
}
|
|
1318
|
+
return child;
|
|
1319
|
+
}));
|
|
1320
|
+
}, [children]);
|
|
1098
1321
|
// --- internal functions ---
|
|
1099
|
-
|
|
1322
|
+
const notePosition = useCallback((note) => {
|
|
1100
1323
|
const pitchPositions = {
|
|
1101
1324
|
C: 0,
|
|
1102
|
-
'C#':
|
|
1325
|
+
'C#': 1,
|
|
1103
1326
|
D: 1,
|
|
1104
|
-
'D#':
|
|
1327
|
+
'D#': 2,
|
|
1105
1328
|
E: 2,
|
|
1106
1329
|
F: 3,
|
|
1107
|
-
'F#':
|
|
1330
|
+
'F#': 4,
|
|
1108
1331
|
G: 4,
|
|
1109
|
-
'G#':
|
|
1332
|
+
'G#': 5,
|
|
1110
1333
|
A: 5,
|
|
1111
|
-
'A#':
|
|
1334
|
+
'A#': 6,
|
|
1112
1335
|
B: 6,
|
|
1113
1336
|
};
|
|
1114
|
-
const
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
const
|
|
1119
|
-
const firstNoteName = toNoteName(noteRange.first);
|
|
1120
|
-
const pos = pitchPositions[_noteName] - pitchPositions[firstNoteName];
|
|
1337
|
+
const whiteNoteWidth = defaultWhiteKeyWidth;
|
|
1338
|
+
const blackNoteWidth = defaultBlackKeyWidth;
|
|
1339
|
+
const padding = 1;
|
|
1340
|
+
const targetNoteKey = noteKey(note);
|
|
1341
|
+
const firstNoteKey = noteKey(noteRange.first);
|
|
1121
1342
|
const octave = Math.floor((note - noteRange.first) / 12);
|
|
1122
|
-
const octaveOffset =
|
|
1123
|
-
const
|
|
1124
|
-
|
|
1125
|
-
|
|
1343
|
+
const octaveOffset = noteKeys.indexOf(firstNoteKey) > noteKeys.indexOf(targetNoteKey) ? 1 : 0;
|
|
1344
|
+
const w = whiteNoteWidth + padding;
|
|
1345
|
+
const pos = pitchPositions[targetNoteKey] - pitchPositions[firstNoteKey];
|
|
1346
|
+
const blackKeyOffset = isBlackKey(note) ? blackNoteWidth / 2 : 0;
|
|
1347
|
+
return pos * w + (octave + octaveOffset) * 7 * w - blackKeyOffset;
|
|
1348
|
+
}, [noteRange.first]);
|
|
1349
|
+
const getHitKeyIndex = useCallback((x, y) => {
|
|
1350
|
+
if (!pianoRef.current)
|
|
1351
|
+
return -1;
|
|
1352
|
+
const containerHeight = pianoRef.current.clientHeight;
|
|
1353
|
+
const notes = [...blackNotes, ...whiteNotes];
|
|
1354
|
+
for (let i = 0; i < notes.length; i++) {
|
|
1355
|
+
const note = notes[i];
|
|
1356
|
+
const pos = notePosition(note);
|
|
1357
|
+
const w = isWhiteKey(note) ? defaultWhiteKeyWidth : defaultBlackKeyWidth;
|
|
1358
|
+
const h = isWhiteKey(note) ? containerHeight : containerHeight * 0.6;
|
|
1359
|
+
if (pos <= x && x < pos + w && 0 <= y && y < h) {
|
|
1360
|
+
return note;
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
return -1;
|
|
1364
|
+
}, [blackNotes, notePosition, whiteNotes]);
|
|
1365
|
+
// TODO: 単一のポインターに対しては、useDragで対応可能だが、
|
|
1366
|
+
// マルチタッチに対しては、TouchEventを使う必要がありそう
|
|
1367
|
+
// 取り敢えず、シングルタッチだけ対応
|
|
1368
|
+
const onDrag = useCallback((perX, perY) => {
|
|
1369
|
+
var _a, _b, _c, _d;
|
|
1370
|
+
if (!pianoRef.current)
|
|
1371
|
+
return;
|
|
1372
|
+
const x = perX * staticWidth;
|
|
1373
|
+
const y = perY * pianoRef.current.clientHeight;
|
|
1374
|
+
const note = getHitKeyIndex(x, y);
|
|
1375
|
+
const index = noteRangeArray.indexOf(note);
|
|
1376
|
+
if (index == -1)
|
|
1377
|
+
return;
|
|
1378
|
+
if (hitKeyIndex.current != index) {
|
|
1379
|
+
(_b = (_a = keyRefs.current[hitKeyIndex.current]) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.stop();
|
|
1380
|
+
(_d = (_c = keyRefs.current[index]) === null || _c === void 0 ? void 0 : _c.current) === null || _d === void 0 ? void 0 : _d.play();
|
|
1381
|
+
hitKeyIndex.current = index;
|
|
1382
|
+
}
|
|
1383
|
+
}, [getHitKeyIndex, noteRangeArray, staticWidth]);
|
|
1384
|
+
const onDragEnd = useCallback(() => {
|
|
1385
|
+
var _a, _b, _c, _d;
|
|
1386
|
+
if ((_b = (_a = keyRefs.current[hitKeyIndex.current]) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.played()) {
|
|
1387
|
+
(_d = (_c = keyRefs.current[hitKeyIndex.current]) === null || _c === void 0 ? void 0 : _c.current) === null || _d === void 0 ? void 0 : _d.stop();
|
|
1388
|
+
}
|
|
1389
|
+
hitKeyIndex.current = -1;
|
|
1390
|
+
}, [keyRefs]);
|
|
1126
1391
|
// --- hooks ---
|
|
1127
1392
|
useEffect(() => {
|
|
1128
1393
|
if (fill && pianoRef.current) {
|
|
@@ -1131,14 +1396,22 @@ function Piano(_a) {
|
|
|
1131
1396
|
throw new Error("doesn't have a parent element.");
|
|
1132
1397
|
const resizeObserver = new ResizeObserver(() => {
|
|
1133
1398
|
const w = pianoRef.current.clientWidth;
|
|
1134
|
-
setWhiteNoteWidth(w / whiteNoteCount -
|
|
1399
|
+
setWhiteNoteWidth(w / whiteNoteCount - padding);
|
|
1135
1400
|
});
|
|
1136
1401
|
resizeObserver.observe(parent);
|
|
1402
|
+
return () => {
|
|
1403
|
+
resizeObserver.unobserve(parent);
|
|
1404
|
+
};
|
|
1137
1405
|
}
|
|
1138
1406
|
else {
|
|
1139
|
-
setWhiteNoteWidth(
|
|
1407
|
+
setWhiteNoteWidth(_whiteNoteWidth);
|
|
1140
1408
|
}
|
|
1141
|
-
}, [fill,
|
|
1409
|
+
}, [fill, _whiteNoteWidth, whiteNoteCount]);
|
|
1410
|
+
const [touchMoveRefCallback, pointerDownHandler] = useDragWithElement({
|
|
1411
|
+
baseElementRef: pianoRef,
|
|
1412
|
+
onDrag: onDrag,
|
|
1413
|
+
onDragEnd: onDragEnd,
|
|
1414
|
+
});
|
|
1142
1415
|
useEventListener(globalThis.window, 'keydown', (e) => {
|
|
1143
1416
|
var _a, _b, _c, _d;
|
|
1144
1417
|
if (e.repeat)
|
|
@@ -1163,17 +1436,14 @@ function Piano(_a) {
|
|
|
1163
1436
|
(_b = (_a = keyRefs.current[index]) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.stop();
|
|
1164
1437
|
}
|
|
1165
1438
|
});
|
|
1166
|
-
return (jsx(
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
} }, props, { children: noteRangeArray.map((note, index) => {
|
|
1175
|
-
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, __onPlayNote: onPlayNote, __onStopNote: onStopNote, __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, __onPlayNote: onPlayNote, __onStopNote: onStopNote, __label: label }, blackKeyProps), note));
|
|
1176
|
-
}) })));
|
|
1439
|
+
return (jsx(PianoProvider, { noteRange: noteRange, glissando: glissando, midiMax: midiMax, fill: fill, onPlayNote: onPlayNote, onStopNote: onStopNote, label: label, children: jsx("div", Object.assign({ ref: (div) => {
|
|
1440
|
+
pianoRef.current = div;
|
|
1441
|
+
touchMoveRefCallback(div);
|
|
1442
|
+
}, className: clsx('tremolo-piano', className), style: Object.assign({ width: fill ? '100%' : staticWidth, height: height }, style), onPointerDown: (event) => {
|
|
1443
|
+
pointerDownHandler(event);
|
|
1444
|
+
onPointerDown === null || onPointerDown === void 0 ? void 0 : onPointerDown(event);
|
|
1445
|
+
} }, props, { children: childrenWithProps ||
|
|
1446
|
+
getNoteRangeArray(noteRange).map((note, index) => isWhiteKey(note) ? (jsx(WhiteKey, { ref: keyRefs.current[index], noteNumber: note }, note)) : (jsx(BlackKey, { ref: keyRefs.current[index], noteNumber: note }, note))) })) }));
|
|
1177
1447
|
}
|
|
1178
1448
|
|
|
1179
1449
|
const createSliderStore = (initProps) => {
|
|
@@ -1287,7 +1557,7 @@ function Scale(_a) {
|
|
|
1287
1557
|
/** @category Slider */
|
|
1288
1558
|
const defaultThumbSize = 22;
|
|
1289
1559
|
/** @category Slider */
|
|
1290
|
-
const SliderThumb = forwardRef(({ size, width = defaultThumbSize, height = defaultThumbSize, children, className, style, __percent, }, ref) => {
|
|
1560
|
+
const SliderThumb = forwardRef(({ size, width = defaultThumbSize, height = defaultThumbSize, color, children, className, style, __percent, }, ref) => {
|
|
1291
1561
|
const wrapperRef = useRef(null);
|
|
1292
1562
|
const vertical = useSliderContext((s) => s.vertical);
|
|
1293
1563
|
const disabled = useSliderContext((s) => s.disabled);
|
|
@@ -1311,13 +1581,14 @@ const SliderThumb = forwardRef(({ size, width = defaultThumbSize, height = defau
|
|
|
1311
1581
|
// default slider thumb
|
|
1312
1582
|
jsx("div", { ref: wrapperRef, className: clsx('tremolo-slider-thumb', className),
|
|
1313
1583
|
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
|
1314
|
-
tabIndex: 0, "aria-disabled": disabled, "aria-readonly": readonly, style: Object.assign({ width: size !== null && size !== void 0 ? size : width, height: size !== null && size !== void 0 ? size : height }, style) })) }));
|
|
1584
|
+
tabIndex: 0, "aria-disabled": disabled, "aria-readonly": readonly, style: Object.assign(Object.assign({ '--color': color }, { width: size !== null && size !== void 0 ? size : width, height: size !== null && size !== void 0 ? size : height }), style) })) }));
|
|
1315
1585
|
});
|
|
1316
1586
|
|
|
1317
1587
|
const defaultLength = 140;
|
|
1318
1588
|
const defaultThickness = 10;
|
|
1319
1589
|
/** @category Slider */
|
|
1320
|
-
function SliderTrack(
|
|
1590
|
+
function SliderTrack(_a) {
|
|
1591
|
+
var { length = defaultLength, thickness = defaultThickness, active, inactive, children, className, style, defaultStyle = true, __thumb, __percent = 0 } = _a, props = __rest(_a, ["length", "thickness", "active", "inactive", "children", "className", "style", "defaultStyle", "__thumb", "__percent"]);
|
|
1321
1592
|
const vertical = useSliderContext((s) => s.vertical);
|
|
1322
1593
|
const reverse = useSliderContext((s) => s.reverse);
|
|
1323
1594
|
const disabled = useSliderContext((s) => s.disabled);
|
|
@@ -1326,11 +1597,11 @@ function SliderTrack({ length = defaultLength, thickness = defaultThickness, act
|
|
|
1326
1597
|
'--active': active,
|
|
1327
1598
|
'--inactive': inactive,
|
|
1328
1599
|
};
|
|
1329
|
-
return (jsxs("div", { className: clsx('tremolo-slider-track', className), "aria-disabled": disabled, "data-vertical": vertical, style: !defaultStyle
|
|
1600
|
+
return (jsxs("div", Object.assign({ className: clsx('tremolo-slider-track', className), "aria-disabled": disabled, "data-vertical": vertical, style: !defaultStyle
|
|
1330
1601
|
? style
|
|
1331
1602
|
: Object.assign(Object.assign(Object.assign({}, colors), { background: xor(vertical, reverse)
|
|
1332
1603
|
? `linear-gradient(to ${direction}, var(--inactive, #eee) ${__percent}%, var(--active, #7998ec) ${__percent}%)`
|
|
1333
|
-
: `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] }));
|
|
1604
|
+
: `linear-gradient(to ${direction}, var(--active, #7998ec) ${__percent}%, var(--inactive, #eee) ${__percent}%)`, borderRadius: styleHelper(thickness, '/', 2), width: !vertical ? length : thickness, height: vertical ? length : thickness }), style) }, props, { children: [children, __thumb] })));
|
|
1334
1605
|
}
|
|
1335
1606
|
|
|
1336
1607
|
/**
|
|
@@ -1338,12 +1609,11 @@ function SliderTrack({ length = defaultLength, thickness = defaultThickness, act
|
|
|
1338
1609
|
* @category Slider
|
|
1339
1610
|
*/
|
|
1340
1611
|
const Slider = forwardRef((_a, ref) => {
|
|
1341
|
-
var _b
|
|
1342
|
-
var { value, min, max, step = 1, skew = 1, vertical = false, reverse = false,
|
|
1612
|
+
var _b;
|
|
1613
|
+
var { value, min, max, step = 1, skew = 1, vertical = false, reverse = false, bodyNoSelect = true, wheel = ['raw', 1], keyboard = ['raw', 1], disabled = false, readonly = false, onChange, onDragStart, onDragEnd, className, style, children, onFocus, onBlur, onPointerDown, onKeyDown } = _a, props = __rest(_a, ["value", "min", "max", "step", "skew", "vertical", "reverse", "bodyNoSelect", "wheel", "keyboard", "disabled", "readonly", "onChange", "onDragStart", "onDragEnd", "className", "style", "children", "onFocus", "onBlur", "onPointerDown", "onKeyDown"]);
|
|
1343
1614
|
// -- state and ref ---
|
|
1344
1615
|
const trackElementRef = useRef(null);
|
|
1345
1616
|
const thumbRef = useRef(null);
|
|
1346
|
-
const thumbDragged = useRef(false);
|
|
1347
1617
|
// --- interpret props ---
|
|
1348
1618
|
const p = toFixed$1(normalizeValue(value, min, max, skew) * 100);
|
|
1349
1619
|
const rev = toFixed$1(100 - p);
|
|
@@ -1379,27 +1649,25 @@ const Slider = forwardRef((_a, ref) => {
|
|
|
1379
1649
|
});
|
|
1380
1650
|
}
|
|
1381
1651
|
// --- internal functions ---
|
|
1382
|
-
const
|
|
1383
|
-
if (!
|
|
1384
|
-
!thumbDragged.current ||
|
|
1385
|
-
!onChange ||
|
|
1386
|
-
readonly) {
|
|
1652
|
+
const onDrag = useCallback((nx, ny) => {
|
|
1653
|
+
if (!onChange || readonly)
|
|
1387
1654
|
return;
|
|
1388
|
-
|
|
1389
|
-
const isTouch = event instanceof TouchEvent;
|
|
1390
|
-
if (isTouch && event.cancelable)
|
|
1391
|
-
event.preventDefault();
|
|
1392
|
-
if (bodyNoSelect)
|
|
1393
|
-
addNoSelect();
|
|
1394
|
-
const { left: x1, top: y1, right: x2, bottom: y2, } = trackElementRef.current.getBoundingClientRect();
|
|
1395
|
-
const mouseX = isTouch ? event.touches[0].clientX : event.clientX;
|
|
1396
|
-
const mouseY = isTouch ? event.touches[0].clientY : event.clientY;
|
|
1397
|
-
const n = vertical
|
|
1398
|
-
? normalizeValue(mouseY, y1, y2)
|
|
1399
|
-
: normalizeValue(mouseX, x1, x2);
|
|
1655
|
+
const n = vertical ? ny : nx;
|
|
1400
1656
|
const v = rawValue(displayReversed ? 1 - n : n, min, max, skew);
|
|
1401
|
-
|
|
1402
|
-
|
|
1657
|
+
const v2 = clamp(stepValue(v, step), min, max);
|
|
1658
|
+
onChange(v2);
|
|
1659
|
+
}, [displayReversed, max, min, onChange, readonly, skew, step, vertical]);
|
|
1660
|
+
const updateValueByEvent = useCallback((eventType, x) => {
|
|
1661
|
+
let newValue;
|
|
1662
|
+
if (eventType == 'normalized') {
|
|
1663
|
+
const n = normalizeValue(value, min, max, skew);
|
|
1664
|
+
newValue = rawValue(n + x, min, max, skew);
|
|
1665
|
+
}
|
|
1666
|
+
else {
|
|
1667
|
+
newValue = value + x;
|
|
1668
|
+
}
|
|
1669
|
+
return clamp(stepValue(newValue, step), min, max);
|
|
1670
|
+
}, [max, min, skew, step, value]);
|
|
1403
1671
|
const handleKeyDown = useCallback((event) => {
|
|
1404
1672
|
if (!keyboard || !onChange || readonly)
|
|
1405
1673
|
return;
|
|
@@ -1409,19 +1677,30 @@ const Slider = forwardRef((_a, ref) => {
|
|
|
1409
1677
|
let x = key == 'ArrowRight' || key == 'ArrowUp' ? keyboard[1] : -keyboard[1];
|
|
1410
1678
|
if (reverse)
|
|
1411
1679
|
x *= -1;
|
|
1412
|
-
|
|
1413
|
-
if (keyboard[0] == 'normalized') {
|
|
1414
|
-
const n = normalizeValue(value, min, max, skew);
|
|
1415
|
-
v = rawValue(n + x, min, max, skew);
|
|
1416
|
-
}
|
|
1417
|
-
else {
|
|
1418
|
-
v = value + x;
|
|
1419
|
-
}
|
|
1420
|
-
onChange(clamp(stepValue(v, step), min, max));
|
|
1680
|
+
onChange(updateValueByEvent(keyboard[0], x));
|
|
1421
1681
|
}
|
|
1422
|
-
}, [
|
|
1682
|
+
}, [keyboard, onChange, readonly, reverse, updateValueByEvent]);
|
|
1423
1683
|
// --- hooks ---
|
|
1424
|
-
const touchMoveRefCallback =
|
|
1684
|
+
const [touchMoveRefCallback, pointerDownHandler] = useDragWithElement({
|
|
1685
|
+
baseElementRef: trackElementRef,
|
|
1686
|
+
onDrag: onDrag,
|
|
1687
|
+
onDragStart: (nx, ny) => {
|
|
1688
|
+
var _a;
|
|
1689
|
+
if (readonly)
|
|
1690
|
+
return;
|
|
1691
|
+
if (bodyNoSelect)
|
|
1692
|
+
addNoSelect();
|
|
1693
|
+
(_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
1694
|
+
onDragStart === null || onDragStart === void 0 ? void 0 : onDragStart(clamp(stepValue(rawValue(vertical ? ny : nx, min, max, skew), step), min, max));
|
|
1695
|
+
},
|
|
1696
|
+
onDragEnd: (nx, ny) => {
|
|
1697
|
+
if (readonly)
|
|
1698
|
+
return;
|
|
1699
|
+
if (bodyNoSelect)
|
|
1700
|
+
removeNoSelect();
|
|
1701
|
+
onDragEnd === null || onDragEnd === void 0 ? void 0 : onDragEnd(clamp(stepValue(rawValue(vertical ? ny : nx, min, max, skew), step), min, max));
|
|
1702
|
+
},
|
|
1703
|
+
});
|
|
1425
1704
|
const wheelRefCallback = useRefCallbackEvent('wheel', (event) => {
|
|
1426
1705
|
if (!wheel || !onChange || readonly)
|
|
1427
1706
|
return;
|
|
@@ -1437,26 +1716,10 @@ const Slider = forwardRef((_a, ref) => {
|
|
|
1437
1716
|
}
|
|
1438
1717
|
if (vertical && reverse)
|
|
1439
1718
|
x *= -1;
|
|
1440
|
-
|
|
1441
|
-
if (wheel[0] == 'normalized') {
|
|
1442
|
-
const n = normalizeValue(value, min, max, skew);
|
|
1443
|
-
v = rawValue(n + x, min, max, skew);
|
|
1444
|
-
}
|
|
1445
|
-
else {
|
|
1446
|
-
v = value + x;
|
|
1447
|
-
}
|
|
1448
|
-
onChange(clamp(stepValue(v, step), min, max));
|
|
1719
|
+
onChange(updateValueByEvent(wheel[0], x));
|
|
1449
1720
|
}, {
|
|
1450
1721
|
passive: false,
|
|
1451
|
-
}, [wheel,
|
|
1452
|
-
useEventListener(globalThis.window, 'mousemove', (event) => {
|
|
1453
|
-
handleValue(event);
|
|
1454
|
-
});
|
|
1455
|
-
useEventListener(globalThis.window, 'mouseup', () => {
|
|
1456
|
-
thumbDragged.current = false;
|
|
1457
|
-
if (bodyNoSelect)
|
|
1458
|
-
removeNoSelect();
|
|
1459
|
-
});
|
|
1722
|
+
}, [wheel, vertical, readonly, onChange, updateValueByEvent]);
|
|
1460
1723
|
useImperativeHandle(ref, () => {
|
|
1461
1724
|
return {
|
|
1462
1725
|
focus() {
|
|
@@ -1473,9 +1736,20 @@ const Slider = forwardRef((_a, ref) => {
|
|
|
1473
1736
|
wheelRefCallback(div);
|
|
1474
1737
|
touchMoveRefCallback(div);
|
|
1475
1738
|
}, 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) => {
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
}, onKeyDown:
|
|
1739
|
+
pointerDownHandler(event);
|
|
1740
|
+
onPointerDown === null || onPointerDown === void 0 ? void 0 : onPointerDown(event);
|
|
1741
|
+
}, onKeyDown: (event) => {
|
|
1742
|
+
handleKeyDown(event);
|
|
1743
|
+
onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event);
|
|
1744
|
+
}, onFocus: (event) => {
|
|
1745
|
+
var _a;
|
|
1746
|
+
(_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
1747
|
+
onFocus === null || onFocus === void 0 ? void 0 : onFocus(event);
|
|
1748
|
+
}, onBlur: (event) => {
|
|
1749
|
+
var _a;
|
|
1750
|
+
(_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.blur();
|
|
1751
|
+
onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
|
|
1752
|
+
} }, props, { children: jsxs("div", { style: {
|
|
1479
1753
|
display: 'flex',
|
|
1480
1754
|
flexDirection: vertical ? 'row' : 'column',
|
|
1481
1755
|
width: !vertical ? '100%' : undefined,
|
|
@@ -1494,12 +1768,14 @@ function WheelObserver(props) {
|
|
|
1494
1768
|
}
|
|
1495
1769
|
|
|
1496
1770
|
/** @category XYPad */
|
|
1497
|
-
function XYPadArea(
|
|
1498
|
-
|
|
1771
|
+
function XYPadArea(_a) {
|
|
1772
|
+
var { width = 120, height = 120, color, children, className, style, __thumb } = _a, props = __rest(_a, ["width", "height", "color", "children", "className", "style", "__thumb"]);
|
|
1773
|
+
return (jsxs("div", Object.assign({ className: clsx('tremolo-xy-pad-area', className), style: Object.assign(Object.assign({ '--color': color }, { width: width, height: height }), style) }, props, { children: [children, __thumb] })));
|
|
1499
1774
|
}
|
|
1500
1775
|
|
|
1501
1776
|
/** @category XYPad */
|
|
1502
|
-
const XYPadThumb = forwardRef((
|
|
1777
|
+
const XYPadThumb = forwardRef((_a, ref) => {
|
|
1778
|
+
var { size, width = 22, height = 22, color, children, wrapperClassName, wrapperStyle, className, style, __disabled, __css } = _a, props = __rest(_a, ["size", "width", "height", "color", "children", "wrapperClassName", "wrapperStyle", "className", "style", "__disabled", "__css"]);
|
|
1503
1779
|
const wrapperRef = useRef(null);
|
|
1504
1780
|
useImperativeHandle(ref, () => {
|
|
1505
1781
|
return {
|
|
@@ -1513,11 +1789,11 @@ const XYPadThumb = forwardRef(({ size, width = 22, height = 22, children, wrappe
|
|
|
1513
1789
|
},
|
|
1514
1790
|
};
|
|
1515
1791
|
}, []);
|
|
1516
|
-
return (jsx("div", { className: clsx('tremolo-xy-pad-thumb-wrapper', wrapperClassName), style: Object.assign(Object.assign({}, __css), wrapperStyle), children: children ? (children) : (
|
|
1792
|
+
return (jsx("div", Object.assign({ className: clsx('tremolo-xy-pad-thumb-wrapper', wrapperClassName), style: Object.assign(Object.assign({}, __css), wrapperStyle) }, props, { children: children ? (children) : (
|
|
1517
1793
|
// default thumb
|
|
1518
1794
|
jsx("div", { ref: wrapperRef, className: clsx('tremolo-xy-pad-thumb', className),
|
|
1519
1795
|
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
|
1520
|
-
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) })) }));
|
|
1796
|
+
tabIndex: 0, "aria-disabled": __disabled, style: Object.assign(Object.assign({ '--color': color }, { width: size !== null && size !== void 0 ? size : width, height: size !== null && size !== void 0 ? size : height }), style) })) })));
|
|
1521
1797
|
});
|
|
1522
1798
|
|
|
1523
1799
|
const defaultValueOptions = {
|
|
@@ -1532,8 +1808,8 @@ const defaultValueOptions = {
|
|
|
1532
1808
|
* @category XYPad
|
|
1533
1809
|
*/
|
|
1534
1810
|
const XYPad = forwardRef((_a, ref) => {
|
|
1535
|
-
var _b
|
|
1536
|
-
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"]);
|
|
1811
|
+
var _b;
|
|
1812
|
+
var { x: _x, y: _y, className, style, bodyNoSelect = true, disabled = false, readonly = false, onChange, onDragStart, onDragEnd, onPointerDown, onKeyDown, onFocus, onBlur, children } = _a, props = __rest(_a, ["x", "y", "className", "style", "bodyNoSelect", "disabled", "readonly", "onChange", "onDragStart", "onDragEnd", "onPointerDown", "onKeyDown", "onFocus", "onBlur", "children"]);
|
|
1537
1813
|
const x = useMemo(() => {
|
|
1538
1814
|
return Object.assign(Object.assign({}, defaultValueOptions), _x);
|
|
1539
1815
|
}, [_x]);
|
|
@@ -1543,13 +1819,13 @@ const XYPad = forwardRef((_a, ref) => {
|
|
|
1543
1819
|
// -- state and ref ---
|
|
1544
1820
|
const areaElementRef = useRef(null);
|
|
1545
1821
|
const thumbRef = useRef(null);
|
|
1546
|
-
const thumbDragged = useRef(false);
|
|
1547
1822
|
// --- interpret props ---
|
|
1548
1823
|
const nx = normalizeValue(x.value, x.min, x.max, x.skew);
|
|
1549
1824
|
const ny = normalizeValue(y.value, y.min, y.max, y.skew);
|
|
1550
1825
|
const percentX = toFixed$1((x.reverse ? 1 - nx : nx) * 100);
|
|
1551
1826
|
const percentY = toFixed$1((y.reverse ? 1 - ny : ny) * 100);
|
|
1552
|
-
let areaProps = {}
|
|
1827
|
+
let areaProps = {};
|
|
1828
|
+
let thumbProps = {};
|
|
1553
1829
|
if (children != undefined) {
|
|
1554
1830
|
React.Children.forEach(children, (child) => {
|
|
1555
1831
|
if (React.isValidElement(child)) {
|
|
@@ -1569,28 +1845,39 @@ const XYPad = forwardRef((_a, ref) => {
|
|
|
1569
1845
|
});
|
|
1570
1846
|
}
|
|
1571
1847
|
// --- internal functions ---
|
|
1572
|
-
const
|
|
1573
|
-
if (!
|
|
1574
|
-
!thumbDragged.current ||
|
|
1575
|
-
!onChange ||
|
|
1576
|
-
readonly)
|
|
1848
|
+
const onDrag = useCallback((nx, ny) => {
|
|
1849
|
+
if (!onChange || readonly)
|
|
1577
1850
|
return;
|
|
1578
|
-
const isTouch = event instanceof TouchEvent;
|
|
1579
|
-
if (isTouch && event.cancelable)
|
|
1580
|
-
event.preventDefault();
|
|
1581
|
-
if (bodyNoSelect)
|
|
1582
|
-
addNoSelect();
|
|
1583
|
-
const { left: x1, top: y1, right: x2, bottom: y2, } = areaElementRef.current.getBoundingClientRect();
|
|
1584
|
-
const mouseX = isTouch ? event.touches[0].clientX : event.clientX;
|
|
1585
|
-
const mouseY = isTouch ? event.touches[0].clientY : event.clientY;
|
|
1586
|
-
const nx = normalizeValue(mouseX, x1, x2);
|
|
1587
|
-
const ny = normalizeValue(mouseY, y1, y2);
|
|
1588
1851
|
const vx = rawValue(x.reverse ? 1 - nx : nx, x.min, x.max, x.skew);
|
|
1589
1852
|
const vy = rawValue(y.reverse ? 1 - ny : ny, y.min, y.max, y.skew);
|
|
1590
1853
|
const newX = clamp(stepValue(vx, x.step), x.min, x.max);
|
|
1591
1854
|
const newY = clamp(stepValue(vy, y.step), y.min, y.max);
|
|
1592
1855
|
onChange(newX, newY);
|
|
1593
|
-
}
|
|
1856
|
+
}, [
|
|
1857
|
+
onChange,
|
|
1858
|
+
readonly,
|
|
1859
|
+
x.max,
|
|
1860
|
+
x.min,
|
|
1861
|
+
x.reverse,
|
|
1862
|
+
x.skew,
|
|
1863
|
+
x.step,
|
|
1864
|
+
y.max,
|
|
1865
|
+
y.min,
|
|
1866
|
+
y.reverse,
|
|
1867
|
+
y.skew,
|
|
1868
|
+
y.step,
|
|
1869
|
+
]);
|
|
1870
|
+
const updateValueByEvent = useCallback((eventType, target, x) => {
|
|
1871
|
+
let v;
|
|
1872
|
+
if (eventType == 'normalized') {
|
|
1873
|
+
const n = normalizeValue(target.value, target.min, target.max, target.skew);
|
|
1874
|
+
v = rawValue(n + x, target.min, target.max, target.skew);
|
|
1875
|
+
}
|
|
1876
|
+
else {
|
|
1877
|
+
v = target.value + x;
|
|
1878
|
+
}
|
|
1879
|
+
return clamp(stepValue(v, target.step), target.min, target.max);
|
|
1880
|
+
}, []);
|
|
1594
1881
|
const handleKeyDown = useCallback((event) => {
|
|
1595
1882
|
if (!onChange || readonly)
|
|
1596
1883
|
return;
|
|
@@ -1606,15 +1893,7 @@ const XYPad = forwardRef((_a, ref) => {
|
|
|
1606
1893
|
delta *= -1;
|
|
1607
1894
|
if (target.reverse)
|
|
1608
1895
|
delta *= -1;
|
|
1609
|
-
|
|
1610
|
-
if (target.keyboard[0] == 'normalized') {
|
|
1611
|
-
const n = normalizeValue(target.value, target.min, target.max, target.skew);
|
|
1612
|
-
v = rawValue(n + delta, target.min, target.max, target.skew);
|
|
1613
|
-
}
|
|
1614
|
-
else {
|
|
1615
|
-
v = target.value + delta;
|
|
1616
|
-
}
|
|
1617
|
-
const newValue = clamp(stepValue(v, target.step), target.min, target.max);
|
|
1896
|
+
const newValue = updateValueByEvent(target.keyboard[0], target, delta);
|
|
1618
1897
|
if (isHorizontal) {
|
|
1619
1898
|
onChange(newValue, y.value);
|
|
1620
1899
|
}
|
|
@@ -1622,9 +1901,32 @@ const XYPad = forwardRef((_a, ref) => {
|
|
|
1622
1901
|
onChange(x.value, newValue);
|
|
1623
1902
|
}
|
|
1624
1903
|
}
|
|
1625
|
-
}, [x, y,
|
|
1904
|
+
}, [onChange, readonly, x, y, updateValueByEvent]);
|
|
1626
1905
|
// --- hooks ---
|
|
1627
|
-
const touchMoveRefCallback =
|
|
1906
|
+
const [touchMoveRefCallback, pointerDownHandler] = useDragWithElement({
|
|
1907
|
+
baseElementRef: areaElementRef,
|
|
1908
|
+
onDrag: onDrag,
|
|
1909
|
+
onDragStart: (nx, ny) => {
|
|
1910
|
+
var _a;
|
|
1911
|
+
if (readonly)
|
|
1912
|
+
return;
|
|
1913
|
+
if (bodyNoSelect)
|
|
1914
|
+
addNoSelect();
|
|
1915
|
+
(_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
1916
|
+
const valueX = clamp(stepValue(rawValue(nx, x.min, x.max, x.skew), x.step), x.min, x.max);
|
|
1917
|
+
const valueY = clamp(stepValue(rawValue(ny, y.min, y.max, y.skew), y.step), y.min, y.max);
|
|
1918
|
+
onDragStart === null || onDragStart === void 0 ? void 0 : onDragStart(valueX, valueY);
|
|
1919
|
+
},
|
|
1920
|
+
onDragEnd: (nx, ny) => {
|
|
1921
|
+
if (readonly)
|
|
1922
|
+
return;
|
|
1923
|
+
if (bodyNoSelect)
|
|
1924
|
+
removeNoSelect();
|
|
1925
|
+
const valueX = clamp(stepValue(rawValue(nx, x.min, x.max, x.skew), x.step), x.min, x.max);
|
|
1926
|
+
const valueY = clamp(stepValue(rawValue(ny, y.min, y.max, y.skew), y.step), y.min, y.max);
|
|
1927
|
+
onDragEnd === null || onDragEnd === void 0 ? void 0 : onDragEnd(valueX, valueY);
|
|
1928
|
+
},
|
|
1929
|
+
});
|
|
1628
1930
|
const wheelRefCallback = useRefCallbackEvent('wheel', (event) => {
|
|
1629
1931
|
if (!onChange || readonly)
|
|
1630
1932
|
return;
|
|
@@ -1637,15 +1939,7 @@ const XYPad = forwardRef((_a, ref) => {
|
|
|
1637
1939
|
delta *= -1;
|
|
1638
1940
|
if (target.reverse)
|
|
1639
1941
|
delta *= -1;
|
|
1640
|
-
|
|
1641
|
-
if (target.wheel[0] == 'normalized') {
|
|
1642
|
-
const n = normalizeValue(target.value, target.min, target.max, target.skew);
|
|
1643
|
-
v = rawValue(n + delta, target.min, target.max, target.skew);
|
|
1644
|
-
}
|
|
1645
|
-
else {
|
|
1646
|
-
v = target.value + delta;
|
|
1647
|
-
}
|
|
1648
|
-
const newValue = clamp(stepValue(v, target.step), target.min, target.max);
|
|
1942
|
+
const newValue = updateValueByEvent(target.wheel[0], target, delta);
|
|
1649
1943
|
if (event.shiftKey) {
|
|
1650
1944
|
onChange(newValue, y.value);
|
|
1651
1945
|
}
|
|
@@ -1654,15 +1948,7 @@ const XYPad = forwardRef((_a, ref) => {
|
|
|
1654
1948
|
}
|
|
1655
1949
|
}, {
|
|
1656
1950
|
passive: false,
|
|
1657
|
-
}, [x, y]);
|
|
1658
|
-
useEventListener(globalThis.window, 'mousemove', (event) => {
|
|
1659
|
-
handleValue(event);
|
|
1660
|
-
});
|
|
1661
|
-
useEventListener(globalThis.window, 'mouseup', () => {
|
|
1662
|
-
thumbDragged.current = false;
|
|
1663
|
-
if (bodyNoSelect)
|
|
1664
|
-
removeNoSelect();
|
|
1665
|
-
});
|
|
1951
|
+
}, [x, y, onChange, readonly, updateValueByEvent]);
|
|
1666
1952
|
useImperativeHandle(ref, () => {
|
|
1667
1953
|
return {
|
|
1668
1954
|
focus() {
|
|
@@ -1681,13 +1967,37 @@ const XYPad = forwardRef((_a, ref) => {
|
|
|
1681
1967
|
wheelRefCallback(div);
|
|
1682
1968
|
touchMoveRefCallback(div);
|
|
1683
1969
|
}, 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) => {
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
}, onKeyDown:
|
|
1970
|
+
pointerDownHandler(event);
|
|
1971
|
+
onPointerDown === null || onPointerDown === void 0 ? void 0 : onPointerDown(event);
|
|
1972
|
+
}, onKeyDown: (event) => {
|
|
1973
|
+
handleKeyDown(event);
|
|
1974
|
+
onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event);
|
|
1975
|
+
}, onFocus: (event) => {
|
|
1976
|
+
var _a;
|
|
1977
|
+
(_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
1978
|
+
onFocus === null || onFocus === void 0 ? void 0 : onFocus(event);
|
|
1979
|
+
}, onBlur: (event) => {
|
|
1980
|
+
var _a;
|
|
1981
|
+
(_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.blur();
|
|
1982
|
+
onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
|
|
1983
|
+
} }, 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: {
|
|
1687
1984
|
top: `${percentY}%`,
|
|
1688
1985
|
left: `${percentX}%`,
|
|
1689
1986
|
} }, thumbProps)) }, areaProps)) }) })));
|
|
1690
1987
|
});
|
|
1691
1988
|
|
|
1692
|
-
|
|
1989
|
+
const useAnimationFrame = (callback = () => { }, deps = []) => {
|
|
1990
|
+
const reqIdRef = useRef(-1);
|
|
1991
|
+
const loop = useCallback(() => {
|
|
1992
|
+
reqIdRef.current = requestAnimationFrame(loop);
|
|
1993
|
+
callback();
|
|
1994
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1995
|
+
}, [callback, ...deps]);
|
|
1996
|
+
useEffect(() => {
|
|
1997
|
+
reqIdRef.current = requestAnimationFrame(loop);
|
|
1998
|
+
return () => cancelAnimationFrame(reqIdRef.current);
|
|
1999
|
+
}, [loop]);
|
|
2000
|
+
};
|
|
2001
|
+
|
|
2002
|
+
export { AnimationCanvas, AnimationKnob, BlackKey, DecrementStepper, DragObserver, IncrementStepper, KeyLabel, Knob, NumberInput, Piano, SHORTCUTS, Scale, ScaleOption, Slider, SliderThumb, SliderTrack, Stepper, WheelObserver, WhiteKey, XYPad, XYPadArea, XYPadThumb, getNoteRangeArray, useAnimationFrame, useDrag, useEventListener, useInterval, useLongPress, useSliderContext };
|
|
1693
2003
|
//# sourceMappingURL=index.js.map
|