@tremolo-ui/react 0.1.6 → 0.2.1
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 +1329 -1152
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +238 -183
- package/dist/index.d.cts +367 -366
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +367 -366
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1313 -1125
- package/dist/index.js.map +1 -1
- package/package.json +17 -19
- package/src/components/AnimationCanvas/index.tsx +0 -7
- package/src/components/DragObserver/index.tsx +9 -7
- package/src/components/Knob/ActiveLine.tsx +29 -0
- package/src/components/Knob/InactiveLine.tsx +53 -0
- package/src/components/Knob/SVGRoot.tsx +39 -0
- package/src/components/Knob/Thumb.tsx +64 -0
- package/src/components/Knob/context.tsx +120 -0
- package/src/components/Knob/index.css +25 -27
- package/src/components/Knob/index.tsx +110 -164
- package/src/components/NumberInput/DecrementStepper.tsx +0 -2
- package/src/components/NumberInput/IncrementStepper.tsx +0 -2
- package/src/components/NumberInput/InternalInput.tsx +3 -1
- package/src/components/NumberInput/Stepper.tsx +0 -2
- package/src/components/NumberInput/context.tsx +0 -1
- package/src/components/NumberInput/index.css +31 -15
- package/src/components/NumberInput/index.tsx +21 -14
- package/src/components/Piano/KeyLabel.tsx +0 -2
- package/src/components/Piano/context.tsx +0 -2
- package/src/components/Piano/index.css +7 -14
- package/src/components/Piano/index.tsx +14 -15
- package/src/components/Piano/key.tsx +0 -10
- package/src/components/Piano/keyboardShortcuts.ts +0 -2
- package/src/components/PointsEditor/Background.tsx +17 -0
- package/src/components/PointsEditor/Container.tsx +26 -0
- package/src/components/PointsEditor/Point.tsx +137 -0
- package/src/components/PointsEditor/context.tsx +91 -0
- package/src/components/PointsEditor/index.css +30 -0
- package/src/components/PointsEditor/index.tsx +129 -0
- package/src/components/Slider/Scale.tsx +0 -2
- package/src/components/Slider/ScaleOption.tsx +0 -2
- package/src/components/Slider/Thumb.tsx +1 -5
- package/src/components/Slider/Track.tsx +1 -3
- package/src/components/Slider/context.tsx +0 -1
- package/src/components/Slider/index.css +33 -8
- package/src/components/Slider/index.tsx +61 -33
- package/src/components/WheelObserver/index.tsx +17 -19
- package/src/components/XYPad/Area.tsx +4 -5
- package/src/components/XYPad/Thumb.tsx +8 -7
- package/src/components/XYPad/index.css +18 -4
- package/src/components/XYPad/index.tsx +71 -38
- package/src/components/_util/composeRefs.tsx +63 -0
- package/src/components/_util/index.ts +23 -6
- package/src/components/_util/type.ts +1 -0
- package/src/hooks/useAnimationFrame.ts +0 -3
- package/src/hooks/useCallbackRef.ts +2 -2
- package/src/hooks/useDrag.ts +12 -11
- package/src/hooks/useDragWithElement.ts +12 -17
- package/src/hooks/useEventListener.ts +6 -9
- package/src/hooks/useInterval.ts +0 -3
- package/src/hooks/useLongPress.ts +0 -3
- package/src/hooks/useMIDIAccess.ts +0 -1
- package/src/hooks/useMIDIInput.ts +0 -1
- package/src/hooks/useMIDIMessage.ts +0 -1
- package/src/hooks/usePianoDrag.ts +75 -0
- package/src/index.ts +21 -30
- package/src/styles/global.css +16 -4
- package/dist/index.css.map +0 -1
- package/src/components/AnimationKnob/index.tsx +0 -314
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
2
3
|
var __create = Object.create;
|
|
3
4
|
var __defProp = Object.defineProperty;
|
|
4
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -19,16 +20,14 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
19
20
|
value: mod,
|
|
20
21
|
enumerable: true
|
|
21
22
|
}) : target, mod));
|
|
22
|
-
|
|
23
23
|
//#endregion
|
|
24
24
|
let clsx = require("clsx");
|
|
25
25
|
clsx = __toESM(clsx);
|
|
26
26
|
let react = require("react");
|
|
27
27
|
react = __toESM(react);
|
|
28
28
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
29
|
-
let
|
|
29
|
+
let _tremolo_ui_functions = require("@tremolo-ui/functions");
|
|
30
30
|
let zustand = require("zustand");
|
|
31
|
-
|
|
32
31
|
//#region src/components/AnimationCanvas/canvas.ts
|
|
33
32
|
const drawingState = [
|
|
34
33
|
"strokeStyle",
|
|
@@ -58,10 +57,9 @@ function setDprConfig(canvas, context, width, height, dpr) {
|
|
|
58
57
|
canvas.style.width = `${width}px`;
|
|
59
58
|
canvas.style.height = `${height}px`;
|
|
60
59
|
}
|
|
61
|
-
|
|
62
60
|
//#endregion
|
|
63
61
|
//#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()
|
|
62
|
+
function AnimationCanvas({ draw, init, animate = true, options, width: _width = 100, height: _height = 100, relativeSize, reduceFlickering = true, className, onContextMenu = (event) => event.preventDefault(), ...props }) {
|
|
65
63
|
const canvasRef = (0, react.useRef)(null);
|
|
66
64
|
const memoCanvasRef = (0, react.useRef)(null);
|
|
67
65
|
const reqIdRef = (0, react.useRef)(-1);
|
|
@@ -165,7 +163,6 @@ function AnimationCanvas({ draw, init, animate = true, options, width: _width =
|
|
|
165
163
|
ref: memoCanvasRef
|
|
166
164
|
})] });
|
|
167
165
|
}
|
|
168
|
-
|
|
169
166
|
//#endregion
|
|
170
167
|
//#region src/hooks/useCallbackRef.ts
|
|
171
168
|
/**
|
|
@@ -181,14 +178,13 @@ function useCallbackRef(callback, deps = []) {
|
|
|
181
178
|
});
|
|
182
179
|
return (0, react.useCallback)((...args) => callbackRef.current?.(...args), deps);
|
|
183
180
|
}
|
|
184
|
-
|
|
185
181
|
//#endregion
|
|
186
182
|
//#region src/hooks/useEventListener.ts
|
|
187
183
|
function useEventListener(target, event, handler, options) {
|
|
188
184
|
const listener = useCallbackRef(handler);
|
|
189
185
|
(0, react.useEffect)(() => {
|
|
190
186
|
const node = typeof target === "function" ? target() : target ?? document;
|
|
191
|
-
if (!
|
|
187
|
+
if (!node) return;
|
|
192
188
|
node.addEventListener(event, listener, options);
|
|
193
189
|
return () => {
|
|
194
190
|
node.removeEventListener(event, listener, options);
|
|
@@ -197,14 +193,12 @@ function useEventListener(target, event, handler, options) {
|
|
|
197
193
|
event,
|
|
198
194
|
target,
|
|
199
195
|
options,
|
|
200
|
-
listener
|
|
201
|
-
handler
|
|
196
|
+
listener
|
|
202
197
|
]);
|
|
203
198
|
return () => {
|
|
204
199
|
(typeof target === "function" ? target() : target ?? document)?.removeEventListener(event, listener, options);
|
|
205
200
|
};
|
|
206
201
|
}
|
|
207
|
-
|
|
208
202
|
//#endregion
|
|
209
203
|
//#region src/hooks/useRefCallbackEvent.ts
|
|
210
204
|
function useRefCallbackEvent(event, handler, options, deps = []) {
|
|
@@ -222,18 +216,17 @@ function useRefCallbackEvent(event, handler, options, deps = []) {
|
|
|
222
216
|
};
|
|
223
217
|
}, deps);
|
|
224
218
|
}
|
|
225
|
-
|
|
226
219
|
//#endregion
|
|
227
220
|
//#region src/hooks/useDrag.ts
|
|
228
221
|
/**
|
|
229
|
-
* @category hooks
|
|
230
222
|
* @returns [refCallback, pointerDownHandler]
|
|
231
223
|
*/
|
|
232
|
-
function useDrag({ threshold = 1, onDrag, onDragStart, onDragEnd }) {
|
|
224
|
+
function useDrag({ threshold: _threshold = 1, onDrag, onDragStart, onDragEnd }) {
|
|
233
225
|
const dragOffsetX = (0, react.useRef)(void 0);
|
|
234
226
|
const dragOffsetY = (0, react.useRef)(void 0);
|
|
235
227
|
const dragStartX = (0, react.useRef)(0);
|
|
236
228
|
const dragStartY = (0, react.useRef)(0);
|
|
229
|
+
const threshold = Math.max(_threshold, 1);
|
|
237
230
|
const handleDrag = (0, react.useCallback)((event, first = false) => {
|
|
238
231
|
const isTouch = event instanceof TouchEvent;
|
|
239
232
|
if (isTouch && event.cancelable) event.preventDefault();
|
|
@@ -254,15 +247,15 @@ function useDrag({ threshold = 1, onDrag, onDragStart, onDragEnd }) {
|
|
|
254
247
|
dragOffsetY.current = screenY;
|
|
255
248
|
}
|
|
256
249
|
if (Math.abs(deltaX) < threshold && Math.abs(deltaY) < threshold) return;
|
|
257
|
-
onDrag(screenX - dragStartX.current, screenY - dragStartY.current, deltaX, deltaY);
|
|
250
|
+
onDrag?.(screenX - dragStartX.current, screenY - dragStartY.current, deltaX, deltaY);
|
|
258
251
|
}, [threshold, onDrag]);
|
|
259
|
-
const refHandler = useRefCallbackEvent("touchmove", handleDrag, { passive: false }, [onDrag]);
|
|
260
252
|
const pointerDownHandler = (0, react.useCallback)((event) => {
|
|
261
253
|
dragOffsetX.current = event.screenX;
|
|
262
254
|
dragOffsetY.current = event.screenY;
|
|
263
255
|
handleDrag(event, true);
|
|
264
256
|
onDragStart?.();
|
|
265
257
|
}, [handleDrag, onDragStart]);
|
|
258
|
+
const refHandler = useRefCallbackEvent("touchmove", handleDrag, { passive: false }, [onDrag]);
|
|
266
259
|
useEventListener(globalThis.window, "mousemove", handleDrag);
|
|
267
260
|
useEventListener(globalThis.window, "pointerup", () => {
|
|
268
261
|
dragOffsetX.current = void 0;
|
|
@@ -271,168 +264,10 @@ function useDrag({ threshold = 1, onDrag, onDragStart, onDragEnd }) {
|
|
|
271
264
|
});
|
|
272
265
|
return [refHandler, pointerDownHandler];
|
|
273
266
|
}
|
|
274
|
-
|
|
275
|
-
//#endregion
|
|
276
|
-
//#region src/components/_util/index.ts
|
|
277
|
-
function addNoSelect() {
|
|
278
|
-
document.body.classList.add("tremolo-no-select");
|
|
279
|
-
}
|
|
280
|
-
function removeNoSelect() {
|
|
281
|
-
document.body.classList.remove("tremolo-no-select");
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
//#endregion
|
|
285
|
-
//#region src/components/AnimationKnob/index.tsx
|
|
286
|
-
/**
|
|
287
|
-
* Animatable rotary knob.
|
|
288
|
-
* @category AnimationKnob
|
|
289
|
-
*/
|
|
290
|
-
const AnimationKnob = (0, react.forwardRef)(({ 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,...props }, ref) => {
|
|
291
|
-
const valueRef = (0, react.useRef)(0);
|
|
292
|
-
const elmRef = (0, react.useRef)(null);
|
|
293
|
-
const isRelativeSize = typeof (size ?? width) == "string" || typeof height == "string";
|
|
294
|
-
const onDrag = (0, react.useCallback)((_x, y) => {
|
|
295
|
-
if (!onChange) return;
|
|
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));
|
|
297
|
-
}, [
|
|
298
|
-
max,
|
|
299
|
-
min,
|
|
300
|
-
onChange,
|
|
301
|
-
skew,
|
|
302
|
-
step
|
|
303
|
-
]);
|
|
304
|
-
const handleKeyDown = (0, react.useCallback)((event) => {
|
|
305
|
-
if (!keyboard || !onChange) return;
|
|
306
|
-
const key = event.key;
|
|
307
|
-
if ([
|
|
308
|
-
"ArrowRight",
|
|
309
|
-
"ArrowLeft",
|
|
310
|
-
"ArrowUp",
|
|
311
|
-
"ArrowDown"
|
|
312
|
-
].includes(key)) {
|
|
313
|
-
event.preventDefault();
|
|
314
|
-
const x = key == "ArrowRight" || key == "ArrowUp" ? keyboard[1] : -keyboard[1];
|
|
315
|
-
let v;
|
|
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;
|
|
318
|
-
onChange((0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)(v, step), min, max));
|
|
319
|
-
}
|
|
320
|
-
}, [
|
|
321
|
-
value,
|
|
322
|
-
min,
|
|
323
|
-
max,
|
|
324
|
-
step,
|
|
325
|
-
skew,
|
|
326
|
-
keyboard,
|
|
327
|
-
onChange
|
|
328
|
-
]);
|
|
329
|
-
const [touchMoveRefCallback, pointerDownHandler] = useDrag({
|
|
330
|
-
onDrag,
|
|
331
|
-
onDragStart: () => {
|
|
332
|
-
valueRef.current = (0, __tremolo_ui_functions.normalizeValue)(value, min, max, skew);
|
|
333
|
-
if (bodyNoSelect) addNoSelect();
|
|
334
|
-
},
|
|
335
|
-
onDragEnd: () => {
|
|
336
|
-
if (bodyNoSelect) removeNoSelect();
|
|
337
|
-
}
|
|
338
|
-
});
|
|
339
|
-
const wheelRefCallback = useRefCallbackEvent("wheel", (event) => {
|
|
340
|
-
if (!wheel) return;
|
|
341
|
-
event.preventDefault();
|
|
342
|
-
const x = event.deltaY > 0 ? -wheel[1] : wheel[1];
|
|
343
|
-
let v;
|
|
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;
|
|
346
|
-
if (onChange) onChange((0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)(v, step), min, max));
|
|
347
|
-
}, { passive: false }, [
|
|
348
|
-
wheel,
|
|
349
|
-
value,
|
|
350
|
-
min,
|
|
351
|
-
max,
|
|
352
|
-
skew,
|
|
353
|
-
step,
|
|
354
|
-
onChange
|
|
355
|
-
]);
|
|
356
|
-
(0, react.useImperativeHandle)(ref, () => {
|
|
357
|
-
return {
|
|
358
|
-
focus() {
|
|
359
|
-
elmRef.current?.focus();
|
|
360
|
-
},
|
|
361
|
-
blur() {
|
|
362
|
-
elmRef.current?.blur();
|
|
363
|
-
}
|
|
364
|
-
};
|
|
365
|
-
}, []);
|
|
366
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
367
|
-
className: (0, clsx.default)("tremolo-animation-knob", className),
|
|
368
|
-
ref: (div) => {
|
|
369
|
-
elmRef.current = div;
|
|
370
|
-
wheelRefCallback(div);
|
|
371
|
-
touchMoveRefCallback(div);
|
|
372
|
-
},
|
|
373
|
-
role: "slider",
|
|
374
|
-
tabIndex: 0,
|
|
375
|
-
"aria-valuenow": value,
|
|
376
|
-
"aria-valuemax": max,
|
|
377
|
-
"aria-valuemin": min,
|
|
378
|
-
style: {
|
|
379
|
-
display: "inline-block",
|
|
380
|
-
width: size ?? width,
|
|
381
|
-
height: size ?? height,
|
|
382
|
-
cursor: "pointer",
|
|
383
|
-
WebkitTapHighlightColor: "transparent",
|
|
384
|
-
...style
|
|
385
|
-
},
|
|
386
|
-
onPointerDown: pointerDownHandler,
|
|
387
|
-
onDoubleClick: () => {
|
|
388
|
-
if (enableDoubleClickDefault && onChange) onChange(defaultValue);
|
|
389
|
-
},
|
|
390
|
-
onKeyDown: handleKeyDown,
|
|
391
|
-
...props,
|
|
392
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AnimationCanvas, {
|
|
393
|
-
...isRelativeSize ? { relativeSize: true } : {
|
|
394
|
-
width: size ?? width,
|
|
395
|
-
height: size ?? height
|
|
396
|
-
},
|
|
397
|
-
draw: draw ? draw : (ctx, { width: width$1, height: height$1 }) => {
|
|
398
|
-
const p = (0, __tremolo_ui_functions.normalizeValue)(value, min, max, skew);
|
|
399
|
-
const startP = (0, __tremolo_ui_functions.normalizeValue)(startValue, min, max, skew);
|
|
400
|
-
const cx = width$1 / 2;
|
|
401
|
-
const cy = height$1 / 2;
|
|
402
|
-
const r = Math.min(cx, cy);
|
|
403
|
-
if (r <= 0) return;
|
|
404
|
-
ctx.clearRect(0, 0, width$1, height$1);
|
|
405
|
-
ctx.lineCap = "butt";
|
|
406
|
-
ctx.beginPath();
|
|
407
|
-
ctx.fillStyle = bg;
|
|
408
|
-
ctx.arc(cx, cy, r - .5, 0, 2 * Math.PI);
|
|
409
|
-
ctx.fill();
|
|
410
|
-
ctx.lineWidth = lineWeight;
|
|
411
|
-
ctx.beginPath();
|
|
412
|
-
ctx.strokeStyle = inactiveColor;
|
|
413
|
-
ctx.arc(cx, cy, r - .5 - lineWeight / 2, (0, __tremolo_ui_functions.radian)(135), (0, __tremolo_ui_functions.radian)(45));
|
|
414
|
-
ctx.stroke();
|
|
415
|
-
ctx.beginPath();
|
|
416
|
-
ctx.strokeStyle = activeColor;
|
|
417
|
-
ctx.arc(cx, cy, r - .5 - lineWeight / 2, (0, __tremolo_ui_functions.radian)(135 + 270 * startP), (0, __tremolo_ui_functions.radian)(135 + 270 * p), p < startP);
|
|
418
|
-
ctx.stroke();
|
|
419
|
-
ctx.lineCap = "round";
|
|
420
|
-
ctx.beginPath();
|
|
421
|
-
ctx.moveTo(cx, cy);
|
|
422
|
-
ctx.lineTo(cx + Math.cos((0, __tremolo_ui_functions.radian)(135 + 270 * p)) * (r - .5 - thumbWeight / 2), cy + Math.sin((0, __tremolo_ui_functions.radian)(135 + 270 * p)) * (r - .5 - thumbWeight / 2));
|
|
423
|
-
ctx.strokeStyle = thumb;
|
|
424
|
-
ctx.lineWidth = thumbWeight;
|
|
425
|
-
ctx.stroke();
|
|
426
|
-
}
|
|
427
|
-
})
|
|
428
|
-
});
|
|
429
|
-
});
|
|
430
|
-
|
|
431
267
|
//#endregion
|
|
432
268
|
//#region src/components/DragObserver/index.tsx
|
|
433
|
-
/** @category DragObserver */
|
|
434
269
|
function DragObserver(props) {
|
|
435
|
-
const { as: Component = "div", threshold = 1,
|
|
270
|
+
const { as: Component = "div", threshold = 1, onDrag, onDragStart, onDragEnd, children, onPointerDown, ...attributes } = props;
|
|
436
271
|
const [refCallback, pointerDownHandler] = useDrag({
|
|
437
272
|
threshold,
|
|
438
273
|
onDrag,
|
|
@@ -441,190 +276,333 @@ function DragObserver(props) {
|
|
|
441
276
|
});
|
|
442
277
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, {
|
|
443
278
|
ref: refCallback,
|
|
444
|
-
onPointerDown:
|
|
279
|
+
onPointerDown: (e) => {
|
|
280
|
+
pointerDownHandler(e);
|
|
281
|
+
onPointerDown?.(e);
|
|
282
|
+
},
|
|
445
283
|
...attributes,
|
|
446
284
|
children
|
|
447
285
|
});
|
|
448
286
|
}
|
|
449
|
-
|
|
450
287
|
//#endregion
|
|
451
|
-
//#region src/components/
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
let newValue;
|
|
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;
|
|
475
|
-
return (0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)(newValue, step), min, max);
|
|
476
|
-
}, [
|
|
477
|
-
max,
|
|
478
|
-
min,
|
|
479
|
-
skew,
|
|
480
|
-
step,
|
|
481
|
-
value
|
|
482
|
-
]);
|
|
483
|
-
const handleKeyDown = (0, react.useCallback)((event) => {
|
|
484
|
-
if (!keyboard || !onChange || readonly) return;
|
|
485
|
-
const key = event.key;
|
|
486
|
-
if ([
|
|
487
|
-
"ArrowRight",
|
|
488
|
-
"ArrowLeft",
|
|
489
|
-
"ArrowUp",
|
|
490
|
-
"ArrowDown"
|
|
491
|
-
].includes(key)) {
|
|
492
|
-
event.preventDefault();
|
|
493
|
-
const x = key == "ArrowRight" || key == "ArrowUp" ? keyboard[1] : -keyboard[1];
|
|
494
|
-
onChange(updateValueByEvent(keyboard[0], x));
|
|
495
|
-
}
|
|
496
|
-
}, [
|
|
497
|
-
keyboard,
|
|
498
|
-
onChange,
|
|
499
|
-
readonly,
|
|
500
|
-
updateValueByEvent
|
|
501
|
-
]);
|
|
502
|
-
const [touchMoveRefCallback, pointerDownHandler] = useDrag({
|
|
503
|
-
onDrag,
|
|
504
|
-
onDragStart: () => {
|
|
505
|
-
valueRef.current = (0, __tremolo_ui_functions.normalizeValue)(value, min, max, skew);
|
|
506
|
-
if (bodyNoSelect) addNoSelect();
|
|
507
|
-
},
|
|
508
|
-
onDragEnd: () => {
|
|
509
|
-
if (bodyNoSelect) removeNoSelect();
|
|
510
|
-
}
|
|
288
|
+
//#region src/components/_util/index.ts
|
|
289
|
+
function addUserSelectNone() {
|
|
290
|
+
window.document.body.classList.add("tremolo-user-select-none");
|
|
291
|
+
}
|
|
292
|
+
function removeUserSelectNone() {
|
|
293
|
+
window.document.body.classList.remove("tremolo-user-select-none");
|
|
294
|
+
}
|
|
295
|
+
const cursorStyles = {
|
|
296
|
+
grabbing: "tremolo-cursor-grabbing",
|
|
297
|
+
grab: "tremolo-cursor-grab",
|
|
298
|
+
pointer: "tremolo-cursor-pointer",
|
|
299
|
+
move: "tremolo-cursor-move",
|
|
300
|
+
none: "tremolo-cursor-none"
|
|
301
|
+
};
|
|
302
|
+
function setCursorStyle(type) {
|
|
303
|
+
resetCursorStyle();
|
|
304
|
+
window.document.body.classList.add(cursorStyles[type]);
|
|
305
|
+
}
|
|
306
|
+
function resetCursorStyle() {
|
|
307
|
+
Object.values(cursorStyles).forEach((s) => {
|
|
308
|
+
window.document.body.classList.remove(s);
|
|
511
309
|
});
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
const x = Math.sign(event.deltaY) * -wheel[1];
|
|
517
|
-
onChange(updateValueByEvent(wheel[0], x));
|
|
518
|
-
}, { passive: false }, [
|
|
519
|
-
wheel,
|
|
520
|
-
onChange,
|
|
521
|
-
readonly,
|
|
522
|
-
updateValueByEvent
|
|
523
|
-
]);
|
|
524
|
-
(0, react.useImperativeHandle)(forwardedRef, () => {
|
|
525
|
-
return {
|
|
526
|
-
focus() {
|
|
527
|
-
elmRef.current?.focus();
|
|
528
|
-
},
|
|
529
|
-
blur() {
|
|
530
|
-
elmRef.current?.blur();
|
|
531
|
-
}
|
|
532
|
-
};
|
|
533
|
-
}, []);
|
|
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);
|
|
310
|
+
}
|
|
311
|
+
function calcDrawingState({ value, min, max, skew, startValue, angleRange }) {
|
|
312
|
+
const p = (0, _tremolo_ui_functions.normalizeValue)(value, min, max, skew);
|
|
313
|
+
const s = (0, _tremolo_ui_functions.normalizeValue)(startValue, min, max, skew);
|
|
539
314
|
const r1 = -angleRange / 2;
|
|
540
315
|
const r2 = r1 + Math.min(p, s) * angleRange;
|
|
541
316
|
const r3 = r1 + Math.max(p, s) * angleRange;
|
|
542
317
|
const r4 = angleRange / 2;
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
318
|
+
return {
|
|
319
|
+
p,
|
|
320
|
+
r1,
|
|
321
|
+
r2,
|
|
322
|
+
r3,
|
|
323
|
+
r4,
|
|
324
|
+
x1: 50 + 50 * Math.cos((0, _tremolo_ui_functions.radian)(r1 - 90)),
|
|
325
|
+
y1: 50 + 50 * Math.sin((0, _tremolo_ui_functions.radian)(r1 - 90)),
|
|
326
|
+
x2: 50 + 50 * Math.cos((0, _tremolo_ui_functions.radian)(r2 - 90)),
|
|
327
|
+
y2: 50 + 50 * Math.sin((0, _tremolo_ui_functions.radian)(r2 - 90)),
|
|
328
|
+
x3: 50 + 50 * Math.cos((0, _tremolo_ui_functions.radian)(r3 - 90)),
|
|
329
|
+
y3: 50 + 50 * Math.sin((0, _tremolo_ui_functions.radian)(r3 - 90)),
|
|
330
|
+
x4: 50 + 50 * Math.cos((0, _tremolo_ui_functions.radian)(r4 - 90)),
|
|
331
|
+
y4: 50 + 50 * Math.sin((0, _tremolo_ui_functions.radian)(r4 - 90))
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
const createKnobStore = (initProps) => {
|
|
335
|
+
return (0, zustand.createStore)()(() => ({
|
|
336
|
+
...initProps,
|
|
337
|
+
...calcDrawingState(initProps)
|
|
338
|
+
}));
|
|
339
|
+
};
|
|
340
|
+
const KnobContext = (0, react.createContext)(null);
|
|
341
|
+
function KnobProvider({ children, ...props }) {
|
|
342
|
+
const storeRef = (0, react.useRef)(null);
|
|
343
|
+
if (!storeRef.current) storeRef.current = createKnobStore(props);
|
|
344
|
+
(0, react.useEffect)(() => {
|
|
345
|
+
if (storeRef.current) storeRef.current.setState({
|
|
346
|
+
...props,
|
|
347
|
+
...calcDrawingState(props)
|
|
348
|
+
});
|
|
349
|
+
else storeRef.current = createKnobStore(props);
|
|
350
|
+
}, [props]);
|
|
351
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KnobContext.Provider, {
|
|
352
|
+
value: storeRef.current,
|
|
353
|
+
children
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
function useKnobContext(selector) {
|
|
357
|
+
const store = (0, react.useContext)(KnobContext);
|
|
358
|
+
if (!store) throw new Error("Missing KnobContext.Provider in the tree");
|
|
359
|
+
return (0, zustand.useStore)(store, selector);
|
|
360
|
+
}
|
|
361
|
+
//#endregion
|
|
362
|
+
//#region src/components/Knob/ActiveLine.tsx
|
|
363
|
+
function ActiveLine({ stroke = "currentColor", strokeWidth = 6, className, ...props }) {
|
|
364
|
+
const r2 = useKnobContext((s) => s.r2);
|
|
365
|
+
const r3 = useKnobContext((s) => s.r3);
|
|
366
|
+
const x2 = useKnobContext((s) => s.x2);
|
|
367
|
+
const y2 = useKnobContext((s) => s.y2);
|
|
368
|
+
const x3 = useKnobContext((s) => s.x3);
|
|
369
|
+
const y3 = useKnobContext((s) => s.y3);
|
|
370
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
371
|
+
className: (0, clsx.default)("tremolo-knob-active-line", className),
|
|
372
|
+
d: `M ${x2} ${y2} A 50 50 -135 ${r3 - r2 > 180 ? 1 : 0} 1 ${x3} ${y3}`,
|
|
373
|
+
fill: "none",
|
|
374
|
+
stroke,
|
|
375
|
+
strokeWidth,
|
|
376
|
+
...props
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
//#endregion
|
|
380
|
+
//#region src/components/Knob/InactiveLine.tsx
|
|
381
|
+
function InactiveLine({ stroke = "currentColor", strokeWidth = 6, className, ...props }) {
|
|
382
|
+
const min = useKnobContext((s) => s.min);
|
|
383
|
+
const max = useKnobContext((s) => s.max);
|
|
384
|
+
const startValue = useKnobContext((s) => s.startValue);
|
|
385
|
+
const r1 = useKnobContext((s) => s.r1);
|
|
386
|
+
const r2 = useKnobContext((s) => s.r2);
|
|
387
|
+
const r3 = useKnobContext((s) => s.r3);
|
|
388
|
+
const r4 = useKnobContext((s) => s.r4);
|
|
389
|
+
const x1 = useKnobContext((s) => s.x1);
|
|
390
|
+
const y1 = useKnobContext((s) => s.y1);
|
|
391
|
+
const x2 = useKnobContext((s) => s.x2);
|
|
392
|
+
const y2 = useKnobContext((s) => s.y2);
|
|
393
|
+
const x3 = useKnobContext((s) => s.x3);
|
|
394
|
+
const y3 = useKnobContext((s) => s.y3);
|
|
395
|
+
const x4 = useKnobContext((s) => s.x4);
|
|
396
|
+
const y4 = useKnobContext((s) => s.y4);
|
|
397
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [startValue > min && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
398
|
+
className: (0, clsx.default)("tremolo-knob-inactive-line", className),
|
|
399
|
+
d: `M ${x1} ${y1} A 50 50 -135 ${r2 - r1 > 180 ? 1 : 0} 1 ${x2} ${y2}`,
|
|
400
|
+
fill: "none",
|
|
401
|
+
stroke,
|
|
402
|
+
strokeWidth,
|
|
403
|
+
...props
|
|
404
|
+
}), startValue < max && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
405
|
+
className: (0, clsx.default)("tremolo-knob-inactive-line", className),
|
|
406
|
+
d: `M ${x3} ${y3} A 50 50 -135 ${r4 - r3 > 180 ? 1 : 0} 1 ${x4} ${y4}`,
|
|
407
|
+
fill: "none",
|
|
408
|
+
stroke,
|
|
409
|
+
strokeWidth,
|
|
410
|
+
...props
|
|
411
|
+
})] });
|
|
412
|
+
}
|
|
413
|
+
//#endregion
|
|
414
|
+
//#region src/components/Knob/Thumb.tsx
|
|
415
|
+
function Thumb$2({ className, thumb = "currentColor", thumbLine = "currentColor", thumbSize = 84, thumbLineWeight = 6, thumbLineLength = 35, classes, ...props }) {
|
|
416
|
+
const angleRange = useKnobContext((s) => s.angleRange);
|
|
417
|
+
const p = useKnobContext((s) => s.p);
|
|
418
|
+
const r1 = useKnobContext((s) => s.r1);
|
|
551
419
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
420
|
+
className: (0, clsx.default)("tremolo-knob-thumb", classes?.thumb),
|
|
421
|
+
...props,
|
|
422
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
423
|
+
cx: "50%",
|
|
424
|
+
cy: "50%",
|
|
425
|
+
r: `${thumbSize / 2}%`,
|
|
426
|
+
fill: thumb
|
|
427
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
|
|
428
|
+
className: (0, clsx.default)("tremolo-knob-thumb-line", classes?.thumbLine),
|
|
429
|
+
x1: "50%",
|
|
430
|
+
y1: `${(100 - (0, _tremolo_ui_functions.clamp)(thumbSize, 0, 100)) / 2}%`,
|
|
431
|
+
x2: "50%",
|
|
432
|
+
y2: `${thumbLineLength}%`,
|
|
433
|
+
stroke: thumbLine,
|
|
434
|
+
strokeWidth: `${thumbLineWeight}%`,
|
|
435
|
+
style: {
|
|
436
|
+
transform: `rotate(${r1 + p * angleRange}deg)`,
|
|
437
|
+
transformOrigin: "50% 50%"
|
|
438
|
+
}
|
|
439
|
+
})]
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
//#endregion
|
|
443
|
+
//#region src/components/Knob/SVGRoot.tsx
|
|
444
|
+
function SVGRoot({ children, style, ...props }) {
|
|
445
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
446
|
+
viewBox: `0 0 100 100`,
|
|
447
|
+
style: {
|
|
448
|
+
display: "block",
|
|
449
|
+
overflow: "visible",
|
|
450
|
+
...style
|
|
579
451
|
},
|
|
580
452
|
...props,
|
|
581
|
-
children: [
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
stroke: inactiveLine || "currentColor",
|
|
587
|
-
strokeWidth: lineWeight
|
|
588
|
-
}),
|
|
589
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
590
|
-
className: (0, clsx.default)("tremolo-knob-active-line", classes?.activeLine),
|
|
591
|
-
d: `M ${x2} ${y2} A ${center} ${center} -135 ${r3 - r2 > 180 ? 1 : 0} 1 ${x3}, ${y3}`,
|
|
592
|
-
fill: "none",
|
|
593
|
-
stroke: activeLine || "currentColor",
|
|
594
|
-
strokeWidth: lineWeight
|
|
595
|
-
}),
|
|
596
|
-
startValue < max && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
597
|
-
className: (0, clsx.default)("tremolo-knob-inactive-line", classes?.inactiveLine),
|
|
598
|
-
d: `M ${x3} ${y3} A ${center} ${center} -135 ${r4 - r3 > 180 ? 1 : 0} 1 ${x4}, ${y4}`,
|
|
599
|
-
fill: "none",
|
|
600
|
-
stroke: inactiveLine || "currentColor",
|
|
601
|
-
strokeWidth: lineWeight
|
|
602
|
-
}),
|
|
603
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
604
|
-
className: (0, clsx.default)("tremolo-knob-thumb", classes?.thumb),
|
|
605
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
606
|
-
cx: "50%",
|
|
607
|
-
cy: "50%",
|
|
608
|
-
r: `${thumbSize / 2}%`,
|
|
609
|
-
fill: thumb || "currentColor"
|
|
610
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
|
|
611
|
-
className: (0, clsx.default)("tremolo-knob-thumb-line", classes?.thumbLine),
|
|
612
|
-
x1: "50%",
|
|
613
|
-
y1: `${padding}%`,
|
|
614
|
-
x2: "50%",
|
|
615
|
-
y2: `${thumbLineLength}%`,
|
|
616
|
-
stroke: thumbLine || "currentColor",
|
|
617
|
-
strokeWidth: `${thumbLineWeight}%`,
|
|
618
|
-
style: {
|
|
619
|
-
transform: `rotate(${r1 + p * angleRange}deg)`,
|
|
620
|
-
transformOrigin: "50% 50%"
|
|
621
|
-
}
|
|
622
|
-
})]
|
|
623
|
-
})
|
|
624
|
-
]
|
|
453
|
+
children: children || /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
454
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InactiveLine, {}),
|
|
455
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActiveLine, {}),
|
|
456
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Thumb$2, {})
|
|
457
|
+
] })
|
|
625
458
|
});
|
|
626
|
-
}
|
|
627
|
-
|
|
459
|
+
}
|
|
460
|
+
//#endregion
|
|
461
|
+
//#region src/components/Knob/index.tsx
|
|
462
|
+
const defaultExternalStyles$3 = {
|
|
463
|
+
userSelectNone: true,
|
|
464
|
+
cursor: "grabbing"
|
|
465
|
+
};
|
|
466
|
+
/**
|
|
467
|
+
* Interactive rotary knob component implemented in SVG.
|
|
468
|
+
*/
|
|
469
|
+
const Knob = {
|
|
470
|
+
Root: (0, react.forwardRef)(({ value, min, max, step = 1, skew = 1, defaultValue = min, startValue = min, size, externalStyles: _externalStyles, wheel = ["raw", 1], keyboard = ["raw", 1], enableDoubleClickDefault = true, disabled = false, readonly = false, angleRange = 270, onChange, onKeyDown, onPointerDown, onDoubleClick, className, style, children, ...props }, forwardedRef) => {
|
|
471
|
+
const [dragging, setDragging] = (0, react.useState)(false);
|
|
472
|
+
const valueRef = (0, react.useRef)(0);
|
|
473
|
+
const elmRef = (0, react.useRef)(null);
|
|
474
|
+
const externalStyles = {
|
|
475
|
+
...defaultExternalStyles$3,
|
|
476
|
+
..._externalStyles
|
|
477
|
+
};
|
|
478
|
+
const onDrag = (0, react.useCallback)((_x, y) => {
|
|
479
|
+
if (!onChange || readonly) return;
|
|
480
|
+
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));
|
|
481
|
+
}, [
|
|
482
|
+
max,
|
|
483
|
+
min,
|
|
484
|
+
onChange,
|
|
485
|
+
readonly,
|
|
486
|
+
skew,
|
|
487
|
+
step
|
|
488
|
+
]);
|
|
489
|
+
const updateValueByEvent = (0, react.useCallback)((eventType, x) => {
|
|
490
|
+
let newValue;
|
|
491
|
+
if (eventType == "normalized") newValue = (0, _tremolo_ui_functions.rawValue)((0, _tremolo_ui_functions.normalizeValue)(value, min, max, skew) + x, min, max, skew);
|
|
492
|
+
else newValue = value + x;
|
|
493
|
+
return (0, _tremolo_ui_functions.clamp)((0, _tremolo_ui_functions.stepValue)(newValue, step), min, max);
|
|
494
|
+
}, [
|
|
495
|
+
max,
|
|
496
|
+
min,
|
|
497
|
+
skew,
|
|
498
|
+
step,
|
|
499
|
+
value
|
|
500
|
+
]);
|
|
501
|
+
const handleKeyDown = (0, react.useCallback)((event) => {
|
|
502
|
+
if (!keyboard || !onChange || readonly) return;
|
|
503
|
+
const key = event.key;
|
|
504
|
+
if ([
|
|
505
|
+
"ArrowRight",
|
|
506
|
+
"ArrowLeft",
|
|
507
|
+
"ArrowUp",
|
|
508
|
+
"ArrowDown"
|
|
509
|
+
].includes(key)) {
|
|
510
|
+
event.preventDefault();
|
|
511
|
+
const x = key == "ArrowRight" || key == "ArrowUp" ? keyboard[1] : -keyboard[1];
|
|
512
|
+
onChange(updateValueByEvent(keyboard[0], x));
|
|
513
|
+
}
|
|
514
|
+
}, [
|
|
515
|
+
keyboard,
|
|
516
|
+
onChange,
|
|
517
|
+
readonly,
|
|
518
|
+
updateValueByEvent
|
|
519
|
+
]);
|
|
520
|
+
const [touchMoveRefCallback, pointerDownHandler] = useDrag({
|
|
521
|
+
onDrag,
|
|
522
|
+
onDragStart: () => {
|
|
523
|
+
setDragging(true);
|
|
524
|
+
valueRef.current = (0, _tremolo_ui_functions.normalizeValue)(value, min, max, skew);
|
|
525
|
+
if (externalStyles.userSelectNone) addUserSelectNone();
|
|
526
|
+
if (externalStyles.cursor) setCursorStyle(externalStyles.cursor);
|
|
527
|
+
},
|
|
528
|
+
onDragEnd: () => {
|
|
529
|
+
setDragging(false);
|
|
530
|
+
if (externalStyles.userSelectNone) removeUserSelectNone();
|
|
531
|
+
if (externalStyles.cursor) resetCursorStyle();
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
const wheelRefCallback = useRefCallbackEvent("wheel", (event) => {
|
|
535
|
+
if (!wheel || readonly) return;
|
|
536
|
+
event.preventDefault();
|
|
537
|
+
if (!onChange || event.deltaY == 0) return;
|
|
538
|
+
const x = Math.sign(event.deltaY) * -wheel[1];
|
|
539
|
+
onChange(updateValueByEvent(wheel[0], x));
|
|
540
|
+
}, { passive: false }, [
|
|
541
|
+
wheel,
|
|
542
|
+
onChange,
|
|
543
|
+
readonly,
|
|
544
|
+
updateValueByEvent
|
|
545
|
+
]);
|
|
546
|
+
(0, react.useImperativeHandle)(forwardedRef, () => {
|
|
547
|
+
return {
|
|
548
|
+
focus() {
|
|
549
|
+
elmRef.current?.focus();
|
|
550
|
+
},
|
|
551
|
+
blur() {
|
|
552
|
+
elmRef.current?.blur();
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
}, []);
|
|
556
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KnobProvider, {
|
|
557
|
+
value,
|
|
558
|
+
min,
|
|
559
|
+
max,
|
|
560
|
+
step,
|
|
561
|
+
skew,
|
|
562
|
+
startValue,
|
|
563
|
+
angleRange,
|
|
564
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
565
|
+
ref: (elm) => {
|
|
566
|
+
elmRef.current = elm;
|
|
567
|
+
wheelRefCallback(elm);
|
|
568
|
+
touchMoveRefCallback(elm);
|
|
569
|
+
},
|
|
570
|
+
className: (0, clsx.default)("tremolo-knob", className),
|
|
571
|
+
tabIndex: 0,
|
|
572
|
+
role: "slider",
|
|
573
|
+
"aria-valuenow": value,
|
|
574
|
+
"aria-valuemin": min,
|
|
575
|
+
"aria-valuemax": max,
|
|
576
|
+
"aria-disabled": disabled,
|
|
577
|
+
"aria-readonly": readonly,
|
|
578
|
+
"data-dragging": dragging,
|
|
579
|
+
style: {
|
|
580
|
+
width: size,
|
|
581
|
+
height: size,
|
|
582
|
+
...style
|
|
583
|
+
},
|
|
584
|
+
onPointerDown: (event) => {
|
|
585
|
+
pointerDownHandler(event);
|
|
586
|
+
onPointerDown?.(event);
|
|
587
|
+
},
|
|
588
|
+
onDoubleClick: (event) => {
|
|
589
|
+
if (enableDoubleClickDefault && onChange) onChange(defaultValue);
|
|
590
|
+
onDoubleClick?.(event);
|
|
591
|
+
},
|
|
592
|
+
onKeyDown: (event) => {
|
|
593
|
+
handleKeyDown(event);
|
|
594
|
+
onKeyDown?.(event);
|
|
595
|
+
},
|
|
596
|
+
...props,
|
|
597
|
+
children: children || /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SVGRoot, {})
|
|
598
|
+
})
|
|
599
|
+
});
|
|
600
|
+
}),
|
|
601
|
+
SVGRoot,
|
|
602
|
+
InactiveLine,
|
|
603
|
+
ActiveLine,
|
|
604
|
+
Thumb: Thumb$2
|
|
605
|
+
};
|
|
628
606
|
//#endregion
|
|
629
607
|
//#region src/components/NumberInput/type.ts
|
|
630
608
|
function selectUnit(units, value) {
|
|
@@ -635,8 +613,8 @@ function selectUnit(units, value) {
|
|
|
635
613
|
function parseValue(inputString, units, digit) {
|
|
636
614
|
const str = inputString.trim();
|
|
637
615
|
if (!units || typeof units == "string") {
|
|
638
|
-
const m
|
|
639
|
-
let v = Number(m
|
|
616
|
+
const m = str.match(/-?\d+(\.\d+)?/);
|
|
617
|
+
let v = Number(m?.[0] ?? "0");
|
|
640
618
|
v = isNaN(v) ? 0 : v;
|
|
641
619
|
return {
|
|
642
620
|
rawValue: v,
|
|
@@ -646,30 +624,29 @@ function parseValue(inputString, units, digit) {
|
|
|
646
624
|
}
|
|
647
625
|
const unitList = units.map(([u, _s]) => u);
|
|
648
626
|
const scaleList = units.map(([_u, s]) => s);
|
|
649
|
-
let rawValue
|
|
627
|
+
let rawValue = 0;
|
|
650
628
|
let unit = unitList[0];
|
|
651
|
-
let formatValue = (digit != void 0 ? rawValue
|
|
629
|
+
let formatValue = (digit != void 0 ? rawValue.toFixed(digit) : rawValue) + unit;
|
|
652
630
|
const m = str.match(/^(-?\d+(\.\d+)?)\s*(\w*)$/);
|
|
653
631
|
if (m) {
|
|
654
|
-
rawValue
|
|
632
|
+
rawValue = Number(m[1]) || 0;
|
|
655
633
|
const uIndex = unitList.indexOf(m[3]);
|
|
656
|
-
rawValue
|
|
657
|
-
const [u, scale] = selectUnit(units, rawValue
|
|
658
|
-
const v = rawValue
|
|
634
|
+
rawValue *= uIndex != -1 ? scaleList[uIndex] : 1;
|
|
635
|
+
const [u, scale] = selectUnit(units, rawValue);
|
|
636
|
+
const v = rawValue / scale;
|
|
659
637
|
formatValue = (digit != void 0 ? v.toFixed(digit) : v) + u;
|
|
660
638
|
unit = u;
|
|
661
639
|
}
|
|
662
640
|
return {
|
|
663
|
-
rawValue
|
|
641
|
+
rawValue,
|
|
664
642
|
formatValue,
|
|
665
643
|
unit
|
|
666
644
|
};
|
|
667
645
|
}
|
|
668
|
-
|
|
669
646
|
//#endregion
|
|
670
647
|
//#region src/components/NumberInput/context.tsx
|
|
671
648
|
function safeClamp(value, min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER) {
|
|
672
|
-
return (0,
|
|
649
|
+
return (0, _tremolo_ui_functions.clamp)(value, min, max);
|
|
673
650
|
}
|
|
674
651
|
const createNumberInputStore = (initProps) => {
|
|
675
652
|
const DEFAULT_PROPS = {
|
|
@@ -720,7 +697,7 @@ const createNumberInputStore = (initProps) => {
|
|
|
720
697
|
}));
|
|
721
698
|
};
|
|
722
699
|
const NumberInputContext = (0, react.createContext)(null);
|
|
723
|
-
function NumberInputProvider({ children
|
|
700
|
+
function NumberInputProvider({ children, ...props }) {
|
|
724
701
|
const storeRef = (0, react.useRef)(null);
|
|
725
702
|
if (!storeRef.current) storeRef.current = createNumberInputStore(props);
|
|
726
703
|
(0, react.useEffect)(() => {
|
|
@@ -738,51 +715,142 @@ function NumberInputProvider({ children,...props }) {
|
|
|
738
715
|
children
|
|
739
716
|
});
|
|
740
717
|
}
|
|
741
|
-
/** @category NumberInput */
|
|
742
718
|
function useNumberInputContext(selector) {
|
|
743
719
|
const store = (0, react.useContext)(NumberInputContext);
|
|
744
720
|
if (!store) throw new Error("Missing NumberInputContext.Provider in the tree");
|
|
745
721
|
return (0, zustand.useStore)(store, selector);
|
|
746
722
|
}
|
|
747
|
-
|
|
748
723
|
//#endregion
|
|
749
|
-
//#region src/
|
|
750
|
-
|
|
751
|
-
const
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
}, [
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
724
|
+
//#region src/hooks/useInterval.ts
|
|
725
|
+
function useInterval(callback, delay) {
|
|
726
|
+
const savedCallback = (0, react.useRef)(callback);
|
|
727
|
+
(0, react.useEffect)(() => {
|
|
728
|
+
savedCallback.current = callback;
|
|
729
|
+
}, [callback]);
|
|
730
|
+
(0, react.useEffect)(() => {
|
|
731
|
+
if (delay === null) return;
|
|
732
|
+
const id = setInterval(() => {
|
|
733
|
+
savedCallback.current();
|
|
734
|
+
}, delay);
|
|
735
|
+
return () => {
|
|
736
|
+
clearInterval(id);
|
|
737
|
+
};
|
|
738
|
+
}, [delay]);
|
|
739
|
+
}
|
|
740
|
+
//#endregion
|
|
741
|
+
//#region src/hooks/useLongPress.ts
|
|
742
|
+
function useLongPress(callback, initialDelay = 500, interval = 40) {
|
|
743
|
+
const [pressed, setPressed] = (0, react.useState)(false);
|
|
744
|
+
const [delay, setDelay] = (0, react.useState)(initialDelay);
|
|
745
|
+
useInterval(() => {
|
|
746
|
+
callback();
|
|
747
|
+
setDelay(interval);
|
|
748
|
+
}, pressed ? delay : null);
|
|
749
|
+
useEventListener(globalThis.window, "pointerup", () => {
|
|
750
|
+
setPressed(false);
|
|
751
|
+
setDelay(initialDelay);
|
|
752
|
+
});
|
|
753
|
+
return (0, react.useCallback)(() => {
|
|
754
|
+
callback();
|
|
755
|
+
setPressed(true);
|
|
756
|
+
}, [callback]);
|
|
757
|
+
}
|
|
758
|
+
//#endregion
|
|
759
|
+
//#region src/components/NumberInput/DecrementStepper.tsx
|
|
760
|
+
function DecrementStepper({ size = 12, children, className, ...props }) {
|
|
761
|
+
const min = useNumberInputContext((s) => s.min);
|
|
762
|
+
const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
|
|
763
|
+
const readonly = useNumberInputContext((s) => s.readonly);
|
|
764
|
+
const keepWithinRange = useNumberInputContext((s) => s.keepWithinRange);
|
|
765
|
+
const press = useLongPress(useNumberInputContext((s) => s.decrement));
|
|
766
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
767
|
+
className: (0, clsx.default)("tremolo-number-input-decrement-stepper", className),
|
|
768
|
+
role: "button",
|
|
769
|
+
tabIndex: -1,
|
|
770
|
+
"aria-disabled": keepWithinRange && valueAsNumber <= (min ?? Number.MIN_SAFE_INTEGER),
|
|
771
|
+
"aria-readonly": readonly,
|
|
772
|
+
onPointerDown: press,
|
|
773
|
+
...props,
|
|
774
|
+
children: children || /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
775
|
+
width: size,
|
|
776
|
+
height: size,
|
|
777
|
+
viewBox: "0 0 24 24",
|
|
778
|
+
fill: "none",
|
|
779
|
+
stroke: "currentColor",
|
|
780
|
+
strokeWidth: "2",
|
|
781
|
+
strokeLinecap: "round",
|
|
782
|
+
strokeLinejoin: "round",
|
|
783
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polyline", { points: "6 9 12 15 18 9" })
|
|
784
|
+
})
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
//#endregion
|
|
788
|
+
//#region src/components/NumberInput/IncrementStepper.tsx
|
|
789
|
+
function IncrementStepper({ size = 12, children, className, ...props }) {
|
|
790
|
+
const max = useNumberInputContext((s) => s.max);
|
|
791
|
+
const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
|
|
792
|
+
const readonly = useNumberInputContext((s) => s.readonly);
|
|
793
|
+
const keepWithinRange = useNumberInputContext((s) => s.keepWithinRange);
|
|
794
|
+
const press = useLongPress(useNumberInputContext((s) => s.increment));
|
|
795
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
796
|
+
className: (0, clsx.default)("tremolo-number-input-increment-stepper", className),
|
|
797
|
+
role: "button",
|
|
798
|
+
tabIndex: -1,
|
|
799
|
+
"aria-disabled": keepWithinRange && valueAsNumber >= (max ?? Number.MAX_SAFE_INTEGER),
|
|
800
|
+
"aria-readonly": readonly,
|
|
801
|
+
onPointerDown: press,
|
|
802
|
+
...props,
|
|
803
|
+
children: children || /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
804
|
+
width: size,
|
|
805
|
+
height: size,
|
|
806
|
+
viewBox: "0 0 24 24",
|
|
807
|
+
fill: "none",
|
|
808
|
+
stroke: "currentColor",
|
|
809
|
+
strokeWidth: "2",
|
|
810
|
+
strokeLinecap: "round",
|
|
811
|
+
strokeLinejoin: "round",
|
|
812
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polyline", { points: "18 15 12 9 6 15" })
|
|
813
|
+
})
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
//#endregion
|
|
817
|
+
//#region src/components/NumberInput/InternalInput.tsx
|
|
818
|
+
const InternalInput = (0, react.forwardRef)(({ disabled, readonly, selectWithFocus, blurOnEnter, clampValueOnBlur, wheel, keyboard, className, onChange, onFocus, onBlur, onKeyDown, children: _children, ...props }, ref) => {
|
|
819
|
+
const elmRef = (0, react.useRef)(null);
|
|
820
|
+
const [editing, setEditing] = (0, react.useState)(false);
|
|
821
|
+
const value = useNumberInputContext((s) => s.value);
|
|
822
|
+
const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
|
|
823
|
+
const min = useNumberInputContext((s) => s.min);
|
|
824
|
+
const max = useNumberInputContext((s) => s.max);
|
|
825
|
+
const step = useNumberInputContext((s) => s.step);
|
|
826
|
+
const units = useNumberInputContext((s) => s.units);
|
|
827
|
+
const digit = useNumberInputContext((s) => s.digit);
|
|
828
|
+
const change = useNumberInputContext((s) => s.change);
|
|
829
|
+
const error = (0, react.useMemo)(() => {
|
|
830
|
+
if (editing) return false;
|
|
831
|
+
const v = valueAsNumber;
|
|
832
|
+
return (min ?? -Infinity) > v || v > (max ?? Infinity);
|
|
833
|
+
}, [
|
|
834
|
+
editing,
|
|
835
|
+
max,
|
|
836
|
+
min,
|
|
837
|
+
valueAsNumber
|
|
838
|
+
]);
|
|
839
|
+
const updateValueByEvent = (0, react.useCallback)((eventType, x) => {
|
|
840
|
+
let newValue;
|
|
841
|
+
if (eventType == "normalized") {
|
|
842
|
+
if (!min || !max) throw new Error("[NumberInput] \"min\" and \"max\" are required when InputEventOption[0] is set to \"normalized\".");
|
|
843
|
+
newValue = (0, _tremolo_ui_functions.rawValue)((0, _tremolo_ui_functions.normalizeValue)(valueAsNumber, min, max) + x, min, max);
|
|
844
|
+
return (0, _tremolo_ui_functions.clamp)((0, _tremolo_ui_functions.stepValue)(newValue, step), min, max);
|
|
845
|
+
} else {
|
|
846
|
+
newValue = valueAsNumber + x;
|
|
847
|
+
return (0, _tremolo_ui_functions.stepValue)(newValue, step);
|
|
848
|
+
}
|
|
849
|
+
}, [
|
|
850
|
+
max,
|
|
851
|
+
min,
|
|
852
|
+
step,
|
|
853
|
+
valueAsNumber
|
|
786
854
|
]);
|
|
787
855
|
const handleKeyDown = (0, react.useCallback)((event) => {
|
|
788
856
|
if (!keyboard || !onChange || readonly) return;
|
|
@@ -841,8 +909,8 @@ const InternalInput = (0, react.forwardRef)(({ disabled, readonly, selectWithFoc
|
|
|
841
909
|
const v = event.currentTarget.value;
|
|
842
910
|
const cursorPos = event.target.selectionStart;
|
|
843
911
|
change(v);
|
|
844
|
-
const valueAsNumber
|
|
845
|
-
onChange?.(valueAsNumber
|
|
912
|
+
const valueAsNumber = parseValue(v, units, digit).rawValue;
|
|
913
|
+
onChange?.(valueAsNumber, v);
|
|
846
914
|
if (cursorPos) {
|
|
847
915
|
event.target.selectionStart = cursorPos;
|
|
848
916
|
event.target.selectionEnd = cursorPos;
|
|
@@ -873,11 +941,9 @@ const InternalInput = (0, react.forwardRef)(({ disabled, readonly, selectWithFoc
|
|
|
873
941
|
...props
|
|
874
942
|
});
|
|
875
943
|
});
|
|
876
|
-
|
|
877
944
|
//#endregion
|
|
878
945
|
//#region src/components/NumberInput/Stepper.tsx
|
|
879
|
-
|
|
880
|
-
function Stepper({ dynamic = true, children, className,...props }) {
|
|
946
|
+
function Stepper({ dynamic = true, children, className, ...props }) {
|
|
881
947
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
882
948
|
className: (0, clsx.default)("tremolo-number-input-stepper", className),
|
|
883
949
|
"data-dynamic": dynamic,
|
|
@@ -885,162 +951,58 @@ function Stepper({ dynamic = true, children, className,...props }) {
|
|
|
885
951
|
children
|
|
886
952
|
});
|
|
887
953
|
}
|
|
888
|
-
|
|
889
|
-
//#endregion
|
|
890
|
-
//#region src/hooks/useInterval.ts
|
|
891
|
-
/**
|
|
892
|
-
* @category hooks
|
|
893
|
-
*/
|
|
894
|
-
function useInterval(callback, delay) {
|
|
895
|
-
const savedCallback = (0, react.useRef)(callback);
|
|
896
|
-
(0, react.useEffect)(() => {
|
|
897
|
-
savedCallback.current = callback;
|
|
898
|
-
}, [callback]);
|
|
899
|
-
(0, react.useEffect)(() => {
|
|
900
|
-
if (delay === null) return;
|
|
901
|
-
const id = setInterval(() => {
|
|
902
|
-
savedCallback.current();
|
|
903
|
-
}, delay);
|
|
904
|
-
return () => {
|
|
905
|
-
clearInterval(id);
|
|
906
|
-
};
|
|
907
|
-
}, [delay]);
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
//#endregion
|
|
911
|
-
//#region src/hooks/useLongPress.ts
|
|
912
|
-
/**
|
|
913
|
-
* @category hooks
|
|
914
|
-
*/
|
|
915
|
-
function useLongPress(callback, initialDelay = 500, interval = 40) {
|
|
916
|
-
const [pressed, setPressed] = (0, react.useState)(false);
|
|
917
|
-
const [delay, setDelay] = (0, react.useState)(initialDelay);
|
|
918
|
-
useInterval(() => {
|
|
919
|
-
callback();
|
|
920
|
-
setDelay(interval);
|
|
921
|
-
}, pressed ? delay : null);
|
|
922
|
-
useEventListener(globalThis.window, "pointerup", () => {
|
|
923
|
-
setPressed(false);
|
|
924
|
-
setDelay(initialDelay);
|
|
925
|
-
});
|
|
926
|
-
return (0, react.useCallback)(() => {
|
|
927
|
-
callback();
|
|
928
|
-
setPressed(true);
|
|
929
|
-
}, [callback]);
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
//#endregion
|
|
933
|
-
//#region src/components/NumberInput/IncrementStepper.tsx
|
|
934
|
-
/** @category NumberInput */
|
|
935
|
-
function IncrementStepper({ size = 12, children, className,...props }) {
|
|
936
|
-
const max = useNumberInputContext((s) => s.max);
|
|
937
|
-
const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
|
|
938
|
-
const readonly = useNumberInputContext((s) => s.readonly);
|
|
939
|
-
const keepWithinRange = useNumberInputContext((s) => s.keepWithinRange);
|
|
940
|
-
const press = useLongPress(useNumberInputContext((s) => s.increment));
|
|
941
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
942
|
-
className: (0, clsx.default)("tremolo-number-input-increment-stepper", className),
|
|
943
|
-
role: "button",
|
|
944
|
-
tabIndex: -1,
|
|
945
|
-
"aria-disabled": keepWithinRange && valueAsNumber >= (max ?? Number.MAX_SAFE_INTEGER),
|
|
946
|
-
"aria-readonly": readonly,
|
|
947
|
-
onPointerDown: press,
|
|
948
|
-
...props,
|
|
949
|
-
children: children || /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
950
|
-
width: size,
|
|
951
|
-
height: size,
|
|
952
|
-
viewBox: "0 0 24 24",
|
|
953
|
-
fill: "none",
|
|
954
|
-
stroke: "currentColor",
|
|
955
|
-
strokeWidth: "2",
|
|
956
|
-
strokeLinecap: "round",
|
|
957
|
-
strokeLinejoin: "round",
|
|
958
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polyline", { points: "18 15 12 9 6 15" })
|
|
959
|
-
})
|
|
960
|
-
});
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
//#endregion
|
|
964
|
-
//#region src/components/NumberInput/DecrementStepper.tsx
|
|
965
|
-
/** @category NumberInput */
|
|
966
|
-
function DecrementStepper({ size = 12, children, className,...props }) {
|
|
967
|
-
const min = useNumberInputContext((s) => s.min);
|
|
968
|
-
const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
|
|
969
|
-
const readonly = useNumberInputContext((s) => s.readonly);
|
|
970
|
-
const keepWithinRange = useNumberInputContext((s) => s.keepWithinRange);
|
|
971
|
-
const press = useLongPress(useNumberInputContext((s) => s.decrement));
|
|
972
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
973
|
-
className: (0, clsx.default)("tremolo-number-input-decrement-stepper", className),
|
|
974
|
-
role: "button",
|
|
975
|
-
tabIndex: -1,
|
|
976
|
-
"aria-disabled": keepWithinRange && valueAsNumber <= (min ?? Number.MIN_SAFE_INTEGER),
|
|
977
|
-
"aria-readonly": readonly,
|
|
978
|
-
onPointerDown: press,
|
|
979
|
-
...props,
|
|
980
|
-
children: children || /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
981
|
-
width: size,
|
|
982
|
-
height: size,
|
|
983
|
-
viewBox: "0 0 24 24",
|
|
984
|
-
fill: "none",
|
|
985
|
-
stroke: "currentColor",
|
|
986
|
-
strokeWidth: "2",
|
|
987
|
-
strokeLinecap: "round",
|
|
988
|
-
strokeLinejoin: "round",
|
|
989
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polyline", { points: "6 9 12 15 18 9" })
|
|
990
|
-
})
|
|
991
|
-
});
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
//#endregion
|
|
995
|
-
//#region src/components/NumberInput/index.tsx
|
|
996
954
|
/**
|
|
997
955
|
* Input with some useful functions for entering numerical values.
|
|
998
|
-
* @category NumberInput
|
|
999
956
|
*/
|
|
1000
|
-
const NumberInput =
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
});
|
|
1036
|
-
|
|
957
|
+
const NumberInput = {
|
|
958
|
+
Root: (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) => {
|
|
959
|
+
const colors = { "--active-color": activeColor };
|
|
960
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NumberInputProvider, {
|
|
961
|
+
value: String(value),
|
|
962
|
+
min,
|
|
963
|
+
max,
|
|
964
|
+
step,
|
|
965
|
+
units,
|
|
966
|
+
digit,
|
|
967
|
+
readonly,
|
|
968
|
+
keepWithinRange,
|
|
969
|
+
onChange,
|
|
970
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
971
|
+
className: (0, clsx.default)("tremolo-number-input-wrapper", wrapperClassName),
|
|
972
|
+
tabIndex: 0,
|
|
973
|
+
style: { ...colors },
|
|
974
|
+
"data-stepper": !!children,
|
|
975
|
+
"data-variant": variant,
|
|
976
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InternalInput, {
|
|
977
|
+
ref: forwardedRef,
|
|
978
|
+
readonly,
|
|
979
|
+
disabled,
|
|
980
|
+
selectWithFocus,
|
|
981
|
+
blurOnEnter,
|
|
982
|
+
clampValueOnBlur,
|
|
983
|
+
wheel,
|
|
984
|
+
keyboard,
|
|
985
|
+
className,
|
|
986
|
+
onChange,
|
|
987
|
+
onFocus,
|
|
988
|
+
onBlur,
|
|
989
|
+
...props
|
|
990
|
+
}), children]
|
|
991
|
+
})
|
|
992
|
+
});
|
|
993
|
+
}),
|
|
994
|
+
Stepper,
|
|
995
|
+
IncrementStepper,
|
|
996
|
+
DecrementStepper
|
|
997
|
+
};
|
|
1037
998
|
//#endregion
|
|
1038
|
-
//#region src/hooks/
|
|
999
|
+
//#region src/hooks/usePianoDrag.ts
|
|
1039
1000
|
/**
|
|
1040
|
-
*
|
|
1001
|
+
* Internal
|
|
1041
1002
|
* @returns [refCallback, pointerDownHandler]
|
|
1003
|
+
* @private
|
|
1042
1004
|
*/
|
|
1043
|
-
function
|
|
1005
|
+
function usePianoDrag({ baseElementRef, onDrag, onDragStart, onDragEnd }) {
|
|
1044
1006
|
const dragged = (0, react.useRef)(false);
|
|
1045
1007
|
const normalizedX = (0, react.useRef)(0);
|
|
1046
1008
|
const normalizedY = (0, react.useRef)(0);
|
|
@@ -1051,8 +1013,8 @@ function useDragWithElement({ baseElementRef, onDrag, onDragStart, onDragEnd })
|
|
|
1051
1013
|
const { left, top, right, bottom } = baseElementRef.current.getBoundingClientRect();
|
|
1052
1014
|
const mouseX = isTouch ? event.touches[0].clientX : event.clientX;
|
|
1053
1015
|
const mouseY = isTouch ? event.touches[0].clientY : event.clientY;
|
|
1054
|
-
const nx = (0,
|
|
1055
|
-
const ny = (0,
|
|
1016
|
+
const nx = (0, _tremolo_ui_functions.normalizeValue)(mouseX, left, right);
|
|
1017
|
+
const ny = (0, _tremolo_ui_functions.normalizeValue)(mouseY, top, bottom);
|
|
1056
1018
|
normalizedX.current = nx;
|
|
1057
1019
|
normalizedY.current = ny;
|
|
1058
1020
|
onDrag(nx, ny);
|
|
@@ -1069,9 +1031,11 @@ function useDragWithElement({ baseElementRef, onDrag, onDragStart, onDragEnd })
|
|
|
1069
1031
|
dragged.current = false;
|
|
1070
1032
|
onDragEnd?.(normalizedX.current, normalizedY.current);
|
|
1071
1033
|
});
|
|
1072
|
-
return
|
|
1034
|
+
return {
|
|
1035
|
+
refHandler,
|
|
1036
|
+
pointerDownHandler
|
|
1037
|
+
};
|
|
1073
1038
|
}
|
|
1074
|
-
|
|
1075
1039
|
//#endregion
|
|
1076
1040
|
//#region src/components/Piano/context.tsx
|
|
1077
1041
|
const createPianoStore = (initProps) => {
|
|
@@ -1094,7 +1058,7 @@ const createPianoStore = (initProps) => {
|
|
|
1094
1058
|
}));
|
|
1095
1059
|
};
|
|
1096
1060
|
const PianoContext = (0, react.createContext)(null);
|
|
1097
|
-
function PianoProvider({ children
|
|
1061
|
+
function PianoProvider({ children, ...props }) {
|
|
1098
1062
|
const storeRef = (0, react.useRef)(null);
|
|
1099
1063
|
if (!storeRef.current) storeRef.current = createPianoStore(props);
|
|
1100
1064
|
(0, react.useEffect)(() => {
|
|
@@ -1106,17 +1070,14 @@ function PianoProvider({ children,...props }) {
|
|
|
1106
1070
|
children
|
|
1107
1071
|
});
|
|
1108
1072
|
}
|
|
1109
|
-
/** @category Piano */
|
|
1110
1073
|
function usePianoContext(selector) {
|
|
1111
1074
|
const store = (0, react.useContext)(PianoContext);
|
|
1112
1075
|
if (!store) throw new Error("Missing PianoContext.Provider in the tree");
|
|
1113
1076
|
return (0, zustand.useStore)(store, selector);
|
|
1114
1077
|
}
|
|
1115
|
-
|
|
1116
1078
|
//#endregion
|
|
1117
1079
|
//#region src/components/Piano/KeyLabel.tsx
|
|
1118
|
-
|
|
1119
|
-
function KeyLabel({ label, className, wrapperClassName, wrapperStyle, __note, __label,...props }) {
|
|
1080
|
+
function KeyLabel({ label, className, wrapperClassName, wrapperStyle, __note, __label, ...props }) {
|
|
1120
1081
|
const index = getNoteRangeArray(usePianoContext((s) => s.noteRange)).indexOf(__note);
|
|
1121
1082
|
const content = label ? label(__note, index) : __label && __label(__note, index);
|
|
1122
1083
|
return (content != void 0 || content != null) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -1129,14 +1090,7 @@ function KeyLabel({ label, className, wrapperClassName, wrapperStyle, __note, __
|
|
|
1129
1090
|
})
|
|
1130
1091
|
});
|
|
1131
1092
|
}
|
|
1132
|
-
|
|
1133
|
-
//#endregion
|
|
1134
|
-
//#region src/components/Piano/key.tsx
|
|
1135
|
-
/** @category Piano */
|
|
1136
|
-
const defaultWhiteKeyWidth = 40;
|
|
1137
|
-
/** @category Piano */
|
|
1138
|
-
const defaultBlackKeyWidth = 26;
|
|
1139
|
-
const KeyImpl = (0, react.forwardRef)(({ keyType, noteNumber, width, height, bg, color, activeBg, activeColor, className, style, children, __width,...props }, ref) => {
|
|
1093
|
+
const KeyImpl = (0, react.forwardRef)(({ keyType, noteNumber, width, height, bg, color, activeBg, activeColor, className, style, children, __width, ...props }, ref) => {
|
|
1140
1094
|
const [played, setPlayed] = (0, react.useState)(false);
|
|
1141
1095
|
const midiMax = usePianoContext((s) => s.midiMax);
|
|
1142
1096
|
const fill = usePianoContext((s) => s.fill);
|
|
@@ -1198,8 +1152,7 @@ const KeyImpl = (0, react.forwardRef)(({ keyType, noteNumber, width, height, bg,
|
|
|
1198
1152
|
})
|
|
1199
1153
|
});
|
|
1200
1154
|
});
|
|
1201
|
-
|
|
1202
|
-
const WhiteKey = (0, react.forwardRef)(({ width = defaultWhiteKeyWidth, height = "100%", onContextMenu = (e) => e.preventDefault(),...props }, ref) => {
|
|
1155
|
+
const WhiteKey = (0, react.forwardRef)(({ width = 40, height = "100%", onContextMenu = (e) => e.preventDefault(), ...props }, ref) => {
|
|
1203
1156
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KeyImpl, {
|
|
1204
1157
|
ref,
|
|
1205
1158
|
keyType: "white",
|
|
@@ -1209,8 +1162,7 @@ const WhiteKey = (0, react.forwardRef)(({ width = defaultWhiteKeyWidth, height =
|
|
|
1209
1162
|
...props
|
|
1210
1163
|
});
|
|
1211
1164
|
});
|
|
1212
|
-
|
|
1213
|
-
const BlackKey = (0, react.forwardRef)(({ width = defaultBlackKeyWidth, height = "60%", onContextMenu = (e) => e.preventDefault(),...props }, ref) => {
|
|
1165
|
+
const BlackKey = (0, react.forwardRef)(({ width = 26, height = "60%", onContextMenu = (e) => e.preventDefault(), ...props }, ref) => {
|
|
1214
1166
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KeyImpl, {
|
|
1215
1167
|
ref,
|
|
1216
1168
|
keyType: "black",
|
|
@@ -1220,10 +1172,8 @@ const BlackKey = (0, react.forwardRef)(({ width = defaultBlackKeyWidth, height =
|
|
|
1220
1172
|
...props
|
|
1221
1173
|
});
|
|
1222
1174
|
});
|
|
1223
|
-
|
|
1224
1175
|
//#endregion
|
|
1225
1176
|
//#region src/components/Piano/keyboardShortcuts.ts
|
|
1226
|
-
/** @category Piano */
|
|
1227
1177
|
const SHORTCUTS = { HOME_ROW: { keys: [
|
|
1228
1178
|
"a",
|
|
1229
1179
|
"w",
|
|
@@ -1243,203 +1193,406 @@ const SHORTCUTS = { HOME_ROW: { keys: [
|
|
|
1243
1193
|
"p",
|
|
1244
1194
|
";"
|
|
1245
1195
|
] } };
|
|
1246
|
-
|
|
1247
1196
|
//#endregion
|
|
1248
1197
|
//#region src/components/Piano/index.tsx
|
|
1249
1198
|
/**
|
|
1250
1199
|
* [noteRange.first, noteRange.first + 1 ..., noteRange.last]
|
|
1251
|
-
* @category Piano
|
|
1252
1200
|
*/
|
|
1253
1201
|
function getNoteRangeArray(noteRange) {
|
|
1254
1202
|
return Array.from({ length: noteRange.last - noteRange.first + 1 }, (_, i) => i + noteRange.first);
|
|
1255
1203
|
}
|
|
1256
|
-
const blackPerWhiteWidth =
|
|
1204
|
+
const blackPerWhiteWidth = 26 / 40;
|
|
1257
1205
|
/**
|
|
1258
1206
|
* Customizable piano component.
|
|
1259
|
-
* @category Piano
|
|
1260
1207
|
*/
|
|
1261
|
-
const Piano =
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1208
|
+
const Piano = {
|
|
1209
|
+
Root: (0, react.forwardRef)(({ noteRange, glissando = true, midiMax = 127, keyboardShortcuts, fill = false, height = fill ? "100%" : 160, whiteNoteWidth: _whiteNoteWidth = 40, style, className, onPlayNote, onStopNote, label, children, onPointerDown, ...props }, forwardedRef) => {
|
|
1210
|
+
const [whiteNoteWidth, setWhiteNoteWidth] = (0, react.useState)(_whiteNoteWidth);
|
|
1211
|
+
const keyRefs = (0, react.useRef)([]);
|
|
1212
|
+
for (let i = 0; i < noteRange.last - noteRange.first + 1; i++) keyRefs.current[i] = (0, react.createRef)();
|
|
1213
|
+
const pianoRef = (0, react.useRef)(null);
|
|
1214
|
+
const hitKeyIndex = (0, react.useRef)(-1);
|
|
1215
|
+
const noteRangeArray = getNoteRangeArray(noteRange);
|
|
1216
|
+
const whiteNotes = noteRangeArray.filter((v) => (0, _tremolo_ui_functions.isWhiteKey)(v));
|
|
1217
|
+
const blackNotes = noteRangeArray.filter((v) => (0, _tremolo_ui_functions.isBlackKey)(v));
|
|
1218
|
+
const whiteNoteCount = whiteNotes.length;
|
|
1219
|
+
const padding = 1;
|
|
1220
|
+
const staticWidth = (whiteNoteWidth + padding) * whiteNoteCount;
|
|
1221
|
+
const childrenWithProps = (0, react.useMemo)(() => {
|
|
1222
|
+
return children && react.default.Children.map(children, (child, index) => {
|
|
1223
|
+
if (react.default.isValidElement(child)) {
|
|
1224
|
+
const __width = child.type == WhiteKey ? whiteNoteWidth : whiteNoteWidth * .65;
|
|
1225
|
+
const props = {
|
|
1226
|
+
ref: keyRefs.current[index],
|
|
1227
|
+
__width
|
|
1228
|
+
};
|
|
1229
|
+
return react.default.cloneElement(child, props);
|
|
1230
|
+
}
|
|
1231
|
+
return child;
|
|
1232
|
+
});
|
|
1233
|
+
}, [children, whiteNoteWidth]);
|
|
1234
|
+
const notePosition = (0, react.useCallback)((note) => {
|
|
1235
|
+
const pitchPositions = {
|
|
1236
|
+
C: 0,
|
|
1237
|
+
"C#": 1,
|
|
1238
|
+
D: 1,
|
|
1239
|
+
"D#": 2,
|
|
1240
|
+
E: 2,
|
|
1241
|
+
F: 3,
|
|
1242
|
+
"F#": 4,
|
|
1243
|
+
G: 4,
|
|
1244
|
+
"G#": 5,
|
|
1245
|
+
A: 5,
|
|
1246
|
+
"A#": 6,
|
|
1247
|
+
B: 6
|
|
1248
|
+
};
|
|
1249
|
+
const blackNoteWidth = whiteNoteWidth * blackPerWhiteWidth;
|
|
1250
|
+
const padding = 1;
|
|
1251
|
+
const targetNoteKey = (0, _tremolo_ui_functions.noteKey)(note);
|
|
1252
|
+
const firstNoteKey = (0, _tremolo_ui_functions.noteKey)(noteRange.first);
|
|
1253
|
+
const octave = Math.floor((note - noteRange.first) / 12);
|
|
1254
|
+
const octaveOffset = _tremolo_ui_functions.noteKeys.indexOf(firstNoteKey) > _tremolo_ui_functions.noteKeys.indexOf(targetNoteKey) ? 1 : 0;
|
|
1255
|
+
const w = whiteNoteWidth + padding;
|
|
1256
|
+
const pos = pitchPositions[targetNoteKey] - pitchPositions[firstNoteKey];
|
|
1257
|
+
const blackKeyOffset = (0, _tremolo_ui_functions.isBlackKey)(note) ? blackNoteWidth / 2 : 0;
|
|
1258
|
+
return pos * w + (octave + octaveOffset) * 7 * w - blackKeyOffset;
|
|
1259
|
+
}, [noteRange.first, whiteNoteWidth]);
|
|
1260
|
+
const getHitKeyIndex = (0, react.useCallback)((x, y) => {
|
|
1261
|
+
if (!pianoRef.current) return -1;
|
|
1262
|
+
const containerHeight = pianoRef.current.clientHeight;
|
|
1263
|
+
const notes = [...blackNotes, ...whiteNotes];
|
|
1264
|
+
for (let i = 0; i < notes.length; i++) {
|
|
1265
|
+
const note = notes[i];
|
|
1266
|
+
const pos = notePosition(note);
|
|
1267
|
+
const w = (0, _tremolo_ui_functions.isWhiteKey)(note) ? whiteNoteWidth : whiteNoteWidth * blackPerWhiteWidth;
|
|
1268
|
+
const h = (0, _tremolo_ui_functions.isWhiteKey)(note) ? containerHeight : containerHeight * .6;
|
|
1269
|
+
if (pos <= x && x < pos + w && 0 <= y && y < h) return note;
|
|
1270
|
+
}
|
|
1271
|
+
return -1;
|
|
1272
|
+
}, [
|
|
1273
|
+
blackNotes,
|
|
1274
|
+
notePosition,
|
|
1275
|
+
whiteNoteWidth,
|
|
1276
|
+
whiteNotes
|
|
1277
|
+
]);
|
|
1278
|
+
const onDrag = (0, react.useCallback)((perX, perY) => {
|
|
1279
|
+
if (!pianoRef.current) return;
|
|
1280
|
+
const note = getHitKeyIndex(perX * staticWidth, perY * pianoRef.current.clientHeight);
|
|
1281
|
+
const index = noteRangeArray.indexOf(note);
|
|
1282
|
+
if (index == -1) return;
|
|
1283
|
+
if (hitKeyIndex.current != index) {
|
|
1284
|
+
keyRefs.current[hitKeyIndex.current]?.current?.stop();
|
|
1285
|
+
keyRefs.current[index]?.current?.play();
|
|
1286
|
+
hitKeyIndex.current = index;
|
|
1287
|
+
}
|
|
1288
|
+
}, [
|
|
1289
|
+
getHitKeyIndex,
|
|
1290
|
+
noteRangeArray,
|
|
1291
|
+
staticWidth
|
|
1292
|
+
]);
|
|
1293
|
+
const onDragEnd = (0, react.useCallback)(() => {
|
|
1294
|
+
if (keyRefs.current[hitKeyIndex.current]?.current?.played()) keyRefs.current[hitKeyIndex.current]?.current?.stop();
|
|
1295
|
+
hitKeyIndex.current = -1;
|
|
1296
|
+
}, [keyRefs]);
|
|
1297
|
+
(0, react.useEffect)(() => {
|
|
1298
|
+
if (fill && pianoRef.current) {
|
|
1299
|
+
const parent = pianoRef.current.parentElement;
|
|
1300
|
+
if (!parent) throw new Error("doesn't have a parent element.");
|
|
1301
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
1302
|
+
const w = pianoRef.current.clientWidth;
|
|
1303
|
+
setWhiteNoteWidth(w / whiteNoteCount - padding);
|
|
1304
|
+
});
|
|
1305
|
+
resizeObserver.observe(parent);
|
|
1306
|
+
return () => {
|
|
1307
|
+
resizeObserver.unobserve(parent);
|
|
1280
1308
|
};
|
|
1281
|
-
|
|
1309
|
+
} else setWhiteNoteWidth(_whiteNoteWidth);
|
|
1310
|
+
}, [
|
|
1311
|
+
fill,
|
|
1312
|
+
_whiteNoteWidth,
|
|
1313
|
+
whiteNoteCount
|
|
1314
|
+
]);
|
|
1315
|
+
const { refHandler: touchMoveRefCallback, pointerDownHandler } = usePianoDrag({
|
|
1316
|
+
baseElementRef: pianoRef,
|
|
1317
|
+
onDrag,
|
|
1318
|
+
onDragEnd
|
|
1319
|
+
});
|
|
1320
|
+
useEventListener(globalThis.window, "keydown", (e) => {
|
|
1321
|
+
if (e.repeat) return;
|
|
1322
|
+
if (!keyboardShortcuts) return;
|
|
1323
|
+
const index = keyboardShortcuts.keys.indexOf(e.key);
|
|
1324
|
+
if (index != -1) {
|
|
1325
|
+
if (!keyRefs.current[index]?.current?.played()) keyRefs.current[index]?.current?.play();
|
|
1282
1326
|
}
|
|
1283
|
-
return child;
|
|
1284
1327
|
});
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
return
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1328
|
+
useEventListener(globalThis.window, "keyup", (e) => {
|
|
1329
|
+
if (e.repeat) return;
|
|
1330
|
+
if (!keyboardShortcuts) return;
|
|
1331
|
+
const index = keyboardShortcuts.keys.indexOf(e.key);
|
|
1332
|
+
if (index != -1) keyRefs.current[index]?.current?.stop();
|
|
1333
|
+
});
|
|
1334
|
+
(0, react.useImperativeHandle)(forwardedRef, () => {
|
|
1335
|
+
return {
|
|
1336
|
+
playNote(note, velocity) {
|
|
1337
|
+
const index = noteRangeArray.indexOf(note);
|
|
1338
|
+
if (index != -1) {
|
|
1339
|
+
if (!keyRefs.current[index]?.current?.played()) keyRefs.current[index]?.current?.play(velocity);
|
|
1340
|
+
} else onPlayNote?.(note, velocity);
|
|
1341
|
+
},
|
|
1342
|
+
stopNote(note) {
|
|
1343
|
+
const index = noteRangeArray.indexOf(note);
|
|
1344
|
+
if (index != -1) keyRefs.current[index]?.current?.stop();
|
|
1345
|
+
else onStopNote?.(note);
|
|
1346
|
+
}
|
|
1347
|
+
};
|
|
1348
|
+
}, [
|
|
1349
|
+
noteRangeArray,
|
|
1350
|
+
onPlayNote,
|
|
1351
|
+
onStopNote
|
|
1352
|
+
]);
|
|
1353
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PianoProvider, {
|
|
1354
|
+
notePosition,
|
|
1355
|
+
noteRange,
|
|
1356
|
+
glissando,
|
|
1357
|
+
midiMax,
|
|
1358
|
+
fill,
|
|
1359
|
+
onPlayNote,
|
|
1360
|
+
onStopNote,
|
|
1361
|
+
label,
|
|
1362
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1363
|
+
ref: (div) => {
|
|
1364
|
+
pianoRef.current = div;
|
|
1365
|
+
touchMoveRefCallback(div);
|
|
1366
|
+
},
|
|
1367
|
+
className: (0, clsx.default)("tremolo-piano", className),
|
|
1368
|
+
style: {
|
|
1369
|
+
width: fill ? "100%" : staticWidth,
|
|
1370
|
+
height,
|
|
1371
|
+
...style
|
|
1372
|
+
},
|
|
1373
|
+
onPointerDown: (event) => {
|
|
1374
|
+
pointerDownHandler(event);
|
|
1375
|
+
onPointerDown?.(event);
|
|
1376
|
+
},
|
|
1377
|
+
...props,
|
|
1378
|
+
children: childrenWithProps || noteRangeArray.map((note, index) => (0, _tremolo_ui_functions.isWhiteKey)(note) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WhiteKey, {
|
|
1379
|
+
ref: keyRefs.current[index],
|
|
1380
|
+
noteNumber: note,
|
|
1381
|
+
__width: whiteNoteWidth
|
|
1382
|
+
}, note) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlackKey, {
|
|
1383
|
+
ref: keyRefs.current[index],
|
|
1384
|
+
noteNumber: note,
|
|
1385
|
+
__width: whiteNoteWidth * blackPerWhiteWidth
|
|
1386
|
+
}, note))
|
|
1387
|
+
})
|
|
1388
|
+
});
|
|
1389
|
+
}),
|
|
1390
|
+
WhiteKey,
|
|
1391
|
+
BlackKey,
|
|
1392
|
+
KeyLabel
|
|
1393
|
+
};
|
|
1394
|
+
//#endregion
|
|
1395
|
+
//#region src/components/PointsEditor/Background.tsx
|
|
1396
|
+
function Background({ className, children, ...props }) {
|
|
1397
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1398
|
+
className: (0, clsx.default)("tremolo-points-editor-background", className),
|
|
1399
|
+
...props,
|
|
1400
|
+
children
|
|
1401
|
+
});
|
|
1402
|
+
}
|
|
1403
|
+
//#endregion
|
|
1404
|
+
//#region src/components/PointsEditor/context.tsx
|
|
1405
|
+
const createPointsEditorStore = (initProps) => {
|
|
1406
|
+
const DEFAULT_PROPS = {
|
|
1407
|
+
containerElementRef: (0, react.createRef)(),
|
|
1408
|
+
disabled: false,
|
|
1409
|
+
readonly: false,
|
|
1410
|
+
externalStyles: {
|
|
1411
|
+
userSelectNone: true,
|
|
1412
|
+
cursor: "grabbing"
|
|
1322
1413
|
}
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
]);
|
|
1330
|
-
const onDrag = (0, react.useCallback)((perX, perY) => {
|
|
1331
|
-
if (!pianoRef.current) return;
|
|
1332
|
-
const note = getHitKeyIndex(perX * staticWidth, perY * pianoRef.current.clientHeight);
|
|
1333
|
-
const index = noteRangeArray.indexOf(note);
|
|
1334
|
-
if (index == -1) return;
|
|
1335
|
-
if (hitKeyIndex.current != index) {
|
|
1336
|
-
keyRefs.current[hitKeyIndex.current]?.current?.stop();
|
|
1337
|
-
keyRefs.current[index]?.current?.play();
|
|
1338
|
-
hitKeyIndex.current = index;
|
|
1414
|
+
};
|
|
1415
|
+
return (0, zustand.createStore)()((set) => ({
|
|
1416
|
+
...DEFAULT_PROPS,
|
|
1417
|
+
...initProps,
|
|
1418
|
+
setContainerElementRef: (containerElementRef) => {
|
|
1419
|
+
set(() => ({ containerElementRef }));
|
|
1339
1420
|
}
|
|
1340
|
-
}
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
if (keyRefs.current[hitKeyIndex.current]?.current?.played()) keyRefs.current[hitKeyIndex.current]?.current?.stop();
|
|
1347
|
-
hitKeyIndex.current = -1;
|
|
1348
|
-
}, [keyRefs]);
|
|
1421
|
+
}));
|
|
1422
|
+
};
|
|
1423
|
+
const PointsEditorContext = (0, react.createContext)(null);
|
|
1424
|
+
function PointsEditorProvider({ children, ...props }) {
|
|
1425
|
+
const storeRef = (0, react.useRef)(null);
|
|
1426
|
+
if (!storeRef.current) storeRef.current = createPointsEditorStore(props);
|
|
1349
1427
|
(0, react.useEffect)(() => {
|
|
1350
|
-
if (
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1428
|
+
if (storeRef.current) storeRef.current.setState(props);
|
|
1429
|
+
else storeRef.current = createPointsEditorStore(props);
|
|
1430
|
+
}, [props]);
|
|
1431
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PointsEditorContext.Provider, {
|
|
1432
|
+
value: storeRef.current,
|
|
1433
|
+
children
|
|
1434
|
+
});
|
|
1435
|
+
}
|
|
1436
|
+
function usePointsEditorContext(selector) {
|
|
1437
|
+
const store = (0, react.useContext)(PointsEditorContext);
|
|
1438
|
+
if (!store) throw new Error("Missing PointsEditorContext.Provider in the tree");
|
|
1439
|
+
return (0, zustand.useStore)(store, selector);
|
|
1440
|
+
}
|
|
1441
|
+
//#endregion
|
|
1442
|
+
//#region src/components/PointsEditor/Container.tsx
|
|
1443
|
+
function Container({ className, children, ...props }) {
|
|
1444
|
+
const containerElementRef = (0, react.useRef)(null);
|
|
1445
|
+
usePointsEditorContext((s) => s.setContainerElementRef)(containerElementRef);
|
|
1446
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1447
|
+
ref: containerElementRef,
|
|
1448
|
+
className: (0, clsx.default)("tremolo-points-editor-container", className),
|
|
1449
|
+
...props,
|
|
1450
|
+
children
|
|
1451
|
+
});
|
|
1452
|
+
}
|
|
1453
|
+
//#endregion
|
|
1454
|
+
//#region src/hooks/useDragWithElement.ts
|
|
1455
|
+
function useDragWithElement({ baseElementRef, onDrag, onDragStart, onDragEnd }) {
|
|
1456
|
+
const [dragging, setDragging] = (0, react.useState)(false);
|
|
1457
|
+
const normalizedX = (0, react.useRef)(0);
|
|
1458
|
+
const normalizedY = (0, react.useRef)(0);
|
|
1459
|
+
const handleDrag = (0, react.useCallback)((event) => {
|
|
1460
|
+
if (!baseElementRef.current || !dragging) return;
|
|
1461
|
+
const isTouch = event instanceof TouchEvent;
|
|
1462
|
+
if (isTouch && event.cancelable) event.preventDefault();
|
|
1463
|
+
const { left, top, right, bottom } = baseElementRef.current.getBoundingClientRect();
|
|
1464
|
+
const mouseX = isTouch ? event.touches[0].clientX : event.clientX;
|
|
1465
|
+
const mouseY = isTouch ? event.touches[0].clientY : event.clientY;
|
|
1466
|
+
const nx = (0, _tremolo_ui_functions.normalizeValue)(mouseX, left, right);
|
|
1467
|
+
const ny = (0, _tremolo_ui_functions.normalizeValue)(mouseY, top, bottom);
|
|
1468
|
+
normalizedX.current = nx;
|
|
1469
|
+
normalizedY.current = ny;
|
|
1470
|
+
onDrag(nx, ny);
|
|
1362
1471
|
}, [
|
|
1363
|
-
fill,
|
|
1364
|
-
_whiteNoteWidth,
|
|
1365
|
-
whiteNoteCount
|
|
1366
|
-
]);
|
|
1367
|
-
const [touchMoveRefCallback, pointerDownHandler] = useDragWithElement({
|
|
1368
|
-
baseElementRef: pianoRef,
|
|
1369
1472
|
onDrag,
|
|
1370
|
-
|
|
1473
|
+
baseElementRef,
|
|
1474
|
+
dragging
|
|
1475
|
+
]);
|
|
1476
|
+
const refHandler = useRefCallbackEvent("touchmove", handleDrag, { passive: false }, [onDrag]);
|
|
1477
|
+
const pointerDownHandler = (0, react.useCallback)((event) => {
|
|
1478
|
+
setDragging(true);
|
|
1479
|
+
handleDrag(event);
|
|
1480
|
+
onDragStart?.(normalizedX.current, normalizedY.current);
|
|
1481
|
+
}, [handleDrag, onDragStart]);
|
|
1482
|
+
useEventListener(globalThis.window, "pointermove", handleDrag);
|
|
1483
|
+
useEventListener(globalThis.window, "pointerup", () => {
|
|
1484
|
+
if (!dragging) return;
|
|
1485
|
+
setDragging(false);
|
|
1486
|
+
onDragEnd?.(normalizedX.current, normalizedY.current);
|
|
1371
1487
|
});
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1488
|
+
return {
|
|
1489
|
+
refHandler,
|
|
1490
|
+
pointerDownHandler,
|
|
1491
|
+
dragging
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
//#endregion
|
|
1495
|
+
//#region src/components/PointsEditor/Point.tsx
|
|
1496
|
+
function clampPoint(point, min, max) {
|
|
1497
|
+
const { x, y } = point;
|
|
1498
|
+
return {
|
|
1499
|
+
x: (0, _tremolo_ui_functions.clamp)(x, min?.x ?? 0, max?.x ?? 1),
|
|
1500
|
+
y: (0, _tremolo_ui_functions.clamp)(y, min?.y ?? 0, max?.y ?? 1)
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1503
|
+
function Point({ value, min, max, size, width = 16, height = 16, color, disabled, readonly, onChange, onDragStart, onDragEnd, className, style, onPointerDown, ...props }) {
|
|
1504
|
+
const containerElementRef = usePointsEditorContext((s) => s.containerElementRef);
|
|
1505
|
+
const externalStyles = usePointsEditorContext((s) => s.externalStyles);
|
|
1506
|
+
const __disabled = usePointsEditorContext((s) => s.disabled);
|
|
1507
|
+
const __readonly = usePointsEditorContext((s) => s.readonly);
|
|
1508
|
+
const { refHandler: touchMoveRefCallback, pointerDownHandler, dragging } = useDragWithElement({
|
|
1509
|
+
baseElementRef: containerElementRef,
|
|
1510
|
+
onDrag: (x, y) => {
|
|
1511
|
+
if (readonly) return;
|
|
1512
|
+
onChange?.(clampPoint({
|
|
1513
|
+
x,
|
|
1514
|
+
y
|
|
1515
|
+
}, min, max));
|
|
1516
|
+
},
|
|
1517
|
+
onDragStart: (x, y) => {
|
|
1518
|
+
if (readonly) return;
|
|
1519
|
+
if (externalStyles.userSelectNone) addUserSelectNone();
|
|
1520
|
+
if (externalStyles.cursor) setCursorStyle(externalStyles.cursor);
|
|
1521
|
+
onDragStart?.(clampPoint({
|
|
1522
|
+
x,
|
|
1523
|
+
y
|
|
1524
|
+
}, min, max));
|
|
1525
|
+
},
|
|
1526
|
+
onDragEnd: (x, y) => {
|
|
1527
|
+
if (readonly) return;
|
|
1528
|
+
if (externalStyles.userSelectNone) removeUserSelectNone();
|
|
1529
|
+
if (externalStyles.cursor) resetCursorStyle();
|
|
1530
|
+
onDragEnd?.(clampPoint({
|
|
1531
|
+
x,
|
|
1532
|
+
y
|
|
1533
|
+
}, min, max));
|
|
1378
1534
|
}
|
|
1379
1535
|
});
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
onStopNote
|
|
1404
|
-
]);
|
|
1405
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PianoProvider, {
|
|
1406
|
-
notePosition,
|
|
1407
|
-
noteRange,
|
|
1408
|
-
glissando,
|
|
1409
|
-
midiMax,
|
|
1410
|
-
fill,
|
|
1411
|
-
onPlayNote,
|
|
1412
|
-
onStopNote,
|
|
1413
|
-
label,
|
|
1414
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1415
|
-
ref: (div) => {
|
|
1416
|
-
pianoRef.current = div;
|
|
1417
|
-
touchMoveRefCallback(div);
|
|
1418
|
-
},
|
|
1419
|
-
className: (0, clsx.default)("tremolo-piano", className),
|
|
1420
|
-
style: {
|
|
1421
|
-
width: fill ? "100%" : staticWidth,
|
|
1422
|
-
height,
|
|
1423
|
-
...style
|
|
1424
|
-
},
|
|
1425
|
-
onPointerDown: (event) => {
|
|
1426
|
-
pointerDownHandler(event);
|
|
1427
|
-
onPointerDown?.(event);
|
|
1428
|
-
},
|
|
1429
|
-
...props,
|
|
1430
|
-
children: childrenWithProps || noteRangeArray.map((note, index) => (0, __tremolo_ui_functions.isWhiteKey)(note) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WhiteKey, {
|
|
1431
|
-
ref: keyRefs.current[index],
|
|
1432
|
-
noteNumber: note,
|
|
1433
|
-
__width: whiteNoteWidth
|
|
1434
|
-
}, note) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlackKey, {
|
|
1435
|
-
ref: keyRefs.current[index],
|
|
1436
|
-
noteNumber: note,
|
|
1437
|
-
__width: whiteNoteWidth * blackPerWhiteWidth
|
|
1438
|
-
}, note))
|
|
1439
|
-
})
|
|
1536
|
+
const colors = { "--color": color };
|
|
1537
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1538
|
+
ref: (div) => {
|
|
1539
|
+
touchMoveRefCallback(div);
|
|
1540
|
+
},
|
|
1541
|
+
className: (0, clsx.default)("tremolo-points-editor-point", className),
|
|
1542
|
+
tabIndex: 0,
|
|
1543
|
+
"aria-disabled": disabled != void 0 ? disabled : __disabled,
|
|
1544
|
+
"aria-readonly": readonly != void 0 ? readonly : __readonly,
|
|
1545
|
+
"data-dragging": dragging,
|
|
1546
|
+
style: {
|
|
1547
|
+
...colors,
|
|
1548
|
+
width: size ?? width,
|
|
1549
|
+
height: size ?? height,
|
|
1550
|
+
left: `${value.x * 100}%`,
|
|
1551
|
+
top: `${value.y * 100}%`,
|
|
1552
|
+
...style
|
|
1553
|
+
},
|
|
1554
|
+
onPointerDown: (event) => {
|
|
1555
|
+
pointerDownHandler(event);
|
|
1556
|
+
onPointerDown?.(event);
|
|
1557
|
+
},
|
|
1558
|
+
...props
|
|
1440
1559
|
});
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1560
|
+
}
|
|
1561
|
+
//#endregion
|
|
1562
|
+
//#region src/components/PointsEditor/index.tsx
|
|
1563
|
+
const defaultExternalStyles$2 = {
|
|
1564
|
+
userSelectNone: true,
|
|
1565
|
+
cursor: "grabbing"
|
|
1566
|
+
};
|
|
1567
|
+
/**
|
|
1568
|
+
* Multiple Point Controller
|
|
1569
|
+
*/
|
|
1570
|
+
const PointsEditor = {
|
|
1571
|
+
Root: (0, react.forwardRef)(({ width = 200, height = 100, disabled = false, readonly = false, externalStyles: _externalStyles, style, className, children, ...props }, forwardedRef) => {
|
|
1572
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PointsEditorProvider, {
|
|
1573
|
+
disabled,
|
|
1574
|
+
readonly,
|
|
1575
|
+
externalStyles: {
|
|
1576
|
+
...defaultExternalStyles$2,
|
|
1577
|
+
..._externalStyles
|
|
1578
|
+
},
|
|
1579
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1580
|
+
ref: forwardedRef,
|
|
1581
|
+
className: (0, clsx.default)("tremolo-points-editor", className),
|
|
1582
|
+
style: {
|
|
1583
|
+
width,
|
|
1584
|
+
height,
|
|
1585
|
+
...style
|
|
1586
|
+
},
|
|
1587
|
+
...props,
|
|
1588
|
+
children
|
|
1589
|
+
})
|
|
1590
|
+
});
|
|
1591
|
+
}),
|
|
1592
|
+
Background,
|
|
1593
|
+
Container,
|
|
1594
|
+
Point
|
|
1595
|
+
};
|
|
1443
1596
|
//#endregion
|
|
1444
1597
|
//#region src/components/Slider/context.tsx
|
|
1445
1598
|
const createSliderStore = (initProps) => {
|
|
@@ -1459,7 +1612,7 @@ const createSliderStore = (initProps) => {
|
|
|
1459
1612
|
}));
|
|
1460
1613
|
};
|
|
1461
1614
|
const SliderContext = (0, react.createContext)(null);
|
|
1462
|
-
function SliderProvider({ children
|
|
1615
|
+
function SliderProvider({ children, ...props }) {
|
|
1463
1616
|
const storeRef = (0, react.useRef)(null);
|
|
1464
1617
|
if (!storeRef.current) storeRef.current = createSliderStore(props);
|
|
1465
1618
|
(0, react.useEffect)(() => {
|
|
@@ -1471,24 +1624,21 @@ function SliderProvider({ children,...props }) {
|
|
|
1471
1624
|
children
|
|
1472
1625
|
});
|
|
1473
1626
|
}
|
|
1474
|
-
/** @category Slider */
|
|
1475
1627
|
function useSliderContext(selector) {
|
|
1476
1628
|
const store = (0, react.useContext)(SliderContext);
|
|
1477
1629
|
if (!store) throw new Error("Missing SliderContext.Provider in the tree");
|
|
1478
1630
|
return (0, zustand.useStore)(store, selector);
|
|
1479
1631
|
}
|
|
1480
|
-
|
|
1481
1632
|
//#endregion
|
|
1482
1633
|
//#region src/components/Slider/ScaleOption.tsx
|
|
1483
|
-
|
|
1484
|
-
function ScaleOption({ value, type = "mark-number", label, thickness = 1, length = "0.5rem", gap = 2, labelWidth, classes, styles, className, style,...props }) {
|
|
1634
|
+
function ScaleOption({ value, type = "mark-number", label, thickness = 1, length = "0.5rem", gap = 2, labelWidth, classes, styles, className, style, ...props }) {
|
|
1485
1635
|
const min = useSliderContext((s) => s.min);
|
|
1486
1636
|
const max = useSliderContext((s) => s.max);
|
|
1487
1637
|
const skew = useSliderContext((s) => s.skew);
|
|
1488
1638
|
const vertical = useSliderContext((s) => s.vertical);
|
|
1489
1639
|
const reverse = useSliderContext((s) => s.reverse);
|
|
1490
|
-
const calcPercent = (0, react.useCallback)((rawValue
|
|
1491
|
-
return (0,
|
|
1640
|
+
const calcPercent = (0, react.useCallback)((rawValue) => {
|
|
1641
|
+
return (0, _tremolo_ui_functions.toFixed)((0, _tremolo_ui_functions.xor)(vertical, reverse) ? 100 - (0, _tremolo_ui_functions.normalizeValue)(rawValue, min, max, skew) * 100 : (0, _tremolo_ui_functions.normalizeValue)(rawValue, min, max, skew) * 100);
|
|
1492
1642
|
}, [
|
|
1493
1643
|
vertical,
|
|
1494
1644
|
reverse,
|
|
@@ -1525,7 +1675,6 @@ function ScaleOption({ value, type = "mark-number", label, thickness = 1, length
|
|
|
1525
1675
|
})]
|
|
1526
1676
|
});
|
|
1527
1677
|
}
|
|
1528
|
-
|
|
1529
1678
|
//#endregion
|
|
1530
1679
|
//#region src/components/Slider/type.ts
|
|
1531
1680
|
function generateOptionsList(options, min, max, step) {
|
|
@@ -1533,7 +1682,7 @@ function generateOptionsList(options, min, max, step) {
|
|
|
1533
1682
|
const per = options[0] == "step" ? step : options[0];
|
|
1534
1683
|
const count = Math.floor(max / per) - Math.ceil(min / per) + 1;
|
|
1535
1684
|
for (let i = 0; i < count; i++) {
|
|
1536
|
-
const value = (0,
|
|
1685
|
+
const value = (0, _tremolo_ui_functions.toFixed)(per * (Math.ceil(min / per) + i), (0, _tremolo_ui_functions.decimalPart)(per)?.length);
|
|
1537
1686
|
optionsList.push({
|
|
1538
1687
|
value,
|
|
1539
1688
|
type: options[1]
|
|
@@ -1541,18 +1690,16 @@ function generateOptionsList(options, min, max, step) {
|
|
|
1541
1690
|
}
|
|
1542
1691
|
return optionsList;
|
|
1543
1692
|
}
|
|
1544
|
-
|
|
1545
1693
|
//#endregion
|
|
1546
1694
|
//#region src/components/Slider/Scale.tsx
|
|
1547
|
-
|
|
1548
|
-
function Scale({ gap = 6, options, children, className, style,...props }) {
|
|
1695
|
+
function Scale({ gap = 6, options, children, className, style, ...props }) {
|
|
1549
1696
|
const min = useSliderContext((s) => s.min);
|
|
1550
1697
|
const max = useSliderContext((s) => s.max);
|
|
1551
1698
|
const step = useSliderContext((s) => s.step);
|
|
1552
1699
|
const vertical = useSliderContext((s) => s.vertical);
|
|
1553
1700
|
const reverse = useSliderContext((s) => s.reverse);
|
|
1554
1701
|
const optionsList = options ? generateOptionsList(options, min, max, step) : [];
|
|
1555
|
-
if ((0,
|
|
1702
|
+
if ((0, _tremolo_ui_functions.xor)(vertical, reverse)) optionsList.reverse();
|
|
1556
1703
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1557
1704
|
className: (0, clsx.default)("tremolo-slider-scale", className),
|
|
1558
1705
|
style: {
|
|
@@ -1569,13 +1716,7 @@ function Scale({ gap = 6, options, children, className, style,...props }) {
|
|
|
1569
1716
|
}) : children
|
|
1570
1717
|
});
|
|
1571
1718
|
}
|
|
1572
|
-
|
|
1573
|
-
//#endregion
|
|
1574
|
-
//#region src/components/Slider/Thumb.tsx
|
|
1575
|
-
/** @category Slider */
|
|
1576
|
-
const defaultThumbSize = 22;
|
|
1577
|
-
/** @category Slider */
|
|
1578
|
-
const SliderThumb = (0, react.forwardRef)(({ size, width = defaultThumbSize, height = defaultThumbSize, color, children, className, style, __percent }, ref) => {
|
|
1719
|
+
const Thumb$1 = (0, react.forwardRef)(({ size, width = 22, height = 22, color, children, className, style, __percent }, ref) => {
|
|
1579
1720
|
const wrapperRef = (0, react.useRef)(null);
|
|
1580
1721
|
const vertical = useSliderContext((s) => s.vertical);
|
|
1581
1722
|
const disabled = useSliderContext((s) => s.disabled);
|
|
@@ -1611,13 +1752,7 @@ const SliderThumb = (0, react.forwardRef)(({ size, width = defaultThumbSize, hei
|
|
|
1611
1752
|
})
|
|
1612
1753
|
});
|
|
1613
1754
|
});
|
|
1614
|
-
|
|
1615
|
-
//#endregion
|
|
1616
|
-
//#region src/components/Slider/Track.tsx
|
|
1617
|
-
const defaultLength = 140;
|
|
1618
|
-
const defaultThickness = 10;
|
|
1619
|
-
/** @category Slider */
|
|
1620
|
-
function SliderTrack({ length = defaultLength, thickness = defaultThickness, active, inactive, children, className, style, defaultStyle = true, __thumb, __percent = 0,...props }) {
|
|
1755
|
+
function Track({ length = 140, thickness = 10, active, inactive, children, className, style, defaultStyle = true, __thumb, __percent = 0, ...props }) {
|
|
1621
1756
|
const vertical = useSliderContext((s) => s.vertical);
|
|
1622
1757
|
const reverse = useSliderContext((s) => s.reverse);
|
|
1623
1758
|
const disabled = useSliderContext((s) => s.disabled);
|
|
@@ -1632,8 +1767,8 @@ function SliderTrack({ length = defaultLength, thickness = defaultThickness, act
|
|
|
1632
1767
|
"data-vertical": vertical,
|
|
1633
1768
|
style: !defaultStyle ? style : {
|
|
1634
1769
|
...colors,
|
|
1635
|
-
background: (0,
|
|
1636
|
-
borderRadius: (0,
|
|
1770
|
+
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}%)`,
|
|
1771
|
+
borderRadius: (0, _tremolo_ui_functions.styleHelper)(thickness, "/", 2),
|
|
1637
1772
|
width: !vertical ? length : thickness,
|
|
1638
1773
|
height: vertical ? length : thickness,
|
|
1639
1774
|
...style
|
|
@@ -1642,207 +1777,259 @@ function SliderTrack({ length = defaultLength, thickness = defaultThickness, act
|
|
|
1642
1777
|
children: [children, __thumb]
|
|
1643
1778
|
});
|
|
1644
1779
|
}
|
|
1645
|
-
|
|
1646
1780
|
//#endregion
|
|
1647
1781
|
//#region src/components/Slider/index.tsx
|
|
1782
|
+
const defaultExternalStyles$1 = {
|
|
1783
|
+
userSelectNone: true,
|
|
1784
|
+
cursor: "pointer"
|
|
1785
|
+
};
|
|
1648
1786
|
/**
|
|
1649
1787
|
* Customizable slider
|
|
1650
|
-
* @category Slider
|
|
1651
1788
|
*/
|
|
1652
|
-
const Slider =
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
let trackProps = {};
|
|
1660
|
-
let thumbProps = {};
|
|
1661
|
-
let scaleComponent = void 0;
|
|
1662
|
-
if (children != void 0) react.default.Children.forEach(children, (child) => {
|
|
1663
|
-
if (react.default.isValidElement(child)) if (child.type == SliderThumb) thumbProps = child.props;
|
|
1664
|
-
else if (child.type == SliderTrack) trackProps = child.props;
|
|
1665
|
-
else if (child.type == Scale) scaleComponent = child;
|
|
1666
|
-
else throw new Error("only <SliderThumb> or <SliderTrack>");
|
|
1667
|
-
else throw new Error("children is an invalid element.");
|
|
1668
|
-
});
|
|
1669
|
-
const onDrag = (0, react.useCallback)((nx, ny) => {
|
|
1670
|
-
if (!onChange || readonly) return;
|
|
1671
|
-
const n = vertical ? ny : nx;
|
|
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));
|
|
1673
|
-
}, [
|
|
1674
|
-
displayReversed,
|
|
1675
|
-
max,
|
|
1676
|
-
min,
|
|
1677
|
-
onChange,
|
|
1678
|
-
readonly,
|
|
1679
|
-
skew,
|
|
1680
|
-
step,
|
|
1681
|
-
vertical
|
|
1682
|
-
]);
|
|
1683
|
-
const updateValueByEvent = (0, react.useCallback)((eventType, x) => {
|
|
1684
|
-
let newValue;
|
|
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;
|
|
1687
|
-
return (0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)(newValue, step), min, max);
|
|
1688
|
-
}, [
|
|
1689
|
-
max,
|
|
1690
|
-
min,
|
|
1691
|
-
skew,
|
|
1692
|
-
step,
|
|
1693
|
-
value
|
|
1694
|
-
]);
|
|
1695
|
-
const handleKeyDown = (0, react.useCallback)((event) => {
|
|
1696
|
-
if (!keyboard || !onChange || readonly) return;
|
|
1697
|
-
const key = event.key;
|
|
1698
|
-
if ([
|
|
1699
|
-
"ArrowRight",
|
|
1700
|
-
"ArrowLeft",
|
|
1701
|
-
"ArrowUp",
|
|
1702
|
-
"ArrowDown"
|
|
1703
|
-
].includes(key)) {
|
|
1704
|
-
event.preventDefault();
|
|
1705
|
-
let x = key == "ArrowRight" || key == "ArrowUp" ? keyboard[1] : -keyboard[1];
|
|
1706
|
-
if (reverse) x *= -1;
|
|
1707
|
-
onChange(updateValueByEvent(keyboard[0], x));
|
|
1708
|
-
}
|
|
1709
|
-
}, [
|
|
1710
|
-
keyboard,
|
|
1711
|
-
onChange,
|
|
1712
|
-
readonly,
|
|
1713
|
-
reverse,
|
|
1714
|
-
updateValueByEvent
|
|
1715
|
-
]);
|
|
1716
|
-
const [touchMoveRefCallback, pointerDownHandler] = useDragWithElement({
|
|
1717
|
-
baseElementRef: trackElementRef,
|
|
1718
|
-
onDrag,
|
|
1719
|
-
onDragStart: (nx, ny) => {
|
|
1720
|
-
if (readonly) return;
|
|
1721
|
-
if (bodyNoSelect) addNoSelect();
|
|
1722
|
-
thumbRef.current?.focus();
|
|
1723
|
-
onDragStart?.((0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)((0, __tremolo_ui_functions.rawValue)(vertical ? ny : nx, min, max, skew), step), min, max));
|
|
1724
|
-
},
|
|
1725
|
-
onDragEnd: (nx, ny) => {
|
|
1726
|
-
if (readonly) return;
|
|
1727
|
-
if (bodyNoSelect) removeNoSelect();
|
|
1728
|
-
onDragEnd?.((0, __tremolo_ui_functions.clamp)((0, __tremolo_ui_functions.stepValue)((0, __tremolo_ui_functions.rawValue)(vertical ? ny : nx, min, max, skew), step), min, max));
|
|
1729
|
-
}
|
|
1730
|
-
});
|
|
1731
|
-
const wheelRefCallback = useRefCallbackEvent("wheel", (event) => {
|
|
1732
|
-
if (!wheel || !onChange || readonly) return;
|
|
1733
|
-
event.preventDefault();
|
|
1734
|
-
let x;
|
|
1735
|
-
if (!vertical && event.deltaX != 0) x = event.deltaX > 0 ? wheel[1] : -wheel[1];
|
|
1736
|
-
else {
|
|
1737
|
-
if (event.deltaY == 0) return;
|
|
1738
|
-
x = event.deltaY > 0 ? -wheel[1] : wheel[1];
|
|
1739
|
-
}
|
|
1740
|
-
if (vertical && reverse) x *= -1;
|
|
1741
|
-
onChange(updateValueByEvent(wheel[0], x));
|
|
1742
|
-
}, { passive: false }, [
|
|
1743
|
-
wheel,
|
|
1744
|
-
vertical,
|
|
1745
|
-
readonly,
|
|
1746
|
-
onChange,
|
|
1747
|
-
updateValueByEvent
|
|
1748
|
-
]);
|
|
1749
|
-
(0, react.useImperativeHandle)(forwardedRef, () => {
|
|
1750
|
-
return {
|
|
1751
|
-
focus() {
|
|
1752
|
-
thumbRef.current?.focus();
|
|
1753
|
-
},
|
|
1754
|
-
blur() {
|
|
1755
|
-
thumbRef.current?.blur();
|
|
1756
|
-
}
|
|
1789
|
+
const Slider = {
|
|
1790
|
+
Root: (0, react.forwardRef)(({ value, min, max, step = 1, skew = 1, vertical = false, reverse = false, externalStyles: _externalStyles, wheel = ["raw", 1], keyboard = ["raw", 1], disabled = false, readonly = false, onChange, onDragStart, onDragEnd, className, style, children, onFocus, onBlur, onPointerDown, onKeyDown, ...props }, forwardedRef) => {
|
|
1791
|
+
const trackElementRef = (0, react.useRef)(null);
|
|
1792
|
+
const thumbRef = (0, react.useRef)(null);
|
|
1793
|
+
const externalStyles = {
|
|
1794
|
+
...defaultExternalStyles$1,
|
|
1795
|
+
..._externalStyles
|
|
1757
1796
|
};
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1797
|
+
const p = (0, _tremolo_ui_functions.toFixed)((0, _tremolo_ui_functions.normalizeValue)(value, min, max, skew) * 100);
|
|
1798
|
+
const rev = (0, _tremolo_ui_functions.toFixed)(100 - p);
|
|
1799
|
+
const displayReversed = (0, react.useMemo)(() => (0, _tremolo_ui_functions.xor)(vertical, reverse), [vertical, reverse]);
|
|
1800
|
+
const percent = displayReversed ? rev : p;
|
|
1801
|
+
let trackProps = {};
|
|
1802
|
+
let thumbProps = {};
|
|
1803
|
+
let scaleComponent = void 0;
|
|
1804
|
+
if (children != void 0) react.default.Children.forEach(children, (child) => {
|
|
1805
|
+
if (react.default.isValidElement(child)) if (child.type == Thumb$1) thumbProps = child.props;
|
|
1806
|
+
else if (child.type == Track) trackProps = child.props;
|
|
1807
|
+
else if (child.type == Scale) scaleComponent = child;
|
|
1808
|
+
else throw new Error("only <SliderThumb> or <SliderTrack>");
|
|
1809
|
+
else throw new Error("children is an invalid element.");
|
|
1810
|
+
});
|
|
1811
|
+
const onDrag = (0, react.useCallback)((nx, ny) => {
|
|
1812
|
+
if (!onChange || readonly) return;
|
|
1813
|
+
const n = vertical ? ny : nx;
|
|
1814
|
+
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));
|
|
1815
|
+
}, [
|
|
1816
|
+
displayReversed,
|
|
1817
|
+
max,
|
|
1818
|
+
min,
|
|
1819
|
+
onChange,
|
|
1820
|
+
readonly,
|
|
1821
|
+
skew,
|
|
1822
|
+
step,
|
|
1823
|
+
vertical
|
|
1824
|
+
]);
|
|
1825
|
+
const updateValueByEvent = (0, react.useCallback)((eventType, x) => {
|
|
1826
|
+
let newValue;
|
|
1827
|
+
if (eventType == "normalized") newValue = (0, _tremolo_ui_functions.rawValue)((0, _tremolo_ui_functions.normalizeValue)(value, min, max, skew) + x, min, max, skew);
|
|
1828
|
+
else newValue = value + x;
|
|
1829
|
+
return (0, _tremolo_ui_functions.clamp)((0, _tremolo_ui_functions.stepValue)(newValue, step), min, max);
|
|
1830
|
+
}, [
|
|
1831
|
+
max,
|
|
1832
|
+
min,
|
|
1833
|
+
skew,
|
|
1834
|
+
step,
|
|
1835
|
+
value
|
|
1836
|
+
]);
|
|
1837
|
+
const handleKeyDown = (0, react.useCallback)((event) => {
|
|
1838
|
+
if (!keyboard || !onChange || readonly) return;
|
|
1839
|
+
const key = event.key;
|
|
1840
|
+
if ([
|
|
1841
|
+
"ArrowRight",
|
|
1842
|
+
"ArrowLeft",
|
|
1843
|
+
"ArrowUp",
|
|
1844
|
+
"ArrowDown"
|
|
1845
|
+
].includes(key)) {
|
|
1846
|
+
event.preventDefault();
|
|
1847
|
+
let x = key == "ArrowRight" || key == "ArrowUp" ? keyboard[1] : -keyboard[1];
|
|
1848
|
+
if (reverse) x *= -1;
|
|
1849
|
+
onChange(updateValueByEvent(keyboard[0], x));
|
|
1850
|
+
}
|
|
1851
|
+
}, [
|
|
1852
|
+
keyboard,
|
|
1853
|
+
onChange,
|
|
1854
|
+
readonly,
|
|
1855
|
+
reverse,
|
|
1856
|
+
updateValueByEvent
|
|
1857
|
+
]);
|
|
1858
|
+
const { refHandler: touchMoveRefCallback, pointerDownHandler } = useDragWithElement({
|
|
1859
|
+
baseElementRef: trackElementRef,
|
|
1860
|
+
onDrag,
|
|
1861
|
+
onDragStart: (nx, ny) => {
|
|
1862
|
+
if (readonly) return;
|
|
1863
|
+
if (externalStyles.userSelectNone) addUserSelectNone();
|
|
1864
|
+
if (externalStyles.cursor) setCursorStyle(externalStyles.cursor);
|
|
1795
1865
|
thumbRef.current?.focus();
|
|
1796
|
-
|
|
1797
|
-
},
|
|
1798
|
-
onBlur: (event) => {
|
|
1799
|
-
thumbRef.current?.blur();
|
|
1800
|
-
onBlur?.(event);
|
|
1866
|
+
onDragStart?.((0, _tremolo_ui_functions.clamp)((0, _tremolo_ui_functions.stepValue)((0, _tremolo_ui_functions.rawValue)(vertical ? ny : nx, min, max, skew), step), min, max));
|
|
1801
1867
|
},
|
|
1802
|
-
|
|
1803
|
-
|
|
1868
|
+
onDragEnd: (nx, ny) => {
|
|
1869
|
+
if (readonly) return;
|
|
1870
|
+
if (externalStyles.userSelectNone) removeUserSelectNone();
|
|
1871
|
+
if (externalStyles.cursor) resetCursorStyle();
|
|
1872
|
+
onDragEnd?.((0, _tremolo_ui_functions.clamp)((0, _tremolo_ui_functions.stepValue)((0, _tremolo_ui_functions.rawValue)(vertical ? ny : nx, min, max, skew), step), min, max));
|
|
1873
|
+
}
|
|
1874
|
+
});
|
|
1875
|
+
const wheelRefCallback = useRefCallbackEvent("wheel", (event) => {
|
|
1876
|
+
if (!wheel || !onChange || readonly) return;
|
|
1877
|
+
event.preventDefault();
|
|
1878
|
+
let x;
|
|
1879
|
+
if (!vertical && event.deltaX != 0) x = event.deltaX > 0 ? wheel[1] : -wheel[1];
|
|
1880
|
+
else {
|
|
1881
|
+
if (event.deltaY == 0) return;
|
|
1882
|
+
x = event.deltaY > 0 ? -wheel[1] : wheel[1];
|
|
1883
|
+
}
|
|
1884
|
+
if (vertical && reverse) x *= -1;
|
|
1885
|
+
onChange(updateValueByEvent(wheel[0], x));
|
|
1886
|
+
}, { passive: false }, [
|
|
1887
|
+
wheel,
|
|
1888
|
+
vertical,
|
|
1889
|
+
readonly,
|
|
1890
|
+
onChange,
|
|
1891
|
+
updateValueByEvent
|
|
1892
|
+
]);
|
|
1893
|
+
(0, react.useImperativeHandle)(forwardedRef, () => {
|
|
1894
|
+
return {
|
|
1895
|
+
focus() {
|
|
1896
|
+
thumbRef.current?.focus();
|
|
1897
|
+
},
|
|
1898
|
+
blur() {
|
|
1899
|
+
thumbRef.current?.blur();
|
|
1900
|
+
}
|
|
1901
|
+
};
|
|
1902
|
+
}, []);
|
|
1903
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SliderProvider, {
|
|
1904
|
+
min,
|
|
1905
|
+
max,
|
|
1906
|
+
step,
|
|
1907
|
+
skew,
|
|
1908
|
+
vertical,
|
|
1909
|
+
reverse,
|
|
1910
|
+
disabled,
|
|
1911
|
+
readonly,
|
|
1912
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1913
|
+
className: (0, clsx.default)("tremolo-slider", className),
|
|
1914
|
+
ref: (div) => {
|
|
1915
|
+
wheelRefCallback(div);
|
|
1916
|
+
touchMoveRefCallback(div);
|
|
1917
|
+
},
|
|
1918
|
+
tabIndex: -1,
|
|
1919
|
+
role: "slider",
|
|
1920
|
+
"aria-valuenow": value,
|
|
1921
|
+
"aria-valuemin": min,
|
|
1922
|
+
"aria-valuemax": max,
|
|
1923
|
+
"aria-orientation": vertical ? "vertical" : "horizontal",
|
|
1924
|
+
"aria-disabled": disabled,
|
|
1925
|
+
"aria-readonly": readonly,
|
|
1804
1926
|
style: {
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1927
|
+
margin: `calc(${(0, _tremolo_ui_functions.styleHelper)(thumbProps?.size ?? 22)} / 2)`,
|
|
1928
|
+
...style
|
|
1929
|
+
},
|
|
1930
|
+
onPointerDown: (event) => {
|
|
1931
|
+
pointerDownHandler(event);
|
|
1932
|
+
onPointerDown?.(event);
|
|
1809
1933
|
},
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1934
|
+
onKeyDown: (event) => {
|
|
1935
|
+
handleKeyDown(event);
|
|
1936
|
+
onKeyDown?.(event);
|
|
1937
|
+
},
|
|
1938
|
+
onFocus: (event) => {
|
|
1939
|
+
thumbRef.current?.focus();
|
|
1940
|
+
onFocus?.(event);
|
|
1941
|
+
},
|
|
1942
|
+
onBlur: (event) => {
|
|
1943
|
+
thumbRef.current?.blur();
|
|
1944
|
+
onBlur?.(event);
|
|
1945
|
+
},
|
|
1946
|
+
...props,
|
|
1947
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1948
|
+
style: {
|
|
1949
|
+
display: "flex",
|
|
1950
|
+
flexDirection: vertical ? "row" : "column",
|
|
1951
|
+
width: !vertical ? "100%" : void 0,
|
|
1952
|
+
height: vertical ? "100%" : void 0
|
|
1953
|
+
},
|
|
1954
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1955
|
+
className: "tremolo-slider-track-wrapper",
|
|
1956
|
+
ref: trackElementRef,
|
|
1957
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Track, {
|
|
1817
1958
|
__percent: percent,
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1959
|
+
__thumb: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Thumb$1, {
|
|
1960
|
+
ref: thumbRef,
|
|
1961
|
+
__percent: percent,
|
|
1962
|
+
...thumbProps
|
|
1963
|
+
}),
|
|
1964
|
+
...trackProps
|
|
1965
|
+
})
|
|
1966
|
+
}), scaleComponent]
|
|
1967
|
+
})
|
|
1823
1968
|
})
|
|
1824
|
-
})
|
|
1825
|
-
})
|
|
1826
|
-
|
|
1827
|
-
|
|
1969
|
+
});
|
|
1970
|
+
}),
|
|
1971
|
+
Thumb: Thumb$1,
|
|
1972
|
+
Track,
|
|
1973
|
+
Scale,
|
|
1974
|
+
ScaleOption
|
|
1975
|
+
};
|
|
1976
|
+
//#endregion
|
|
1977
|
+
//#region src/components/_util/composeRefs.tsx
|
|
1978
|
+
/**
|
|
1979
|
+
* Set a given ref to a given value
|
|
1980
|
+
* This utility takes care of different types of refs: callback refs and RefObject(s)
|
|
1981
|
+
*/
|
|
1982
|
+
function setRef(ref, value) {
|
|
1983
|
+
if (typeof ref === "function") return ref(value);
|
|
1984
|
+
else if (ref !== null && ref !== void 0) ref.current = value;
|
|
1985
|
+
}
|
|
1986
|
+
/**
|
|
1987
|
+
* A utility to compose multiple refs together
|
|
1988
|
+
* Accepts callback refs and RefObject(s)
|
|
1989
|
+
*/
|
|
1990
|
+
function composeRefs(...refs) {
|
|
1991
|
+
return (node) => {
|
|
1992
|
+
let hasCleanup = false;
|
|
1993
|
+
const cleanups = refs.map((ref) => {
|
|
1994
|
+
const cleanup = setRef(ref, node);
|
|
1995
|
+
if (!hasCleanup && typeof cleanup == "function") hasCleanup = true;
|
|
1996
|
+
return cleanup;
|
|
1997
|
+
});
|
|
1998
|
+
if (hasCleanup) return () => {
|
|
1999
|
+
for (let i = 0; i < cleanups.length; i++) {
|
|
2000
|
+
const cleanup = cleanups[i];
|
|
2001
|
+
if (typeof cleanup == "function") cleanup();
|
|
2002
|
+
else setRef(refs[i], null);
|
|
2003
|
+
}
|
|
2004
|
+
};
|
|
2005
|
+
};
|
|
2006
|
+
}
|
|
1828
2007
|
//#endregion
|
|
1829
2008
|
//#region src/components/WheelObserver/index.tsx
|
|
1830
|
-
/**
|
|
2009
|
+
/**
|
|
2010
|
+
* @example
|
|
2011
|
+
* <WheelObserver
|
|
2012
|
+
* onWheel={(event) => {
|
|
2013
|
+
* event.preventDefault()
|
|
2014
|
+
* console.log(event.deltaY)
|
|
2015
|
+
* }
|
|
2016
|
+
* >
|
|
2017
|
+
* <div>Wheel here</div>
|
|
2018
|
+
* </WheelObserver>
|
|
2019
|
+
*/
|
|
1831
2020
|
function WheelObserver(props) {
|
|
1832
|
-
const {
|
|
2021
|
+
const { as: Component = "div", children, onWheel, ref, ...attributes } = props;
|
|
1833
2022
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, {
|
|
1834
|
-
ref: useRefCallbackEvent("wheel", (event) => {
|
|
2023
|
+
ref: composeRefs(ref, useRefCallbackEvent("wheel", (event) => {
|
|
1835
2024
|
if (onWheel) onWheel(event);
|
|
1836
|
-
}, { passive: false }, [onWheel]),
|
|
2025
|
+
}, { passive: false }, [onWheel])),
|
|
1837
2026
|
...attributes,
|
|
1838
2027
|
children
|
|
1839
2028
|
});
|
|
1840
2029
|
}
|
|
1841
|
-
|
|
1842
2030
|
//#endregion
|
|
1843
2031
|
//#region src/components/XYPad/Area.tsx
|
|
1844
|
-
|
|
1845
|
-
function XYPadArea({ width = 120, height = 120, color, children, className, style, __thumb,...props }) {
|
|
2032
|
+
function Area({ width = 120, height = 120, color, children, className, style, __thumb, ...props }) {
|
|
1846
2033
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1847
2034
|
className: (0, clsx.default)("tremolo-xy-pad-area", className),
|
|
1848
2035
|
style: {
|
|
@@ -1855,11 +2042,9 @@ function XYPadArea({ width = 120, height = 120, color, children, className, styl
|
|
|
1855
2042
|
children: [children, __thumb]
|
|
1856
2043
|
});
|
|
1857
2044
|
}
|
|
1858
|
-
|
|
1859
2045
|
//#endregion
|
|
1860
2046
|
//#region src/components/XYPad/Thumb.tsx
|
|
1861
|
-
|
|
1862
|
-
const XYPadThumb = (0, react.forwardRef)(({ size, width = 22, height = 22, color, children, wrapperClassName, wrapperStyle, className, style, __disabled, __css,...props }, ref) => {
|
|
2047
|
+
const Thumb = (0, react.forwardRef)(({ size, width = 22, height = 22, color, children, wrapperClassName, wrapperStyle, className, style, __disabled, __readonly, __css, ...props }, ref) => {
|
|
1863
2048
|
const wrapperRef = (0, react.useRef)(null);
|
|
1864
2049
|
(0, react.useImperativeHandle)(ref, () => {
|
|
1865
2050
|
return {
|
|
@@ -1883,6 +2068,7 @@ const XYPadThumb = (0, react.forwardRef)(({ size, width = 22, height = 22, color
|
|
|
1883
2068
|
className: (0, clsx.default)("tremolo-xy-pad-thumb", className),
|
|
1884
2069
|
tabIndex: 0,
|
|
1885
2070
|
"aria-disabled": __disabled,
|
|
2071
|
+
"aria-readonly": __readonly,
|
|
1886
2072
|
style: {
|
|
1887
2073
|
"--color": color,
|
|
1888
2074
|
width: size ?? width,
|
|
@@ -1892,7 +2078,6 @@ const XYPadThumb = (0, react.forwardRef)(({ size, width = 22, height = 22, color
|
|
|
1892
2078
|
})
|
|
1893
2079
|
});
|
|
1894
2080
|
});
|
|
1895
|
-
|
|
1896
2081
|
//#endregion
|
|
1897
2082
|
//#region src/components/XYPad/index.tsx
|
|
1898
2083
|
const defaultValueOptions = {
|
|
@@ -1902,192 +2087,201 @@ const defaultValueOptions = {
|
|
|
1902
2087
|
wheel: ["raw", 1],
|
|
1903
2088
|
keyboard: ["raw", 1]
|
|
1904
2089
|
};
|
|
2090
|
+
const defaultExternalStyles = {
|
|
2091
|
+
userSelectNone: true,
|
|
2092
|
+
cursor: "pointer"
|
|
2093
|
+
};
|
|
1905
2094
|
/**
|
|
1906
2095
|
* Simple XYPad
|
|
1907
|
-
* @category XYPad
|
|
1908
2096
|
*/
|
|
1909
|
-
const XYPad =
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
2097
|
+
const XYPad = {
|
|
2098
|
+
Root: (0, react.forwardRef)(({ x: _x, y: _y, className, style, externalStyles: _externalStyles, disabled = false, readonly = false, onChange, onDragStart, onDragEnd, onPointerDown, onKeyDown, onFocus, onBlur, children, ...props }, forwardedRef) => {
|
|
2099
|
+
const x = (0, react.useMemo)(() => {
|
|
2100
|
+
return {
|
|
2101
|
+
...defaultValueOptions,
|
|
2102
|
+
..._x
|
|
2103
|
+
};
|
|
2104
|
+
}, [_x]);
|
|
2105
|
+
const y = (0, react.useMemo)(() => {
|
|
2106
|
+
return {
|
|
2107
|
+
...defaultValueOptions,
|
|
2108
|
+
..._y
|
|
2109
|
+
};
|
|
2110
|
+
}, [_y]);
|
|
2111
|
+
const rootRef = (0, react.useRef)(null);
|
|
2112
|
+
const areaElementRef = (0, react.useRef)(null);
|
|
2113
|
+
const thumbRef = (0, react.useRef)(null);
|
|
2114
|
+
const externalStyles = {
|
|
2115
|
+
...defaultExternalStyles,
|
|
2116
|
+
..._externalStyles
|
|
1920
2117
|
};
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
2118
|
+
const nx = (0, _tremolo_ui_functions.normalizeValue)(x.value, x.min, x.max, x.skew);
|
|
2119
|
+
const ny = (0, _tremolo_ui_functions.normalizeValue)(y.value, y.min, y.max, y.skew);
|
|
2120
|
+
const percentX = (0, _tremolo_ui_functions.toFixed)((x.reverse ? 1 - nx : nx) * 100);
|
|
2121
|
+
const percentY = (0, _tremolo_ui_functions.toFixed)((y.reverse ? 1 - ny : ny) * 100);
|
|
2122
|
+
let areaProps = {};
|
|
2123
|
+
let thumbProps = {};
|
|
2124
|
+
if (children != void 0) react.default.Children.forEach(children, (child) => {
|
|
2125
|
+
if (react.default.isValidElement(child)) if (child.type == Thumb) thumbProps = child.props;
|
|
2126
|
+
else if (child.type == Area) areaProps = child.props;
|
|
2127
|
+
else throw new Error("only <XYPadThumb> or <XYPadArea>");
|
|
2128
|
+
else throw new Error("children is an invalid element.");
|
|
2129
|
+
});
|
|
2130
|
+
const onDrag = (0, react.useCallback)((nx, ny) => {
|
|
2131
|
+
if (!onChange || readonly) return;
|
|
2132
|
+
const vx = (0, _tremolo_ui_functions.rawValue)(x.reverse ? 1 - nx : nx, x.min, x.max, x.skew);
|
|
2133
|
+
const vy = (0, _tremolo_ui_functions.rawValue)(y.reverse ? 1 - ny : ny, y.min, y.max, y.skew);
|
|
2134
|
+
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));
|
|
2135
|
+
}, [
|
|
2136
|
+
onChange,
|
|
2137
|
+
readonly,
|
|
2138
|
+
x.max,
|
|
2139
|
+
x.min,
|
|
2140
|
+
x.reverse,
|
|
2141
|
+
x.skew,
|
|
2142
|
+
x.step,
|
|
2143
|
+
y.max,
|
|
2144
|
+
y.min,
|
|
2145
|
+
y.reverse,
|
|
2146
|
+
y.skew,
|
|
2147
|
+
y.step
|
|
2148
|
+
]);
|
|
2149
|
+
const updateValueByEvent = (0, react.useCallback)((eventType, target, x) => {
|
|
2150
|
+
let v;
|
|
2151
|
+
if (eventType == "normalized") v = (0, _tremolo_ui_functions.rawValue)((0, _tremolo_ui_functions.normalizeValue)(target.value, target.min, target.max, target.skew) + x, target.min, target.max, target.skew);
|
|
2152
|
+
else v = target.value + x;
|
|
2153
|
+
return (0, _tremolo_ui_functions.clamp)((0, _tremolo_ui_functions.stepValue)(v, target.step), target.min, target.max);
|
|
2154
|
+
}, []);
|
|
2155
|
+
const handleKeyDown = (0, react.useCallback)((event) => {
|
|
2156
|
+
if (!onChange || readonly) return;
|
|
2157
|
+
const key = event.key;
|
|
2158
|
+
if ([
|
|
2159
|
+
"ArrowRight",
|
|
2160
|
+
"ArrowLeft",
|
|
2161
|
+
"ArrowUp",
|
|
2162
|
+
"ArrowDown"
|
|
2163
|
+
].includes(key)) {
|
|
2164
|
+
const isHorizontal = key == "ArrowRight" || key == "ArrowLeft";
|
|
2165
|
+
const target = isHorizontal ? x : y;
|
|
2166
|
+
if (!target.keyboard) return;
|
|
2167
|
+
event.preventDefault();
|
|
2168
|
+
let delta = target.keyboard[1];
|
|
2169
|
+
if (key == "ArrowLeft" || key == "ArrowUp") delta *= -1;
|
|
2170
|
+
if (target.reverse) delta *= -1;
|
|
2171
|
+
const newValue = updateValueByEvent(target.keyboard[0], target, delta);
|
|
2172
|
+
if (isHorizontal) onChange(newValue, y.value);
|
|
2173
|
+
else onChange(x.value, newValue);
|
|
2174
|
+
}
|
|
2175
|
+
}, [
|
|
2176
|
+
onChange,
|
|
2177
|
+
readonly,
|
|
2178
|
+
x,
|
|
2179
|
+
y,
|
|
2180
|
+
updateValueByEvent
|
|
2181
|
+
]);
|
|
2182
|
+
const { refHandler: touchMoveRefCallback, pointerDownHandler } = useDragWithElement({
|
|
2183
|
+
baseElementRef: areaElementRef,
|
|
2184
|
+
onDrag,
|
|
2185
|
+
onDragStart: (nx, ny) => {
|
|
2186
|
+
if (readonly) return;
|
|
2187
|
+
if (externalStyles.userSelectNone) addUserSelectNone();
|
|
2188
|
+
if (externalStyles.cursor) setCursorStyle(externalStyles.cursor);
|
|
2189
|
+
thumbRef.current?.focus();
|
|
2190
|
+
const valueX = (0, _tremolo_ui_functions.clamp)((0, _tremolo_ui_functions.stepValue)((0, _tremolo_ui_functions.rawValue)(nx, x.min, x.max, x.skew), x.step), x.min, x.max);
|
|
2191
|
+
const valueY = (0, _tremolo_ui_functions.clamp)((0, _tremolo_ui_functions.stepValue)((0, _tremolo_ui_functions.rawValue)(ny, y.min, y.max, y.skew), y.step), y.min, y.max);
|
|
2192
|
+
onDragStart?.(valueX, valueY);
|
|
2193
|
+
},
|
|
2194
|
+
onDragEnd: (nx, ny) => {
|
|
2195
|
+
if (readonly) return;
|
|
2196
|
+
if (externalStyles.userSelectNone) removeUserSelectNone();
|
|
2197
|
+
if (externalStyles.cursor) resetCursorStyle();
|
|
2198
|
+
const valueX = (0, _tremolo_ui_functions.clamp)((0, _tremolo_ui_functions.stepValue)((0, _tremolo_ui_functions.rawValue)(nx, x.min, x.max, x.skew), x.step), x.min, x.max);
|
|
2199
|
+
const valueY = (0, _tremolo_ui_functions.clamp)((0, _tremolo_ui_functions.stepValue)((0, _tremolo_ui_functions.rawValue)(ny, y.min, y.max, y.skew), y.step), y.min, y.max);
|
|
2200
|
+
onDragEnd?.(valueX, valueY);
|
|
2201
|
+
}
|
|
2202
|
+
});
|
|
2203
|
+
const wheelRefCallback = useRefCallbackEvent("wheel", (event) => {
|
|
2204
|
+
if (!onChange || readonly) return;
|
|
2205
|
+
const target = event.shiftKey ? x : y;
|
|
2206
|
+
if (!target.wheel) return;
|
|
1973
2207
|
event.preventDefault();
|
|
1974
|
-
let delta = target.
|
|
1975
|
-
if (
|
|
2208
|
+
let delta = target.wheel[1];
|
|
2209
|
+
if (event.deltaY < 0) delta *= -1;
|
|
1976
2210
|
if (target.reverse) delta *= -1;
|
|
1977
|
-
const newValue = updateValueByEvent(target.
|
|
1978
|
-
if (
|
|
2211
|
+
const newValue = updateValueByEvent(target.wheel[0], target, delta);
|
|
2212
|
+
if (event.shiftKey) onChange(newValue, y.value);
|
|
1979
2213
|
else onChange(x.value, newValue);
|
|
1980
|
-
}
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
x,
|
|
2020
|
-
y,
|
|
2021
|
-
onChange,
|
|
2022
|
-
readonly,
|
|
2023
|
-
updateValueByEvent
|
|
2024
|
-
]);
|
|
2025
|
-
(0, react.useImperativeHandle)(forwardedRef, () => {
|
|
2026
|
-
return {
|
|
2027
|
-
focus() {
|
|
2214
|
+
}, { passive: false }, [
|
|
2215
|
+
x,
|
|
2216
|
+
y,
|
|
2217
|
+
onChange,
|
|
2218
|
+
readonly,
|
|
2219
|
+
updateValueByEvent
|
|
2220
|
+
]);
|
|
2221
|
+
(0, react.useImperativeHandle)(forwardedRef, () => {
|
|
2222
|
+
return {
|
|
2223
|
+
focus() {
|
|
2224
|
+
thumbRef.current?.focus();
|
|
2225
|
+
},
|
|
2226
|
+
blur() {
|
|
2227
|
+
thumbRef.current?.blur();
|
|
2228
|
+
},
|
|
2229
|
+
original: rootRef
|
|
2230
|
+
};
|
|
2231
|
+
}, []);
|
|
2232
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2233
|
+
className: (0, clsx.default)("tremolo-xy-pad", className),
|
|
2234
|
+
ref: composeRefs(rootRef, wheelRefCallback, touchMoveRefCallback),
|
|
2235
|
+
tabIndex: -1,
|
|
2236
|
+
"aria-disabled": disabled,
|
|
2237
|
+
"aria-readonly": readonly,
|
|
2238
|
+
style: {
|
|
2239
|
+
margin: `calc(${(0, _tremolo_ui_functions.styleHelper)(thumbProps?.size ?? 22)} / 2)`,
|
|
2240
|
+
cursor: readonly ? "not-allowed" : "pointer",
|
|
2241
|
+
WebkitTapHighlightColor: "transparent",
|
|
2242
|
+
...style
|
|
2243
|
+
},
|
|
2244
|
+
onPointerDown: (event) => {
|
|
2245
|
+
pointerDownHandler(event);
|
|
2246
|
+
onPointerDown?.(event);
|
|
2247
|
+
},
|
|
2248
|
+
onKeyDown: (event) => {
|
|
2249
|
+
handleKeyDown(event);
|
|
2250
|
+
onKeyDown?.(event);
|
|
2251
|
+
},
|
|
2252
|
+
onFocus: (event) => {
|
|
2028
2253
|
thumbRef.current?.focus();
|
|
2254
|
+
onFocus?.(event);
|
|
2029
2255
|
},
|
|
2030
|
-
|
|
2256
|
+
onBlur: (event) => {
|
|
2031
2257
|
thumbRef.current?.blur();
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
pointerDownHandler(event);
|
|
2052
|
-
onPointerDown?.(event);
|
|
2053
|
-
},
|
|
2054
|
-
onKeyDown: (event) => {
|
|
2055
|
-
handleKeyDown(event);
|
|
2056
|
-
onKeyDown?.(event);
|
|
2057
|
-
},
|
|
2058
|
-
onFocus: (event) => {
|
|
2059
|
-
thumbRef.current?.focus();
|
|
2060
|
-
onFocus?.(event);
|
|
2061
|
-
},
|
|
2062
|
-
onBlur: (event) => {
|
|
2063
|
-
thumbRef.current?.blur();
|
|
2064
|
-
onBlur?.(event);
|
|
2065
|
-
},
|
|
2066
|
-
...props,
|
|
2067
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2068
|
-
className: "tremolo-xy-pad-area-wrapper",
|
|
2069
|
-
ref: areaElementRef,
|
|
2070
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(XYPadArea, {
|
|
2071
|
-
__thumb: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(XYPadThumb, {
|
|
2072
|
-
ref: thumbRef,
|
|
2073
|
-
__disabled: disabled,
|
|
2074
|
-
__css: {
|
|
2075
|
-
top: `${percentY}%`,
|
|
2076
|
-
left: `${percentX}%`
|
|
2077
|
-
},
|
|
2078
|
-
...thumbProps
|
|
2079
|
-
}),
|
|
2080
|
-
...areaProps
|
|
2258
|
+
onBlur?.(event);
|
|
2259
|
+
},
|
|
2260
|
+
...props,
|
|
2261
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2262
|
+
className: "tremolo-xy-pad-area-wrapper",
|
|
2263
|
+
ref: areaElementRef,
|
|
2264
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Area, {
|
|
2265
|
+
__thumb: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Thumb, {
|
|
2266
|
+
ref: thumbRef,
|
|
2267
|
+
__disabled: disabled,
|
|
2268
|
+
__readonly: readonly,
|
|
2269
|
+
__css: {
|
|
2270
|
+
top: `${percentY}%`,
|
|
2271
|
+
left: `${percentX}%`
|
|
2272
|
+
},
|
|
2273
|
+
...thumbProps
|
|
2274
|
+
}),
|
|
2275
|
+
...areaProps
|
|
2276
|
+
})
|
|
2081
2277
|
})
|
|
2082
|
-
})
|
|
2083
|
-
})
|
|
2084
|
-
|
|
2085
|
-
|
|
2278
|
+
});
|
|
2279
|
+
}),
|
|
2280
|
+
Thumb,
|
|
2281
|
+
Area
|
|
2282
|
+
};
|
|
2086
2283
|
//#endregion
|
|
2087
2284
|
//#region src/hooks/useAnimationFrame.ts
|
|
2088
|
-
/**
|
|
2089
|
-
* @category hooks
|
|
2090
|
-
*/
|
|
2091
2285
|
function useAnimationFrame(callback = () => {}, deps = []) {
|
|
2092
2286
|
const reqIdRef = (0, react.useRef)(-1);
|
|
2093
2287
|
const loop = (0, react.useCallback)(() => {
|
|
@@ -2099,7 +2293,6 @@ function useAnimationFrame(callback = () => {}, deps = []) {
|
|
|
2099
2293
|
return () => cancelAnimationFrame(reqIdRef.current);
|
|
2100
2294
|
}, [loop]);
|
|
2101
2295
|
}
|
|
2102
|
-
|
|
2103
2296
|
//#endregion
|
|
2104
2297
|
//#region src/hooks/useMIDIAccess.ts
|
|
2105
2298
|
/** @private */
|
|
@@ -2108,7 +2301,6 @@ const PERMISSION_DENIED = "PERMISSION_DENIED";
|
|
|
2108
2301
|
const NOT_SUPPORTED = "NOT_SUPPORTED";
|
|
2109
2302
|
/**
|
|
2110
2303
|
* 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
2304
|
*/
|
|
2113
2305
|
function useMIDIAccess(requestOnMount = true) {
|
|
2114
2306
|
const [midiAccess, setMidiAccess] = (0, react.useState)(null);
|
|
@@ -2131,12 +2323,10 @@ function useMIDIAccess(requestOnMount = true) {
|
|
|
2131
2323
|
error
|
|
2132
2324
|
};
|
|
2133
2325
|
}
|
|
2134
|
-
|
|
2135
2326
|
//#endregion
|
|
2136
2327
|
//#region src/hooks/useMIDIMessage.ts
|
|
2137
2328
|
/**
|
|
2138
2329
|
* Hooks for when you want to process MIDI events in more detail than useMIDIInput.
|
|
2139
|
-
* @category hooks
|
|
2140
2330
|
*/
|
|
2141
2331
|
function useMIDIMessage(midiAccess, onMIDIMessage) {
|
|
2142
2332
|
(0, react.useEffect)(() => {
|
|
@@ -2147,7 +2337,6 @@ function useMIDIMessage(midiAccess, onMIDIMessage) {
|
|
|
2147
2337
|
};
|
|
2148
2338
|
}, [midiAccess, onMIDIMessage]);
|
|
2149
2339
|
}
|
|
2150
|
-
|
|
2151
2340
|
//#endregion
|
|
2152
2341
|
//#region src/hooks/useMIDIInput.ts
|
|
2153
2342
|
const MIDI_EVENT_TO_NUMBER = {
|
|
@@ -2157,7 +2346,6 @@ const MIDI_EVENT_TO_NUMBER = {
|
|
|
2157
2346
|
};
|
|
2158
2347
|
/**
|
|
2159
2348
|
* Hooks for handling note on/off events. To be used with useMIDIAccess. Internally uses useMIDIMessage.
|
|
2160
|
-
* @category hooks
|
|
2161
2349
|
*/
|
|
2162
2350
|
function useMIDIInput(midiAccess, onNoteOnEvent, onNoteOffEvent, onPitchBendEvent) {
|
|
2163
2351
|
useMIDIMessage(midiAccess, (0, react.useCallback)((event) => {
|
|
@@ -2172,32 +2360,20 @@ function useMIDIInput(midiAccess, onNoteOnEvent, onNoteOffEvent, onPitchBendEven
|
|
|
2172
2360
|
onPitchBendEvent
|
|
2173
2361
|
]));
|
|
2174
2362
|
}
|
|
2175
|
-
|
|
2176
2363
|
//#endregion
|
|
2177
2364
|
exports.AnimationCanvas = AnimationCanvas;
|
|
2178
|
-
exports.AnimationKnob = AnimationKnob;
|
|
2179
|
-
exports.BlackKey = BlackKey;
|
|
2180
|
-
exports.DecrementStepper = DecrementStepper;
|
|
2181
2365
|
exports.DragObserver = DragObserver;
|
|
2182
|
-
exports.IncrementStepper = IncrementStepper;
|
|
2183
|
-
exports.KeyLabel = KeyLabel;
|
|
2184
2366
|
exports.Knob = Knob;
|
|
2185
2367
|
exports.NOT_SUPPORTED = NOT_SUPPORTED;
|
|
2186
2368
|
exports.NumberInput = NumberInput;
|
|
2187
2369
|
exports.PERMISSION_DENIED = PERMISSION_DENIED;
|
|
2188
2370
|
exports.Piano = Piano;
|
|
2371
|
+
exports.PointsEditor = PointsEditor;
|
|
2189
2372
|
exports.SHORTCUTS = SHORTCUTS;
|
|
2190
|
-
exports.Scale = Scale;
|
|
2191
|
-
exports.ScaleOption = ScaleOption;
|
|
2192
2373
|
exports.Slider = Slider;
|
|
2193
|
-
exports.SliderThumb = SliderThumb;
|
|
2194
|
-
exports.SliderTrack = SliderTrack;
|
|
2195
|
-
exports.Stepper = Stepper;
|
|
2196
2374
|
exports.WheelObserver = WheelObserver;
|
|
2197
|
-
exports.WhiteKey = WhiteKey;
|
|
2198
2375
|
exports.XYPad = XYPad;
|
|
2199
|
-
exports.
|
|
2200
|
-
exports.XYPadThumb = XYPadThumb;
|
|
2376
|
+
exports.clampPoint = clampPoint;
|
|
2201
2377
|
exports.getNoteRangeArray = getNoteRangeArray;
|
|
2202
2378
|
exports.useAnimationFrame = useAnimationFrame;
|
|
2203
2379
|
exports.useDrag = useDrag;
|
|
@@ -2209,4 +2385,5 @@ exports.useMIDIAccess = useMIDIAccess;
|
|
|
2209
2385
|
exports.useMIDIInput = useMIDIInput;
|
|
2210
2386
|
exports.useMIDIMessage = useMIDIMessage;
|
|
2211
2387
|
exports.useSliderContext = useSliderContext;
|
|
2388
|
+
|
|
2212
2389
|
//# sourceMappingURL=index.cjs.map
|