@tremolo-ui/react 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +285 -155
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +80 -32
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +95 -60
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +97 -62
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +262 -139
- package/dist/index.js.map +1 -1
- package/package.json +14 -9
- package/src/components/AnimationCanvas/canvas.ts +16 -0
- package/src/components/AnimationCanvas/index.tsx +54 -71
- package/src/components/Knob/index.css +54 -23
- package/src/components/Knob/index.tsx +30 -25
- package/src/components/NumberInput/InternalInput.tsx +1 -1
- package/src/components/NumberInput/context.tsx +2 -1
- package/src/components/NumberInput/index.css +1 -0
- package/src/components/NumberInput/index.tsx +5 -5
- package/src/components/NumberInput/type.ts +58 -0
- package/src/components/Piano/context.tsx +1 -1
- package/src/components/Piano/index.css +24 -8
- package/src/components/Piano/index.tsx +254 -219
- package/src/components/Piano/key.tsx +3 -3
- package/src/components/Slider/Scale.tsx +1 -1
- package/src/components/Slider/ScaleOption.tsx +1 -1
- package/src/components/Slider/Track.tsx +2 -2
- package/src/components/Slider/index.tsx +3 -2
- package/src/components/Slider/type.ts +26 -0
- package/src/components/XYPad/index.tsx +2 -2
- package/src/hooks/useAnimationFrame.ts +3 -0
- package/src/hooks/useCallbackRef.ts +2 -2
- package/src/hooks/useDrag.ts +2 -1
- package/src/hooks/useDragWithElement.ts +2 -1
- package/src/hooks/useEventListener.ts +3 -0
- package/src/hooks/useInterval.ts +3 -0
- package/src/hooks/useLongPress.ts +3 -0
- package/src/hooks/useMIDIAccess.ts +40 -0
- package/src/hooks/useMIDIInput.ts +50 -0
- package/src/hooks/useMIDIMessage.ts +24 -0
- package/src/hooks/useRefCallbackEvent.ts +4 -0
- package/src/index.ts +21 -13
- package/src/styles/{index.css → global.css} +1 -1
package/dist/index.cjs
CHANGED
|
@@ -21,13 +21,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
21
21
|
}) : target, mod));
|
|
22
22
|
|
|
23
23
|
//#endregion
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
let clsx = require("clsx");
|
|
25
|
+
clsx = __toESM(clsx);
|
|
26
|
+
let react = require("react");
|
|
27
|
+
react = __toESM(react);
|
|
28
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
29
|
+
let __tremolo_ui_functions = require("@tremolo-ui/functions");
|
|
30
|
+
let zustand = require("zustand");
|
|
31
31
|
|
|
32
32
|
//#region src/components/AnimationCanvas/canvas.ts
|
|
33
33
|
const drawingState = [
|
|
@@ -50,9 +50,6 @@ const drawingState = [
|
|
|
50
50
|
"direction",
|
|
51
51
|
"imageSmoothingEnabled"
|
|
52
52
|
];
|
|
53
|
-
|
|
54
|
-
//#endregion
|
|
55
|
-
//#region src/components/AnimationCanvas/index.tsx
|
|
56
53
|
function setDprConfig(canvas, context, width, height, dpr) {
|
|
57
54
|
canvas.width = width * dpr;
|
|
58
55
|
canvas.height = height * dpr;
|
|
@@ -61,7 +58,10 @@ function setDprConfig(canvas, context, width, height, dpr) {
|
|
|
61
58
|
canvas.style.width = `${width}px`;
|
|
62
59
|
canvas.style.height = `${height}px`;
|
|
63
60
|
}
|
|
64
|
-
|
|
61
|
+
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/components/AnimationCanvas/index.tsx
|
|
64
|
+
function AnimationCanvas({ draw, init, animate = true, options, width: _width = 100, height: _height = 100, relativeSize, reduceFlickering = true, className, onContextMenu = (event) => event.preventDefault(),...props }) {
|
|
65
65
|
const canvasRef = (0, react.useRef)(null);
|
|
66
66
|
const memoCanvasRef = (0, react.useRef)(null);
|
|
67
67
|
const reqIdRef = (0, react.useRef)(-1);
|
|
@@ -69,27 +69,40 @@ function AnimationCanvas({ options, init, draw, width = 100, height = 100, relat
|
|
|
69
69
|
const heightRef = (0, react.useRef)(0);
|
|
70
70
|
const deltaMemoRef = (0, react.useRef)(-1);
|
|
71
71
|
const startTimeRef = (0, react.useRef)(-1);
|
|
72
|
-
const loop = (0, react.useCallback)((context, width
|
|
72
|
+
const loop = (0, react.useCallback)((context, width, height, count) => {
|
|
73
73
|
const now = performance.now();
|
|
74
74
|
const deltaTime = now - deltaMemoRef.current;
|
|
75
75
|
const elapsedTime = now - startTimeRef.current;
|
|
76
76
|
deltaMemoRef.current = now;
|
|
77
|
-
reqIdRef.current = requestAnimationFrame(() => loop(context, width
|
|
77
|
+
if (animate) reqIdRef.current = requestAnimationFrame(() => loop(context, width, height, count + 1));
|
|
78
78
|
draw(context, {
|
|
79
|
-
width: width
|
|
80
|
-
height: height
|
|
79
|
+
width: width.current,
|
|
80
|
+
height: height.current,
|
|
81
81
|
count: count + 1,
|
|
82
82
|
deltaTime,
|
|
83
83
|
elapsedTime,
|
|
84
84
|
fps: 1e3 / deltaTime
|
|
85
85
|
});
|
|
86
|
-
}, [draw]);
|
|
86
|
+
}, [draw, animate]);
|
|
87
87
|
(0, react.useEffect)(() => {
|
|
88
88
|
if (!canvasRef.current) return;
|
|
89
89
|
const canvas = canvasRef.current;
|
|
90
90
|
const context = canvas.getContext("2d", options);
|
|
91
91
|
if (!context) throw new Error("Cannot get canvas context.");
|
|
92
92
|
const dpr = globalThis.devicePixelRatio;
|
|
93
|
+
const firstRendering = (width, height) => {
|
|
94
|
+
setDprConfig(canvas, context, width, height, dpr);
|
|
95
|
+
widthRef.current = width;
|
|
96
|
+
heightRef.current = height;
|
|
97
|
+
if (init) init(context, {
|
|
98
|
+
width,
|
|
99
|
+
height
|
|
100
|
+
});
|
|
101
|
+
const now = performance.now();
|
|
102
|
+
deltaMemoRef.current = now;
|
|
103
|
+
startTimeRef.current = now;
|
|
104
|
+
loop(context, widthRef, heightRef, -1);
|
|
105
|
+
};
|
|
93
106
|
if (relativeSize) {
|
|
94
107
|
const parent = canvas.parentElement;
|
|
95
108
|
if (!parent) throw new Error("Canvas doesn't have a parent element.");
|
|
@@ -97,52 +110,37 @@ function AnimationCanvas({ options, init, draw, width = 100, height = 100, relat
|
|
|
97
110
|
const memoContext = memoCanvas?.getContext("2d", options);
|
|
98
111
|
const ro = new ResizeObserver((entries) => {
|
|
99
112
|
for (const entry of entries) {
|
|
113
|
+
const w = entry.contentRect.width;
|
|
114
|
+
const h = entry.contentRect.height;
|
|
100
115
|
const contextMemo = {};
|
|
101
|
-
for (const prop of drawingState) contextMemo[prop] = context[prop];
|
|
102
|
-
const w$1 = entry.contentRect.width;
|
|
103
|
-
const h$1 = entry.contentRect.height;
|
|
104
116
|
if (reduceFlickering && memoCanvas && memoContext) {
|
|
105
|
-
|
|
106
|
-
memoCanvas.
|
|
117
|
+
for (const prop of drawingState) contextMemo[prop] = context[prop];
|
|
118
|
+
memoCanvas.width = w * dpr;
|
|
119
|
+
memoCanvas.height = h * dpr;
|
|
107
120
|
memoContext.scale(1 / dpr, 1 / dpr);
|
|
108
121
|
if (canvas.width > 0 && canvas.height > 0) memoContext.drawImage(canvas, 0, 0);
|
|
109
122
|
}
|
|
110
|
-
setDprConfig(canvas, context, w
|
|
111
|
-
widthRef.current = w
|
|
112
|
-
heightRef.current = h
|
|
113
|
-
|
|
114
|
-
|
|
123
|
+
setDprConfig(canvas, context, w, h, dpr);
|
|
124
|
+
widthRef.current = w;
|
|
125
|
+
heightRef.current = h;
|
|
126
|
+
if (reduceFlickering && memoContext && memoCanvas && memoCanvas.width > 0 && memoCanvas.height > 0) {
|
|
127
|
+
for (const prop of drawingState) context[prop] = contextMemo[prop];
|
|
128
|
+
context.drawImage(memoContext.canvas, 0, 0);
|
|
129
|
+
}
|
|
130
|
+
if (!animate) loop(context, widthRef, heightRef, -1);
|
|
115
131
|
}
|
|
116
132
|
});
|
|
117
133
|
ro.observe(parent);
|
|
118
|
-
const
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
if (init) init(context, {
|
|
122
|
-
width: widthRef.current,
|
|
123
|
-
height: heightRef.current
|
|
124
|
-
});
|
|
125
|
-
const now = performance.now();
|
|
126
|
-
deltaMemoRef.current = now;
|
|
127
|
-
startTimeRef.current = now;
|
|
128
|
-
loop(context, widthRef, heightRef, -1);
|
|
134
|
+
const width = parent.clientWidth;
|
|
135
|
+
const height = parent.clientHeight;
|
|
136
|
+
firstRendering(width, height);
|
|
129
137
|
return () => {
|
|
130
138
|
if (reqIdRef.current) cancelAnimationFrame(reqIdRef.current);
|
|
131
139
|
ro.disconnect();
|
|
132
140
|
};
|
|
133
141
|
} else {
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
widthRef.current = rect.width;
|
|
137
|
-
heightRef.current = rect.height;
|
|
138
|
-
if (init) init(context, {
|
|
139
|
-
width: widthRef.current,
|
|
140
|
-
height: heightRef.current
|
|
141
|
-
});
|
|
142
|
-
const now = performance.now();
|
|
143
|
-
deltaMemoRef.current = now;
|
|
144
|
-
startTimeRef.current = now;
|
|
145
|
-
loop(context, widthRef, heightRef, -1);
|
|
142
|
+
const { width, height } = canvas.getBoundingClientRect();
|
|
143
|
+
firstRendering(width, height);
|
|
146
144
|
return () => {
|
|
147
145
|
if (reqIdRef.current) cancelAnimationFrame(reqIdRef.current);
|
|
148
146
|
};
|
|
@@ -152,12 +150,13 @@ function AnimationCanvas({ options, init, draw, width = 100, height = 100, relat
|
|
|
152
150
|
init,
|
|
153
151
|
options,
|
|
154
152
|
reduceFlickering,
|
|
155
|
-
relativeSize
|
|
153
|
+
relativeSize,
|
|
154
|
+
animate
|
|
156
155
|
]);
|
|
157
156
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("canvas", {
|
|
158
157
|
className: (0, clsx.default)("tremolo-animation-canvas", className),
|
|
159
|
-
width: relativeSize ? 0 :
|
|
160
|
-
height: relativeSize ? 0 :
|
|
158
|
+
width: relativeSize ? 0 : _width,
|
|
159
|
+
height: relativeSize ? 0 : _height,
|
|
161
160
|
ref: canvasRef,
|
|
162
161
|
onContextMenu,
|
|
163
162
|
...props
|
|
@@ -170,8 +169,8 @@ function AnimationCanvas({ options, init, draw, width = 100, height = 100, relat
|
|
|
170
169
|
//#endregion
|
|
171
170
|
//#region src/hooks/useCallbackRef.ts
|
|
172
171
|
/**
|
|
173
|
-
*
|
|
174
|
-
*
|
|
172
|
+
* Internal
|
|
173
|
+
* @private
|
|
175
174
|
*/
|
|
176
175
|
function useCallbackRef(callback, deps = []) {
|
|
177
176
|
const callbackRef = (0, react.useRef)(() => {
|
|
@@ -202,8 +201,7 @@ function useEventListener(target, event, handler, options) {
|
|
|
202
201
|
handler
|
|
203
202
|
]);
|
|
204
203
|
return () => {
|
|
205
|
-
|
|
206
|
-
node?.removeEventListener(event, listener, options);
|
|
204
|
+
(typeof target === "function" ? target() : target ?? document)?.removeEventListener(event, listener, options);
|
|
207
205
|
};
|
|
208
206
|
}
|
|
209
207
|
|
|
@@ -228,6 +226,7 @@ function useRefCallbackEvent(event, handler, options, deps = []) {
|
|
|
228
226
|
//#endregion
|
|
229
227
|
//#region src/hooks/useDrag.ts
|
|
230
228
|
/**
|
|
229
|
+
* @category hooks
|
|
231
230
|
* @returns [refCallback, pointerDownHandler]
|
|
232
231
|
*/
|
|
233
232
|
function useDrag({ threshold = 1, onDrag, onDragStart, onDragEnd }) {
|
|
@@ -294,9 +293,7 @@ const AnimationKnob = (0, react.forwardRef)(({ value, min, max, defaultValue = m
|
|
|
294
293
|
const isRelativeSize = typeof (size ?? width) == "string" || typeof height == "string";
|
|
295
294
|
const onDrag = (0, react.useCallback)((_x, y) => {
|
|
296
295
|
if (!onChange) return;
|
|
297
|
-
|
|
298
|
-
const v2 = (0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)(v, step), min, max);
|
|
299
|
-
onChange(v2);
|
|
296
|
+
onChange((0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)((0, __tremolo_ui_functions.rawValue)(valueRef.current - y / 100, min, max, skew), step), min, max));
|
|
300
297
|
}, [
|
|
301
298
|
max,
|
|
302
299
|
min,
|
|
@@ -316,10 +313,8 @@ const AnimationKnob = (0, react.forwardRef)(({ value, min, max, defaultValue = m
|
|
|
316
313
|
event.preventDefault();
|
|
317
314
|
const x = key == "ArrowRight" || key == "ArrowUp" ? keyboard[1] : -keyboard[1];
|
|
318
315
|
let v;
|
|
319
|
-
if (keyboard[0] == "normalized")
|
|
320
|
-
|
|
321
|
-
v = (0, __tremolo_ui_functions.rawValue)(n + x, min, max, skew);
|
|
322
|
-
} else v = value + x;
|
|
316
|
+
if (keyboard[0] == "normalized") v = (0, __tremolo_ui_functions.rawValue)((0, __tremolo_ui_functions.normalizeValue)(value, min, max, skew) + x, min, max, skew);
|
|
317
|
+
else v = value + x;
|
|
323
318
|
onChange((0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)(v, step), min, max));
|
|
324
319
|
}
|
|
325
320
|
}, [
|
|
@@ -346,10 +341,8 @@ const AnimationKnob = (0, react.forwardRef)(({ value, min, max, defaultValue = m
|
|
|
346
341
|
event.preventDefault();
|
|
347
342
|
const x = event.deltaY > 0 ? -wheel[1] : wheel[1];
|
|
348
343
|
let v;
|
|
349
|
-
if (wheel[0] == "normalized")
|
|
350
|
-
|
|
351
|
-
v = (0, __tremolo_ui_functions.rawValue)(n + x, min, max, skew);
|
|
352
|
-
} else v = value + x;
|
|
344
|
+
if (wheel[0] == "normalized") v = (0, __tremolo_ui_functions.rawValue)((0, __tremolo_ui_functions.normalizeValue)(value, min, max, skew) + x, min, max, skew);
|
|
345
|
+
else v = value + x;
|
|
353
346
|
if (onChange) onChange((0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)(v, step), min, max));
|
|
354
347
|
}, { passive: false }, [
|
|
355
348
|
wheel,
|
|
@@ -461,20 +454,12 @@ function DragObserver(props) {
|
|
|
461
454
|
*
|
|
462
455
|
* @category Knob
|
|
463
456
|
*/
|
|
464
|
-
const Knob = (0, react.forwardRef)(({ value, min, max, step = 1, skew = 1, defaultValue = min, startValue = min, size = 50, bodyNoSelect = true, wheel = ["raw", 1], keyboard = ["raw", 1], enableDoubleClickDefault = true, disabled = false, readonly = false, activeLine, inactiveLine, thumb, thumbLine, onChange, onKeyDown, onPointerDown, onDoubleClick, className, classes,...props },
|
|
457
|
+
const Knob = (0, react.forwardRef)(({ value, min, max, step = 1, skew = 1, defaultValue = min, startValue = min, size = 50, bodyNoSelect = true, wheel = ["raw", 1], keyboard = ["raw", 1], enableDoubleClickDefault = true, disabled = false, readonly = false, activeLine, inactiveLine, thumb, thumbLine, thumbSize = 84, thumbLineWeight = 6, thumbLineLength = 35, lineWeight = 6, angleRange = 270, onChange, onKeyDown, onPointerDown, onDoubleClick, className, classes,...props }, forwardedRef) => {
|
|
465
458
|
const valueRef = (0, react.useRef)(0);
|
|
466
459
|
const elmRef = (0, react.useRef)(null);
|
|
467
|
-
const padding = 8;
|
|
468
|
-
const thumbLineWeight = 6;
|
|
469
|
-
const thumbLineLength = 35;
|
|
470
|
-
const lineWeight = size * .06;
|
|
471
|
-
const p = (0, __tremolo_ui_functions.normalizeValue)(value, min, max, skew);
|
|
472
|
-
const s = (0, __tremolo_ui_functions.normalizeValue)(startValue, min, max, skew);
|
|
473
460
|
const onDrag = (0, react.useCallback)((_x, y) => {
|
|
474
461
|
if (!onChange || readonly) return;
|
|
475
|
-
|
|
476
|
-
const v2 = (0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)(v, step), min, max);
|
|
477
|
-
onChange(v2);
|
|
462
|
+
onChange((0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)((0, __tremolo_ui_functions.rawValue)(valueRef.current - y / 100, min, max, skew), step), min, max));
|
|
478
463
|
}, [
|
|
479
464
|
max,
|
|
480
465
|
min,
|
|
@@ -485,10 +470,8 @@ const Knob = (0, react.forwardRef)(({ value, min, max, step = 1, skew = 1, defau
|
|
|
485
470
|
]);
|
|
486
471
|
const updateValueByEvent = (0, react.useCallback)((eventType, x) => {
|
|
487
472
|
let newValue;
|
|
488
|
-
if (eventType == "normalized")
|
|
489
|
-
|
|
490
|
-
newValue = (0, __tremolo_ui_functions.rawValue)(n + x, min, max, skew);
|
|
491
|
-
} else newValue = value + x;
|
|
473
|
+
if (eventType == "normalized") newValue = (0, __tremolo_ui_functions.rawValue)((0, __tremolo_ui_functions.normalizeValue)(value, min, max, skew) + x, min, max, skew);
|
|
474
|
+
else newValue = value + x;
|
|
492
475
|
return (0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)(newValue, step), min, max);
|
|
493
476
|
}, [
|
|
494
477
|
max,
|
|
@@ -538,7 +521,7 @@ const Knob = (0, react.forwardRef)(({ value, min, max, step = 1, skew = 1, defau
|
|
|
538
521
|
readonly,
|
|
539
522
|
updateValueByEvent
|
|
540
523
|
]);
|
|
541
|
-
(0, react.useImperativeHandle)(
|
|
524
|
+
(0, react.useImperativeHandle)(forwardedRef, () => {
|
|
542
525
|
return {
|
|
543
526
|
focus() {
|
|
544
527
|
elmRef.current?.focus();
|
|
@@ -548,11 +531,15 @@ const Knob = (0, react.forwardRef)(({ value, min, max, step = 1, skew = 1, defau
|
|
|
548
531
|
}
|
|
549
532
|
};
|
|
550
533
|
}, []);
|
|
551
|
-
const
|
|
552
|
-
const
|
|
553
|
-
const
|
|
554
|
-
const
|
|
555
|
-
const
|
|
534
|
+
const viewBoxSize = 100;
|
|
535
|
+
const center = viewBoxSize / 2;
|
|
536
|
+
const padding = (viewBoxSize - (0, __tremolo_ui_functions.clamp)(thumbSize, 0, 100)) / 2;
|
|
537
|
+
const p = (0, __tremolo_ui_functions.normalizeValue)(value, min, max, skew);
|
|
538
|
+
const s = (0, __tremolo_ui_functions.normalizeValue)(startValue, min, max, skew);
|
|
539
|
+
const r1 = -angleRange / 2;
|
|
540
|
+
const r2 = r1 + Math.min(p, s) * angleRange;
|
|
541
|
+
const r3 = r1 + Math.max(p, s) * angleRange;
|
|
542
|
+
const r4 = angleRange / 2;
|
|
556
543
|
const x1 = center + center * Math.cos((0, __tremolo_ui_functions.radian)(r1 - 90));
|
|
557
544
|
const y1 = center + center * Math.sin((0, __tremolo_ui_functions.radian)(r1 - 90));
|
|
558
545
|
const x2 = center + center * Math.cos((0, __tremolo_ui_functions.radian)(r2 - 90));
|
|
@@ -562,12 +549,13 @@ const Knob = (0, react.forwardRef)(({ value, min, max, step = 1, skew = 1, defau
|
|
|
562
549
|
const x4 = center + center * Math.cos((0, __tremolo_ui_functions.radian)(r4 - 90));
|
|
563
550
|
const y4 = center + center * Math.sin((0, __tremolo_ui_functions.radian)(r4 - 90));
|
|
564
551
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
565
|
-
className: (0, clsx.default)("tremolo-knob", className),
|
|
566
552
|
ref: (div) => {
|
|
567
553
|
elmRef.current = div;
|
|
568
554
|
wheelRefCallback(div);
|
|
569
555
|
touchMoveRefCallback(div);
|
|
570
556
|
},
|
|
557
|
+
className: (0, clsx.default)("tremolo-knob", className),
|
|
558
|
+
viewBox: `0 0 ${viewBoxSize} ${viewBoxSize}`,
|
|
571
559
|
width: size,
|
|
572
560
|
height: size,
|
|
573
561
|
tabIndex: 0,
|
|
@@ -617,7 +605,7 @@ const Knob = (0, react.forwardRef)(({ value, min, max, step = 1, skew = 1, defau
|
|
|
617
605
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
618
606
|
cx: "50%",
|
|
619
607
|
cy: "50%",
|
|
620
|
-
r: `${
|
|
608
|
+
r: `${thumbSize / 2}%`,
|
|
621
609
|
fill: thumb || "currentColor"
|
|
622
610
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
|
|
623
611
|
className: (0, clsx.default)("tremolo-knob-thumb-line", classes?.thumbLine),
|
|
@@ -628,7 +616,7 @@ const Knob = (0, react.forwardRef)(({ value, min, max, step = 1, skew = 1, defau
|
|
|
628
616
|
stroke: thumbLine || "currentColor",
|
|
629
617
|
strokeWidth: `${thumbLineWeight}%`,
|
|
630
618
|
style: {
|
|
631
|
-
transform: `rotate(${
|
|
619
|
+
transform: `rotate(${r1 + p * angleRange}deg)`,
|
|
632
620
|
transformOrigin: "50% 50%"
|
|
633
621
|
}
|
|
634
622
|
})]
|
|
@@ -637,6 +625,47 @@ const Knob = (0, react.forwardRef)(({ value, min, max, step = 1, skew = 1, defau
|
|
|
637
625
|
});
|
|
638
626
|
});
|
|
639
627
|
|
|
628
|
+
//#endregion
|
|
629
|
+
//#region src/components/NumberInput/type.ts
|
|
630
|
+
function selectUnit(units, value) {
|
|
631
|
+
let i = 0;
|
|
632
|
+
for (; i < units.length; i++) if (Math.abs(units[i][1]) > Math.abs(value)) break;
|
|
633
|
+
return units[Math.max(0, i - 1)];
|
|
634
|
+
}
|
|
635
|
+
function parseValue(inputString, units, digit) {
|
|
636
|
+
const str = inputString.trim();
|
|
637
|
+
if (!units || typeof units == "string") {
|
|
638
|
+
const m$1 = str.match(/-?\d+(\.\d+)?/);
|
|
639
|
+
let v = Number(m$1?.[0] ?? "0");
|
|
640
|
+
v = isNaN(v) ? 0 : v;
|
|
641
|
+
return {
|
|
642
|
+
rawValue: v,
|
|
643
|
+
formatValue: (digit != void 0 ? v.toFixed(digit) : v) + (units ?? ""),
|
|
644
|
+
unit: units ?? ""
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
const unitList = units.map(([u, _s]) => u);
|
|
648
|
+
const scaleList = units.map(([_u, s]) => s);
|
|
649
|
+
let rawValue$5 = 0;
|
|
650
|
+
let unit = unitList[0];
|
|
651
|
+
let formatValue = (digit != void 0 ? rawValue$5.toFixed(digit) : rawValue$5) + unit;
|
|
652
|
+
const m = str.match(/^(-?\d+(\.\d+)?)\s*(\w*)$/);
|
|
653
|
+
if (m) {
|
|
654
|
+
rawValue$5 = Number(m[1]) || 0;
|
|
655
|
+
const uIndex = unitList.indexOf(m[3]);
|
|
656
|
+
rawValue$5 *= uIndex != -1 ? scaleList[uIndex] : 1;
|
|
657
|
+
const [u, scale] = selectUnit(units, rawValue$5);
|
|
658
|
+
const v = rawValue$5 / scale;
|
|
659
|
+
formatValue = (digit != void 0 ? v.toFixed(digit) : v) + u;
|
|
660
|
+
unit = u;
|
|
661
|
+
}
|
|
662
|
+
return {
|
|
663
|
+
rawValue: rawValue$5,
|
|
664
|
+
formatValue,
|
|
665
|
+
unit
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
|
|
640
669
|
//#endregion
|
|
641
670
|
//#region src/components/NumberInput/context.tsx
|
|
642
671
|
function safeClamp(value, min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER) {
|
|
@@ -656,14 +685,13 @@ const createNumberInputStore = (initProps) => {
|
|
|
656
685
|
return (0, zustand.createStore)()((set) => ({
|
|
657
686
|
...DEFAULT_PROPS,
|
|
658
687
|
...initProps,
|
|
659
|
-
value:
|
|
660
|
-
valueAsNumber:
|
|
688
|
+
value: parseValue(String(initProps?.value || ""), initProps?.units, initProps?.digit).formatValue,
|
|
689
|
+
valueAsNumber: parseValue(String(initProps?.value || ""), initProps?.units, initProps?.digit).rawValue,
|
|
661
690
|
increment: () => set((state) => {
|
|
662
691
|
if (state.readonly) return {};
|
|
663
|
-
|
|
664
|
-
let next = current + (state.step ?? 1);
|
|
692
|
+
let next = parseValue(state.value, state.units, state.digit).rawValue + (state.step ?? 1);
|
|
665
693
|
if (state.keepWithinRange) next = safeClamp(next, state.min, state.max);
|
|
666
|
-
const v =
|
|
694
|
+
const v = parseValue(String(next), state.units, state.digit).formatValue;
|
|
667
695
|
state?.onChange?.(next, v);
|
|
668
696
|
return {
|
|
669
697
|
value: v,
|
|
@@ -672,10 +700,9 @@ const createNumberInputStore = (initProps) => {
|
|
|
672
700
|
}),
|
|
673
701
|
decrement: () => set((state) => {
|
|
674
702
|
if (state.readonly) return {};
|
|
675
|
-
|
|
676
|
-
let next = current - (state.step ?? 1);
|
|
703
|
+
let next = parseValue(state.value, state.units, state.digit).rawValue - (state.step ?? 1);
|
|
677
704
|
if (state.keepWithinRange) next = safeClamp(next, state.min, state.max);
|
|
678
|
-
const v =
|
|
705
|
+
const v = parseValue(String(next), state.units, state.digit).formatValue;
|
|
679
706
|
state?.onChange?.(next, v);
|
|
680
707
|
return {
|
|
681
708
|
value: v,
|
|
@@ -687,7 +714,7 @@ const createNumberInputStore = (initProps) => {
|
|
|
687
714
|
const v = value ?? "";
|
|
688
715
|
return {
|
|
689
716
|
value: v,
|
|
690
|
-
valueAsNumber:
|
|
717
|
+
valueAsNumber: parseValue(v, state.units, state.digit).rawValue
|
|
691
718
|
};
|
|
692
719
|
})
|
|
693
720
|
}));
|
|
@@ -698,7 +725,7 @@ function NumberInputProvider({ children,...props }) {
|
|
|
698
725
|
if (!storeRef.current) storeRef.current = createNumberInputStore(props);
|
|
699
726
|
(0, react.useEffect)(() => {
|
|
700
727
|
if (storeRef.current) {
|
|
701
|
-
const parsed =
|
|
728
|
+
const parsed = parseValue(String(props?.value || ""), props?.units, props?.digit);
|
|
702
729
|
storeRef.current.setState({
|
|
703
730
|
...props,
|
|
704
731
|
value: parsed.formatValue,
|
|
@@ -745,8 +772,7 @@ const InternalInput = (0, react.forwardRef)(({ disabled, readonly, selectWithFoc
|
|
|
745
772
|
let newValue;
|
|
746
773
|
if (eventType == "normalized") {
|
|
747
774
|
if (!min || !max) throw new Error("required parameter: min, max");
|
|
748
|
-
|
|
749
|
-
newValue = (0, __tremolo_ui_functions.rawValue)(n + x, min, max);
|
|
775
|
+
newValue = (0, __tremolo_ui_functions.rawValue)((0, __tremolo_ui_functions.normalizeValue)(valueAsNumber, min, max) + x, min, max);
|
|
750
776
|
return (0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)(newValue, step), min, max);
|
|
751
777
|
} else {
|
|
752
778
|
newValue = valueAsNumber + x;
|
|
@@ -764,7 +790,7 @@ const InternalInput = (0, react.forwardRef)(({ disabled, readonly, selectWithFoc
|
|
|
764
790
|
if (!["ArrowUp", "ArrowDown"].includes(key)) return;
|
|
765
791
|
event.preventDefault();
|
|
766
792
|
const x = key == "ArrowUp" ? keyboard[1] : -keyboard[1];
|
|
767
|
-
const parsed =
|
|
793
|
+
const parsed = parseValue(String(updateValueByEvent(keyboard[0], x)));
|
|
768
794
|
onChange?.(parsed.rawValue, parsed.formatValue);
|
|
769
795
|
}, [
|
|
770
796
|
keyboard,
|
|
@@ -777,7 +803,7 @@ const InternalInput = (0, react.forwardRef)(({ disabled, readonly, selectWithFoc
|
|
|
777
803
|
event.preventDefault();
|
|
778
804
|
if (!onChange || event.deltaY == 0) return;
|
|
779
805
|
const x = Math.sign(event.deltaY) * -wheel[1];
|
|
780
|
-
const parsed =
|
|
806
|
+
const parsed = parseValue(String(updateValueByEvent(wheel[0], x)));
|
|
781
807
|
onChange?.(parsed.rawValue, parsed.formatValue);
|
|
782
808
|
}, { passive: false }, [
|
|
783
809
|
wheel,
|
|
@@ -815,7 +841,7 @@ const InternalInput = (0, react.forwardRef)(({ disabled, readonly, selectWithFoc
|
|
|
815
841
|
const v = event.currentTarget.value;
|
|
816
842
|
const cursorPos = event.target.selectionStart;
|
|
817
843
|
change(v);
|
|
818
|
-
const valueAsNumber$1 =
|
|
844
|
+
const valueAsNumber$1 = parseValue(v, units, digit).rawValue;
|
|
819
845
|
onChange?.(valueAsNumber$1, v);
|
|
820
846
|
if (cursorPos) {
|
|
821
847
|
event.target.selectionStart = cursorPos;
|
|
@@ -825,16 +851,16 @@ const InternalInput = (0, react.forwardRef)(({ disabled, readonly, selectWithFoc
|
|
|
825
851
|
onFocus: (event) => {
|
|
826
852
|
setEditing(true);
|
|
827
853
|
const v = event.currentTarget.value;
|
|
828
|
-
const parsed =
|
|
854
|
+
const parsed = parseValue(v, units, digit);
|
|
829
855
|
if (selectWithFocus == "all") event.currentTarget.setSelectionRange(0, v.length);
|
|
830
856
|
else if (selectWithFocus == "number") event.currentTarget.setSelectionRange(0, parsed.formatValue.length - parsed.unit.length);
|
|
831
857
|
onFocus?.(parsed.rawValue, parsed.formatValue, event);
|
|
832
858
|
},
|
|
833
859
|
onBlur: (event) => {
|
|
834
860
|
setEditing(false);
|
|
835
|
-
let v =
|
|
861
|
+
let v = parseValue(event.currentTarget.value, units, digit).rawValue;
|
|
836
862
|
if (clampValueOnBlur) v = safeClamp(v, min, max);
|
|
837
|
-
const parsed =
|
|
863
|
+
const parsed = parseValue(String(v), units, digit);
|
|
838
864
|
change(parsed.formatValue);
|
|
839
865
|
if (clampValueOnBlur) onChange?.(parsed.rawValue, parsed.formatValue);
|
|
840
866
|
onBlur?.(parsed.rawValue, parsed.formatValue, event);
|
|
@@ -862,6 +888,9 @@ function Stepper({ dynamic = true, children, className,...props }) {
|
|
|
862
888
|
|
|
863
889
|
//#endregion
|
|
864
890
|
//#region src/hooks/useInterval.ts
|
|
891
|
+
/**
|
|
892
|
+
* @category hooks
|
|
893
|
+
*/
|
|
865
894
|
function useInterval(callback, delay) {
|
|
866
895
|
const savedCallback = (0, react.useRef)(callback);
|
|
867
896
|
(0, react.useEffect)(() => {
|
|
@@ -880,6 +909,9 @@ function useInterval(callback, delay) {
|
|
|
880
909
|
|
|
881
910
|
//#endregion
|
|
882
911
|
//#region src/hooks/useLongPress.ts
|
|
912
|
+
/**
|
|
913
|
+
* @category hooks
|
|
914
|
+
*/
|
|
883
915
|
function useLongPress(callback, initialDelay = 500, interval = 40) {
|
|
884
916
|
const [pressed, setPressed] = (0, react.useState)(false);
|
|
885
917
|
const [delay, setDelay] = (0, react.useState)(initialDelay);
|
|
@@ -905,8 +937,7 @@ function IncrementStepper({ size = 12, children, className,...props }) {
|
|
|
905
937
|
const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
|
|
906
938
|
const readonly = useNumberInputContext((s) => s.readonly);
|
|
907
939
|
const keepWithinRange = useNumberInputContext((s) => s.keepWithinRange);
|
|
908
|
-
const
|
|
909
|
-
const press = useLongPress(increment);
|
|
940
|
+
const press = useLongPress(useNumberInputContext((s) => s.increment));
|
|
910
941
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
911
942
|
className: (0, clsx.default)("tremolo-number-input-increment-stepper", className),
|
|
912
943
|
role: "button",
|
|
@@ -937,8 +968,7 @@ function DecrementStepper({ size = 12, children, className,...props }) {
|
|
|
937
968
|
const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
|
|
938
969
|
const readonly = useNumberInputContext((s) => s.readonly);
|
|
939
970
|
const keepWithinRange = useNumberInputContext((s) => s.keepWithinRange);
|
|
940
|
-
const
|
|
941
|
-
const press = useLongPress(decrement);
|
|
971
|
+
const press = useLongPress(useNumberInputContext((s) => s.decrement));
|
|
942
972
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
943
973
|
className: (0, clsx.default)("tremolo-number-input-decrement-stepper", className),
|
|
944
974
|
role: "button",
|
|
@@ -967,7 +997,7 @@ function DecrementStepper({ size = 12, children, className,...props }) {
|
|
|
967
997
|
* Input with some useful functions for entering numerical values.
|
|
968
998
|
* @category NumberInput
|
|
969
999
|
*/
|
|
970
|
-
const NumberInput = (0, react.forwardRef)(({ value, min, max, step = 1, units, readonly = false, disabled = false, digit, variant = "outline", selectWithFocus = "none", blurOnEnter = true, keepWithinRange = true, clampValueOnBlur = true, wheel = ["raw", 1], keyboard = ["raw", 1], activeColor, wrapperClassName, className, onChange, onFocus, onBlur, children,...props },
|
|
1000
|
+
const NumberInput = (0, react.forwardRef)(({ value, min, max, step = 1, units, readonly = false, disabled = false, digit, variant = "outline", selectWithFocus = "none", blurOnEnter = true, keepWithinRange = true, clampValueOnBlur = true, wheel = ["raw", 1], keyboard = ["raw", 1], activeColor, wrapperClassName, className, onChange, onFocus, onBlur, children,...props }, forwardedRef) => {
|
|
971
1001
|
const colors = { "--active-color": activeColor };
|
|
972
1002
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NumberInputProvider, {
|
|
973
1003
|
value: String(value),
|
|
@@ -986,7 +1016,7 @@ const NumberInput = (0, react.forwardRef)(({ value, min, max, step = 1, units, r
|
|
|
986
1016
|
"data-stepper": !!children,
|
|
987
1017
|
"data-variant": variant,
|
|
988
1018
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InternalInput, {
|
|
989
|
-
ref,
|
|
1019
|
+
ref: forwardedRef,
|
|
990
1020
|
readonly,
|
|
991
1021
|
disabled,
|
|
992
1022
|
selectWithFocus,
|
|
@@ -1007,6 +1037,7 @@ const NumberInput = (0, react.forwardRef)(({ value, min, max, step = 1, units, r
|
|
|
1007
1037
|
//#endregion
|
|
1008
1038
|
//#region src/hooks/useDragWithElement.ts
|
|
1009
1039
|
/**
|
|
1040
|
+
* @category hooks
|
|
1010
1041
|
* @returns [refCallback, pointerDownHandler]
|
|
1011
1042
|
*/
|
|
1012
1043
|
function useDragWithElement({ baseElementRef, onDrag, onDragStart, onDragEnd }) {
|
|
@@ -1086,9 +1117,7 @@ function usePianoContext(selector) {
|
|
|
1086
1117
|
//#region src/components/Piano/KeyLabel.tsx
|
|
1087
1118
|
/** @category Piano */
|
|
1088
1119
|
function KeyLabel({ label, className, wrapperClassName, wrapperStyle, __note, __label,...props }) {
|
|
1089
|
-
const
|
|
1090
|
-
const noteRangeArray = getNoteRangeArray(noteRange);
|
|
1091
|
-
const index = noteRangeArray.indexOf(__note);
|
|
1120
|
+
const index = getNoteRangeArray(usePianoContext((s) => s.noteRange)).indexOf(__note);
|
|
1092
1121
|
const content = label ? label(__note, index) : __label && __label(__note, index);
|
|
1093
1122
|
return (content != void 0 || content != null) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1094
1123
|
className: (0, clsx.default)("tremolo-piano-key-label-wrapper", wrapperClassName),
|
|
@@ -1114,8 +1143,7 @@ const KeyImpl = (0, react.forwardRef)(({ keyType, noteNumber, width, height, bg,
|
|
|
1114
1143
|
const onPlayNote = usePianoContext((s) => s.onPlayNote);
|
|
1115
1144
|
const onStopNote = usePianoContext((s) => s.onStopNote);
|
|
1116
1145
|
const label = usePianoContext((s) => s.label);
|
|
1117
|
-
const
|
|
1118
|
-
const position = notePosition(noteNumber);
|
|
1146
|
+
const position = usePianoContext((s) => s.notePosition)(noteNumber);
|
|
1119
1147
|
const disabled = noteNumber > midiMax;
|
|
1120
1148
|
const colors = {
|
|
1121
1149
|
"--bg": bg,
|
|
@@ -1125,10 +1153,10 @@ const KeyImpl = (0, react.forwardRef)(({ keyType, noteNumber, width, height, bg,
|
|
|
1125
1153
|
};
|
|
1126
1154
|
(0, react.useImperativeHandle)(ref, () => {
|
|
1127
1155
|
return {
|
|
1128
|
-
play() {
|
|
1156
|
+
play(velocity) {
|
|
1129
1157
|
if (disabled) return;
|
|
1130
1158
|
setPlayed(true);
|
|
1131
|
-
if (onPlayNote) onPlayNote(noteNumber);
|
|
1159
|
+
if (onPlayNote) onPlayNote(noteNumber, velocity);
|
|
1132
1160
|
},
|
|
1133
1161
|
stop() {
|
|
1134
1162
|
setPlayed(false);
|
|
@@ -1219,12 +1247,9 @@ const SHORTCUTS = { HOME_ROW: { keys: [
|
|
|
1219
1247
|
//#endregion
|
|
1220
1248
|
//#region src/components/Piano/index.tsx
|
|
1221
1249
|
/**
|
|
1222
|
-
*
|
|
1223
|
-
*
|
|
1224
|
-
* TODO:
|
|
1225
|
-
* - add scale highlight
|
|
1250
|
+
* [noteRange.first, noteRange.first + 1 ..., noteRange.last]
|
|
1251
|
+
* @category Piano
|
|
1226
1252
|
*/
|
|
1227
|
-
/** @category Piano */
|
|
1228
1253
|
function getNoteRangeArray(noteRange) {
|
|
1229
1254
|
return Array.from({ length: noteRange.last - noteRange.first + 1 }, (_, i) => i + noteRange.first);
|
|
1230
1255
|
}
|
|
@@ -1233,7 +1258,7 @@ const blackPerWhiteWidth = defaultBlackKeyWidth / defaultWhiteKeyWidth;
|
|
|
1233
1258
|
* Customizable piano component.
|
|
1234
1259
|
* @category Piano
|
|
1235
1260
|
*/
|
|
1236
|
-
|
|
1261
|
+
const Piano = (0, react.forwardRef)(({ noteRange, glissando = true, midiMax = 127, keyboardShortcuts, fill = false, height = fill ? "100%" : 160, whiteNoteWidth: _whiteNoteWidth = defaultWhiteKeyWidth, style, className, onPlayNote, onStopNote, label, children, onPointerDown,...props }, forwardedRef) => {
|
|
1237
1262
|
const [whiteNoteWidth, setWhiteNoteWidth] = (0, react.useState)(_whiteNoteWidth);
|
|
1238
1263
|
const keyRefs = (0, react.useRef)([]);
|
|
1239
1264
|
for (let i = 0; i < noteRange.last - noteRange.first + 1; i++) keyRefs.current[i] = (0, react.createRef)();
|
|
@@ -1304,9 +1329,7 @@ function Piano({ noteRange, glissando = true, midiMax = 127, keyboardShortcuts,
|
|
|
1304
1329
|
]);
|
|
1305
1330
|
const onDrag = (0, react.useCallback)((perX, perY) => {
|
|
1306
1331
|
if (!pianoRef.current) return;
|
|
1307
|
-
const
|
|
1308
|
-
const y = perY * pianoRef.current.clientHeight;
|
|
1309
|
-
const note = getHitKeyIndex(x, y);
|
|
1332
|
+
const note = getHitKeyIndex(perX * staticWidth, perY * pianoRef.current.clientHeight);
|
|
1310
1333
|
const index = noteRangeArray.indexOf(note);
|
|
1311
1334
|
if (index == -1) return;
|
|
1312
1335
|
if (hitKeyIndex.current != index) {
|
|
@@ -1360,6 +1383,25 @@ function Piano({ noteRange, glissando = true, midiMax = 127, keyboardShortcuts,
|
|
|
1360
1383
|
const index = keyboardShortcuts.keys.indexOf(e.key);
|
|
1361
1384
|
if (index != -1) keyRefs.current[index]?.current?.stop();
|
|
1362
1385
|
});
|
|
1386
|
+
(0, react.useImperativeHandle)(forwardedRef, () => {
|
|
1387
|
+
return {
|
|
1388
|
+
playNote(note, velocity) {
|
|
1389
|
+
const index = noteRangeArray.indexOf(note);
|
|
1390
|
+
if (index != -1) {
|
|
1391
|
+
if (!keyRefs.current[index]?.current?.played()) keyRefs.current[index]?.current?.play(velocity);
|
|
1392
|
+
} else onPlayNote?.(note, velocity);
|
|
1393
|
+
},
|
|
1394
|
+
stopNote(note) {
|
|
1395
|
+
const index = noteRangeArray.indexOf(note);
|
|
1396
|
+
if (index != -1) keyRefs.current[index]?.current?.stop();
|
|
1397
|
+
else onStopNote?.(note);
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1400
|
+
}, [
|
|
1401
|
+
noteRangeArray,
|
|
1402
|
+
onPlayNote,
|
|
1403
|
+
onStopNote
|
|
1404
|
+
]);
|
|
1363
1405
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PianoProvider, {
|
|
1364
1406
|
notePosition,
|
|
1365
1407
|
noteRange,
|
|
@@ -1396,7 +1438,7 @@ function Piano({ noteRange, glissando = true, midiMax = 127, keyboardShortcuts,
|
|
|
1396
1438
|
}, note))
|
|
1397
1439
|
})
|
|
1398
1440
|
});
|
|
1399
|
-
}
|
|
1441
|
+
});
|
|
1400
1442
|
|
|
1401
1443
|
//#endregion
|
|
1402
1444
|
//#region src/components/Slider/context.tsx
|
|
@@ -1484,6 +1526,22 @@ function ScaleOption({ value, type = "mark-number", label, thickness = 1, length
|
|
|
1484
1526
|
});
|
|
1485
1527
|
}
|
|
1486
1528
|
|
|
1529
|
+
//#endregion
|
|
1530
|
+
//#region src/components/Slider/type.ts
|
|
1531
|
+
function generateOptionsList(options, min, max, step) {
|
|
1532
|
+
const optionsList = [];
|
|
1533
|
+
const per = options[0] == "step" ? step : options[0];
|
|
1534
|
+
const count = Math.floor(max / per) - Math.ceil(min / per) + 1;
|
|
1535
|
+
for (let i = 0; i < count; i++) {
|
|
1536
|
+
const value = (0, __tremolo_ui_functions.toFixed)(per * (Math.ceil(min / per) + i), (0, __tremolo_ui_functions.decimalPart)(per)?.length);
|
|
1537
|
+
optionsList.push({
|
|
1538
|
+
value,
|
|
1539
|
+
type: options[1]
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
return optionsList;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1487
1545
|
//#endregion
|
|
1488
1546
|
//#region src/components/Slider/Scale.tsx
|
|
1489
1547
|
/** @category Slider */
|
|
@@ -1493,7 +1551,7 @@ function Scale({ gap = 6, options, children, className, style,...props }) {
|
|
|
1493
1551
|
const step = useSliderContext((s) => s.step);
|
|
1494
1552
|
const vertical = useSliderContext((s) => s.vertical);
|
|
1495
1553
|
const reverse = useSliderContext((s) => s.reverse);
|
|
1496
|
-
const optionsList = options ?
|
|
1554
|
+
const optionsList = options ? generateOptionsList(options, min, max, step) : [];
|
|
1497
1555
|
if ((0, __tremolo_ui_functions.xor)(vertical, reverse)) optionsList.reverse();
|
|
1498
1556
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1499
1557
|
className: (0, clsx.default)("tremolo-slider-scale", className),
|
|
@@ -1574,7 +1632,7 @@ function SliderTrack({ length = defaultLength, thickness = defaultThickness, act
|
|
|
1574
1632
|
"data-vertical": vertical,
|
|
1575
1633
|
style: !defaultStyle ? style : {
|
|
1576
1634
|
...colors,
|
|
1577
|
-
background: (0, __tremolo_ui_functions.xor)(vertical, reverse) ? `linear-gradient(to ${direction}, var(--inactive
|
|
1635
|
+
background: (0, __tremolo_ui_functions.xor)(vertical, reverse) ? `linear-gradient(to ${direction}, var(--inactive) ${__percent}%, var(--active) ${__percent}%)` : `linear-gradient(to ${direction}, var(--active) ${__percent}%, var(--inactive) ${__percent}%)`,
|
|
1578
1636
|
borderRadius: (0, __tremolo_ui_functions.styleHelper)(thickness, "/", 2),
|
|
1579
1637
|
width: !vertical ? length : thickness,
|
|
1580
1638
|
height: vertical ? length : thickness,
|
|
@@ -1591,7 +1649,7 @@ function SliderTrack({ length = defaultLength, thickness = defaultThickness, act
|
|
|
1591
1649
|
* Customizable slider
|
|
1592
1650
|
* @category Slider
|
|
1593
1651
|
*/
|
|
1594
|
-
const Slider = (0, react.forwardRef)(({ value, min, max, step = 1, skew = 1, vertical = false, reverse = false, bodyNoSelect = true, wheel = ["raw", 1], keyboard = ["raw", 1], disabled = false, readonly = false, onChange, onDragStart, onDragEnd, className, style, children, onFocus, onBlur, onPointerDown, onKeyDown,...props },
|
|
1652
|
+
const Slider = (0, react.forwardRef)(({ value, min, max, step = 1, skew = 1, vertical = false, reverse = false, bodyNoSelect = true, wheel = ["raw", 1], keyboard = ["raw", 1], disabled = false, readonly = false, onChange, onDragStart, onDragEnd, className, style, children, onFocus, onBlur, onPointerDown, onKeyDown,...props }, forwardedRef) => {
|
|
1595
1653
|
const trackElementRef = (0, react.useRef)(null);
|
|
1596
1654
|
const thumbRef = (0, react.useRef)(null);
|
|
1597
1655
|
const p = (0, __tremolo_ui_functions.toFixed)((0, __tremolo_ui_functions.normalizeValue)(value, min, max, skew) * 100);
|
|
@@ -1611,9 +1669,7 @@ const Slider = (0, react.forwardRef)(({ value, min, max, step = 1, skew = 1, ver
|
|
|
1611
1669
|
const onDrag = (0, react.useCallback)((nx, ny) => {
|
|
1612
1670
|
if (!onChange || readonly) return;
|
|
1613
1671
|
const n = vertical ? ny : nx;
|
|
1614
|
-
|
|
1615
|
-
const v2 = (0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)(v, step), min, max);
|
|
1616
|
-
onChange(v2);
|
|
1672
|
+
onChange((0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)((0, __tremolo_ui_functions.rawValue)(displayReversed ? 1 - n : n, min, max, skew), step), min, max));
|
|
1617
1673
|
}, [
|
|
1618
1674
|
displayReversed,
|
|
1619
1675
|
max,
|
|
@@ -1626,10 +1682,8 @@ const Slider = (0, react.forwardRef)(({ value, min, max, step = 1, skew = 1, ver
|
|
|
1626
1682
|
]);
|
|
1627
1683
|
const updateValueByEvent = (0, react.useCallback)((eventType, x) => {
|
|
1628
1684
|
let newValue;
|
|
1629
|
-
if (eventType == "normalized")
|
|
1630
|
-
|
|
1631
|
-
newValue = (0, __tremolo_ui_functions.rawValue)(n + x, min, max, skew);
|
|
1632
|
-
} else newValue = value + x;
|
|
1685
|
+
if (eventType == "normalized") newValue = (0, __tremolo_ui_functions.rawValue)((0, __tremolo_ui_functions.normalizeValue)(value, min, max, skew) + x, min, max, skew);
|
|
1686
|
+
else newValue = value + x;
|
|
1633
1687
|
return (0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)(newValue, step), min, max);
|
|
1634
1688
|
}, [
|
|
1635
1689
|
max,
|
|
@@ -1692,7 +1746,7 @@ const Slider = (0, react.forwardRef)(({ value, min, max, step = 1, skew = 1, ver
|
|
|
1692
1746
|
onChange,
|
|
1693
1747
|
updateValueByEvent
|
|
1694
1748
|
]);
|
|
1695
|
-
(0, react.useImperativeHandle)(
|
|
1749
|
+
(0, react.useImperativeHandle)(forwardedRef, () => {
|
|
1696
1750
|
return {
|
|
1697
1751
|
focus() {
|
|
1698
1752
|
thumbRef.current?.focus();
|
|
@@ -1776,11 +1830,10 @@ const Slider = (0, react.forwardRef)(({ value, min, max, step = 1, skew = 1, ver
|
|
|
1776
1830
|
/** @category WheelObserver */
|
|
1777
1831
|
function WheelObserver(props) {
|
|
1778
1832
|
const { children, onWheel, as: Component = "div",...attributes } = props;
|
|
1779
|
-
const wheelRefCallback = useRefCallbackEvent("wheel", (event) => {
|
|
1780
|
-
if (onWheel) onWheel(event);
|
|
1781
|
-
}, { passive: false }, [onWheel]);
|
|
1782
1833
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, {
|
|
1783
|
-
ref:
|
|
1834
|
+
ref: useRefCallbackEvent("wheel", (event) => {
|
|
1835
|
+
if (onWheel) onWheel(event);
|
|
1836
|
+
}, { passive: false }, [onWheel]),
|
|
1784
1837
|
...attributes,
|
|
1785
1838
|
children
|
|
1786
1839
|
});
|
|
@@ -1853,7 +1906,7 @@ const defaultValueOptions = {
|
|
|
1853
1906
|
* Simple XYPad
|
|
1854
1907
|
* @category XYPad
|
|
1855
1908
|
*/
|
|
1856
|
-
const XYPad = (0, react.forwardRef)(({ x: _x, y: _y, className, style, bodyNoSelect = true, disabled = false, readonly = false, onChange, onDragStart, onDragEnd, onPointerDown, onKeyDown, onFocus, onBlur, children,...props },
|
|
1909
|
+
const XYPad = (0, react.forwardRef)(({ x: _x, y: _y, className, style, bodyNoSelect = true, disabled = false, readonly = false, onChange, onDragStart, onDragEnd, onPointerDown, onKeyDown, onFocus, onBlur, children,...props }, forwardedRef) => {
|
|
1857
1910
|
const x = (0, react.useMemo)(() => {
|
|
1858
1911
|
return {
|
|
1859
1912
|
...defaultValueOptions,
|
|
@@ -1884,9 +1937,7 @@ const XYPad = (0, react.forwardRef)(({ x: _x, y: _y, className, style, bodyNoSel
|
|
|
1884
1937
|
if (!onChange || readonly) return;
|
|
1885
1938
|
const vx = (0, __tremolo_ui_functions.rawValue)(x.reverse ? 1 - nx$1 : nx$1, x.min, x.max, x.skew);
|
|
1886
1939
|
const vy = (0, __tremolo_ui_functions.rawValue)(y.reverse ? 1 - ny$1 : ny$1, y.min, y.max, y.skew);
|
|
1887
|
-
|
|
1888
|
-
const newY = (0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)(vy, y.step), y.min, y.max);
|
|
1889
|
-
onChange(newX, newY);
|
|
1940
|
+
onChange((0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)(vx, x.step), x.min, x.max), (0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)(vy, y.step), y.min, y.max));
|
|
1890
1941
|
}, [
|
|
1891
1942
|
onChange,
|
|
1892
1943
|
readonly,
|
|
@@ -1903,10 +1954,8 @@ const XYPad = (0, react.forwardRef)(({ x: _x, y: _y, className, style, bodyNoSel
|
|
|
1903
1954
|
]);
|
|
1904
1955
|
const updateValueByEvent = (0, react.useCallback)((eventType, target, x$1) => {
|
|
1905
1956
|
let v;
|
|
1906
|
-
if (eventType == "normalized")
|
|
1907
|
-
|
|
1908
|
-
v = (0, __tremolo_ui_functions.rawValue)(n + x$1, target.min, target.max, target.skew);
|
|
1909
|
-
} else v = target.value + x$1;
|
|
1957
|
+
if (eventType == "normalized") v = (0, __tremolo_ui_functions.rawValue)((0, __tremolo_ui_functions.normalizeValue)(target.value, target.min, target.max, target.skew) + x$1, target.min, target.max, target.skew);
|
|
1958
|
+
else v = target.value + x$1;
|
|
1910
1959
|
return (0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)(v, target.step), target.min, target.max);
|
|
1911
1960
|
}, []);
|
|
1912
1961
|
const handleKeyDown = (0, react.useCallback)((event) => {
|
|
@@ -1973,7 +2022,7 @@ const XYPad = (0, react.forwardRef)(({ x: _x, y: _y, className, style, bodyNoSel
|
|
|
1973
2022
|
readonly,
|
|
1974
2023
|
updateValueByEvent
|
|
1975
2024
|
]);
|
|
1976
|
-
(0, react.useImperativeHandle)(
|
|
2025
|
+
(0, react.useImperativeHandle)(forwardedRef, () => {
|
|
1977
2026
|
return {
|
|
1978
2027
|
focus() {
|
|
1979
2028
|
thumbRef.current?.focus();
|
|
@@ -2036,6 +2085,9 @@ const XYPad = (0, react.forwardRef)(({ x: _x, y: _y, className, style, bodyNoSel
|
|
|
2036
2085
|
|
|
2037
2086
|
//#endregion
|
|
2038
2087
|
//#region src/hooks/useAnimationFrame.ts
|
|
2088
|
+
/**
|
|
2089
|
+
* @category hooks
|
|
2090
|
+
*/
|
|
2039
2091
|
function useAnimationFrame(callback = () => {}, deps = []) {
|
|
2040
2092
|
const reqIdRef = (0, react.useRef)(-1);
|
|
2041
2093
|
const loop = (0, react.useCallback)(() => {
|
|
@@ -2048,6 +2100,79 @@ function useAnimationFrame(callback = () => {}, deps = []) {
|
|
|
2048
2100
|
}, [loop]);
|
|
2049
2101
|
}
|
|
2050
2102
|
|
|
2103
|
+
//#endregion
|
|
2104
|
+
//#region src/hooks/useMIDIAccess.ts
|
|
2105
|
+
/** @private */
|
|
2106
|
+
const PERMISSION_DENIED = "PERMISSION_DENIED";
|
|
2107
|
+
/** @private */
|
|
2108
|
+
const NOT_SUPPORTED = "NOT_SUPPORTED";
|
|
2109
|
+
/**
|
|
2110
|
+
* Hooks for requesting MIDI access in the browser. The first argument allows you to choose whether to request access on mount.
|
|
2111
|
+
* @category hooks
|
|
2112
|
+
*/
|
|
2113
|
+
function useMIDIAccess(requestOnMount = true) {
|
|
2114
|
+
const [midiAccess, setMidiAccess] = (0, react.useState)(null);
|
|
2115
|
+
const [error, setError] = (0, react.useState)(null);
|
|
2116
|
+
const request = (0, react.useCallback)(() => {
|
|
2117
|
+
if (navigator.requestMIDIAccess) navigator.requestMIDIAccess().then((access) => {
|
|
2118
|
+
setMidiAccess(access);
|
|
2119
|
+
setError(null);
|
|
2120
|
+
}).catch(() => {
|
|
2121
|
+
setError(PERMISSION_DENIED);
|
|
2122
|
+
});
|
|
2123
|
+
else setError(NOT_SUPPORTED);
|
|
2124
|
+
}, []);
|
|
2125
|
+
(0, react.useEffect)(() => {
|
|
2126
|
+
if (requestOnMount) request();
|
|
2127
|
+
}, [requestOnMount, request]);
|
|
2128
|
+
return {
|
|
2129
|
+
request,
|
|
2130
|
+
midiAccess,
|
|
2131
|
+
error
|
|
2132
|
+
};
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
//#endregion
|
|
2136
|
+
//#region src/hooks/useMIDIMessage.ts
|
|
2137
|
+
/**
|
|
2138
|
+
* Hooks for when you want to process MIDI events in more detail than useMIDIInput.
|
|
2139
|
+
* @category hooks
|
|
2140
|
+
*/
|
|
2141
|
+
function useMIDIMessage(midiAccess, onMIDIMessage) {
|
|
2142
|
+
(0, react.useEffect)(() => {
|
|
2143
|
+
if (!midiAccess) return;
|
|
2144
|
+
for (const input of midiAccess.inputs.values()) input.addEventListener("midimessage", onMIDIMessage);
|
|
2145
|
+
return () => {
|
|
2146
|
+
for (const input of midiAccess.inputs.values()) input.removeEventListener("midimessage", onMIDIMessage);
|
|
2147
|
+
};
|
|
2148
|
+
}, [midiAccess, onMIDIMessage]);
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
//#endregion
|
|
2152
|
+
//#region src/hooks/useMIDIInput.ts
|
|
2153
|
+
const MIDI_EVENT_TO_NUMBER = {
|
|
2154
|
+
NOTE_ON: 144,
|
|
2155
|
+
NOTE_OFF: 128,
|
|
2156
|
+
PITCH_BEND: 224
|
|
2157
|
+
};
|
|
2158
|
+
/**
|
|
2159
|
+
* Hooks for handling note on/off events. To be used with useMIDIAccess. Internally uses useMIDIMessage.
|
|
2160
|
+
* @category hooks
|
|
2161
|
+
*/
|
|
2162
|
+
function useMIDIInput(midiAccess, onNoteOnEvent, onNoteOffEvent, onPitchBendEvent) {
|
|
2163
|
+
useMIDIMessage(midiAccess, (0, react.useCallback)((event) => {
|
|
2164
|
+
if (!event.data) return;
|
|
2165
|
+
const kind = event.data[0] & 240;
|
|
2166
|
+
if (kind == MIDI_EVENT_TO_NUMBER.NOTE_OFF || kind == MIDI_EVENT_TO_NUMBER.NOTE_ON && event.data[2] == 0) onNoteOffEvent?.(event.data[1]);
|
|
2167
|
+
else if (kind == MIDI_EVENT_TO_NUMBER.NOTE_ON) onNoteOnEvent?.(event.data[1], event.data[2]);
|
|
2168
|
+
else if (kind == MIDI_EVENT_TO_NUMBER.PITCH_BEND) onPitchBendEvent?.(event.data[1], event.data[2]);
|
|
2169
|
+
}, [
|
|
2170
|
+
onNoteOffEvent,
|
|
2171
|
+
onNoteOnEvent,
|
|
2172
|
+
onPitchBendEvent
|
|
2173
|
+
]));
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2051
2176
|
//#endregion
|
|
2052
2177
|
exports.AnimationCanvas = AnimationCanvas;
|
|
2053
2178
|
exports.AnimationKnob = AnimationKnob;
|
|
@@ -2057,7 +2182,9 @@ exports.DragObserver = DragObserver;
|
|
|
2057
2182
|
exports.IncrementStepper = IncrementStepper;
|
|
2058
2183
|
exports.KeyLabel = KeyLabel;
|
|
2059
2184
|
exports.Knob = Knob;
|
|
2185
|
+
exports.NOT_SUPPORTED = NOT_SUPPORTED;
|
|
2060
2186
|
exports.NumberInput = NumberInput;
|
|
2187
|
+
exports.PERMISSION_DENIED = PERMISSION_DENIED;
|
|
2061
2188
|
exports.Piano = Piano;
|
|
2062
2189
|
exports.SHORTCUTS = SHORTCUTS;
|
|
2063
2190
|
exports.Scale = Scale;
|
|
@@ -2078,5 +2205,8 @@ exports.useDragWithElement = useDragWithElement;
|
|
|
2078
2205
|
exports.useEventListener = useEventListener;
|
|
2079
2206
|
exports.useInterval = useInterval;
|
|
2080
2207
|
exports.useLongPress = useLongPress;
|
|
2208
|
+
exports.useMIDIAccess = useMIDIAccess;
|
|
2209
|
+
exports.useMIDIInput = useMIDIInput;
|
|
2210
|
+
exports.useMIDIMessage = useMIDIMessage;
|
|
2081
2211
|
exports.useSliderContext = useSliderContext;
|
|
2082
2212
|
//# sourceMappingURL=index.cjs.map
|