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