@tremolo-ui/react 0.1.6 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1329 -1152
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +238 -183
- package/dist/index.d.cts +367 -366
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +367 -366
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1313 -1125
- package/dist/index.js.map +1 -1
- package/package.json +17 -19
- package/src/components/AnimationCanvas/index.tsx +0 -7
- package/src/components/DragObserver/index.tsx +9 -7
- package/src/components/Knob/ActiveLine.tsx +29 -0
- package/src/components/Knob/InactiveLine.tsx +53 -0
- package/src/components/Knob/SVGRoot.tsx +39 -0
- package/src/components/Knob/Thumb.tsx +64 -0
- package/src/components/Knob/context.tsx +120 -0
- package/src/components/Knob/index.css +25 -27
- package/src/components/Knob/index.tsx +110 -164
- package/src/components/NumberInput/DecrementStepper.tsx +0 -2
- package/src/components/NumberInput/IncrementStepper.tsx +0 -2
- package/src/components/NumberInput/InternalInput.tsx +3 -1
- package/src/components/NumberInput/Stepper.tsx +0 -2
- package/src/components/NumberInput/context.tsx +0 -1
- package/src/components/NumberInput/index.css +31 -15
- package/src/components/NumberInput/index.tsx +21 -14
- package/src/components/Piano/KeyLabel.tsx +0 -2
- package/src/components/Piano/context.tsx +0 -2
- package/src/components/Piano/index.css +7 -14
- package/src/components/Piano/index.tsx +14 -15
- package/src/components/Piano/key.tsx +0 -10
- package/src/components/Piano/keyboardShortcuts.ts +0 -2
- package/src/components/PointsEditor/Background.tsx +17 -0
- package/src/components/PointsEditor/Container.tsx +26 -0
- package/src/components/PointsEditor/Point.tsx +137 -0
- package/src/components/PointsEditor/context.tsx +91 -0
- package/src/components/PointsEditor/index.css +30 -0
- package/src/components/PointsEditor/index.tsx +129 -0
- package/src/components/Slider/Scale.tsx +0 -2
- package/src/components/Slider/ScaleOption.tsx +0 -2
- package/src/components/Slider/Thumb.tsx +1 -5
- package/src/components/Slider/Track.tsx +1 -3
- package/src/components/Slider/context.tsx +0 -1
- package/src/components/Slider/index.css +33 -8
- package/src/components/Slider/index.tsx +61 -33
- package/src/components/WheelObserver/index.tsx +17 -19
- package/src/components/XYPad/Area.tsx +4 -5
- package/src/components/XYPad/Thumb.tsx +8 -7
- package/src/components/XYPad/index.css +18 -4
- package/src/components/XYPad/index.tsx +71 -38
- package/src/components/_util/composeRefs.tsx +63 -0
- package/src/components/_util/index.ts +23 -6
- package/src/components/_util/type.ts +1 -0
- package/src/hooks/useAnimationFrame.ts +0 -3
- package/src/hooks/useCallbackRef.ts +2 -2
- package/src/hooks/useDrag.ts +12 -11
- package/src/hooks/useDragWithElement.ts +12 -17
- package/src/hooks/useEventListener.ts +6 -9
- package/src/hooks/useInterval.ts +0 -3
- package/src/hooks/useLongPress.ts +0 -3
- package/src/hooks/useMIDIAccess.ts +0 -1
- package/src/hooks/useMIDIInput.ts +0 -1
- package/src/hooks/useMIDIMessage.ts +0 -1
- package/src/hooks/usePianoDrag.ts +75 -0
- package/src/index.ts +21 -30
- package/src/styles/global.css +16 -4
- package/dist/index.css.map +0 -1
- package/src/components/AnimationKnob/index.tsx +0 -314
package/dist/index.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
|
-
/* @__PURE__ */ jsx("path", {
|
|
565
|
-
className: clsx("tremolo-knob-active-line", classes?.activeLine),
|
|
566
|
-
d: `M ${x2} ${y2} A ${center} ${center} -135 ${r3 - r2 > 180 ? 1 : 0} 1 ${x3}, ${y3}`,
|
|
567
|
-
fill: "none",
|
|
568
|
-
stroke: activeLine || "currentColor",
|
|
569
|
-
strokeWidth: lineWeight
|
|
570
|
-
}),
|
|
571
|
-
startValue < max && /* @__PURE__ */ jsx("path", {
|
|
572
|
-
className: clsx("tremolo-knob-inactive-line", classes?.inactiveLine),
|
|
573
|
-
d: `M ${x3} ${y3} A ${center} ${center} -135 ${r4 - r3 > 180 ? 1 : 0} 1 ${x4}, ${y4}`,
|
|
574
|
-
fill: "none",
|
|
575
|
-
stroke: inactiveLine || "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,48 +690,139 @@ 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
|
-
//#region src/
|
|
725
|
-
|
|
726
|
-
const
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
}, [
|
|
757
|
-
|
|
699
|
+
//#region src/hooks/useInterval.ts
|
|
700
|
+
function useInterval(callback, delay) {
|
|
701
|
+
const savedCallback = useRef(callback);
|
|
702
|
+
useEffect(() => {
|
|
703
|
+
savedCallback.current = callback;
|
|
704
|
+
}, [callback]);
|
|
705
|
+
useEffect(() => {
|
|
706
|
+
if (delay === null) return;
|
|
707
|
+
const id = setInterval(() => {
|
|
708
|
+
savedCallback.current();
|
|
709
|
+
}, delay);
|
|
710
|
+
return () => {
|
|
711
|
+
clearInterval(id);
|
|
712
|
+
};
|
|
713
|
+
}, [delay]);
|
|
714
|
+
}
|
|
715
|
+
//#endregion
|
|
716
|
+
//#region src/hooks/useLongPress.ts
|
|
717
|
+
function useLongPress(callback, initialDelay = 500, interval = 40) {
|
|
718
|
+
const [pressed, setPressed] = useState(false);
|
|
719
|
+
const [delay, setDelay] = useState(initialDelay);
|
|
720
|
+
useInterval(() => {
|
|
721
|
+
callback();
|
|
722
|
+
setDelay(interval);
|
|
723
|
+
}, pressed ? delay : null);
|
|
724
|
+
useEventListener(globalThis.window, "pointerup", () => {
|
|
725
|
+
setPressed(false);
|
|
726
|
+
setDelay(initialDelay);
|
|
727
|
+
});
|
|
728
|
+
return useCallback(() => {
|
|
729
|
+
callback();
|
|
730
|
+
setPressed(true);
|
|
731
|
+
}, [callback]);
|
|
732
|
+
}
|
|
733
|
+
//#endregion
|
|
734
|
+
//#region src/components/NumberInput/DecrementStepper.tsx
|
|
735
|
+
function DecrementStepper({ size = 12, children, className, ...props }) {
|
|
736
|
+
const min = useNumberInputContext((s) => s.min);
|
|
737
|
+
const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
|
|
738
|
+
const readonly = useNumberInputContext((s) => s.readonly);
|
|
739
|
+
const keepWithinRange = useNumberInputContext((s) => s.keepWithinRange);
|
|
740
|
+
const press = useLongPress(useNumberInputContext((s) => s.decrement));
|
|
741
|
+
return /* @__PURE__ */ jsx("div", {
|
|
742
|
+
className: clsx("tremolo-number-input-decrement-stepper", className),
|
|
743
|
+
role: "button",
|
|
744
|
+
tabIndex: -1,
|
|
745
|
+
"aria-disabled": keepWithinRange && valueAsNumber <= (min ?? Number.MIN_SAFE_INTEGER),
|
|
746
|
+
"aria-readonly": readonly,
|
|
747
|
+
onPointerDown: press,
|
|
748
|
+
...props,
|
|
749
|
+
children: children || /* @__PURE__ */ jsx("svg", {
|
|
750
|
+
width: size,
|
|
751
|
+
height: size,
|
|
752
|
+
viewBox: "0 0 24 24",
|
|
753
|
+
fill: "none",
|
|
754
|
+
stroke: "currentColor",
|
|
755
|
+
strokeWidth: "2",
|
|
756
|
+
strokeLinecap: "round",
|
|
757
|
+
strokeLinejoin: "round",
|
|
758
|
+
children: /* @__PURE__ */ jsx("polyline", { points: "6 9 12 15 18 9" })
|
|
759
|
+
})
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
//#endregion
|
|
763
|
+
//#region src/components/NumberInput/IncrementStepper.tsx
|
|
764
|
+
function IncrementStepper({ size = 12, children, className, ...props }) {
|
|
765
|
+
const max = useNumberInputContext((s) => s.max);
|
|
766
|
+
const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
|
|
767
|
+
const readonly = useNumberInputContext((s) => s.readonly);
|
|
768
|
+
const keepWithinRange = useNumberInputContext((s) => s.keepWithinRange);
|
|
769
|
+
const press = useLongPress(useNumberInputContext((s) => s.increment));
|
|
770
|
+
return /* @__PURE__ */ jsx("div", {
|
|
771
|
+
className: clsx("tremolo-number-input-increment-stepper", className),
|
|
772
|
+
role: "button",
|
|
773
|
+
tabIndex: -1,
|
|
774
|
+
"aria-disabled": keepWithinRange && valueAsNumber >= (max ?? Number.MAX_SAFE_INTEGER),
|
|
775
|
+
"aria-readonly": readonly,
|
|
776
|
+
onPointerDown: press,
|
|
777
|
+
...props,
|
|
778
|
+
children: children || /* @__PURE__ */ jsx("svg", {
|
|
779
|
+
width: size,
|
|
780
|
+
height: size,
|
|
781
|
+
viewBox: "0 0 24 24",
|
|
782
|
+
fill: "none",
|
|
783
|
+
stroke: "currentColor",
|
|
784
|
+
strokeWidth: "2",
|
|
785
|
+
strokeLinecap: "round",
|
|
786
|
+
strokeLinejoin: "round",
|
|
787
|
+
children: /* @__PURE__ */ jsx("polyline", { points: "18 15 12 9 6 15" })
|
|
788
|
+
})
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
//#endregion
|
|
792
|
+
//#region src/components/NumberInput/InternalInput.tsx
|
|
793
|
+
const InternalInput = forwardRef(({ disabled, readonly, selectWithFocus, blurOnEnter, clampValueOnBlur, wheel, keyboard, className, onChange, onFocus, onBlur, onKeyDown, children: _children, ...props }, ref) => {
|
|
794
|
+
const elmRef = useRef(null);
|
|
795
|
+
const [editing, setEditing] = useState(false);
|
|
796
|
+
const value = useNumberInputContext((s) => s.value);
|
|
797
|
+
const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
|
|
798
|
+
const min = useNumberInputContext((s) => s.min);
|
|
799
|
+
const max = useNumberInputContext((s) => s.max);
|
|
800
|
+
const step = useNumberInputContext((s) => s.step);
|
|
801
|
+
const units = useNumberInputContext((s) => s.units);
|
|
802
|
+
const digit = useNumberInputContext((s) => s.digit);
|
|
803
|
+
const change = useNumberInputContext((s) => s.change);
|
|
804
|
+
const error = useMemo(() => {
|
|
805
|
+
if (editing) return false;
|
|
806
|
+
const v = valueAsNumber;
|
|
807
|
+
return (min ?? -Infinity) > v || v > (max ?? Infinity);
|
|
808
|
+
}, [
|
|
809
|
+
editing,
|
|
810
|
+
max,
|
|
811
|
+
min,
|
|
812
|
+
valueAsNumber
|
|
813
|
+
]);
|
|
814
|
+
const updateValueByEvent = useCallback((eventType, x) => {
|
|
815
|
+
let newValue;
|
|
816
|
+
if (eventType == "normalized") {
|
|
817
|
+
if (!min || !max) throw new Error("[NumberInput] \"min\" and \"max\" are required when InputEventOption[0] is set to \"normalized\".");
|
|
818
|
+
newValue = rawValue(normalizeValue(valueAsNumber, min, max) + x, min, max);
|
|
819
|
+
return clamp(stepValue(newValue, step), min, max);
|
|
820
|
+
} else {
|
|
821
|
+
newValue = valueAsNumber + x;
|
|
822
|
+
return stepValue(newValue, step);
|
|
823
|
+
}
|
|
824
|
+
}, [
|
|
825
|
+
max,
|
|
758
826
|
min,
|
|
759
827
|
step,
|
|
760
828
|
valueAsNumber
|
|
@@ -816,8 +884,8 @@ const InternalInput = forwardRef(({ disabled, readonly, selectWithFocus, blurOnE
|
|
|
816
884
|
const v = event.currentTarget.value;
|
|
817
885
|
const cursorPos = event.target.selectionStart;
|
|
818
886
|
change(v);
|
|
819
|
-
const valueAsNumber
|
|
820
|
-
onChange?.(valueAsNumber
|
|
887
|
+
const valueAsNumber = parseValue(v, units, digit).rawValue;
|
|
888
|
+
onChange?.(valueAsNumber, v);
|
|
821
889
|
if (cursorPos) {
|
|
822
890
|
event.target.selectionStart = cursorPos;
|
|
823
891
|
event.target.selectionEnd = cursorPos;
|
|
@@ -848,11 +916,9 @@ const InternalInput = forwardRef(({ disabled, readonly, selectWithFocus, blurOnE
|
|
|
848
916
|
...props
|
|
849
917
|
});
|
|
850
918
|
});
|
|
851
|
-
|
|
852
919
|
//#endregion
|
|
853
920
|
//#region src/components/NumberInput/Stepper.tsx
|
|
854
|
-
|
|
855
|
-
function Stepper({ dynamic = true, children, className,...props }) {
|
|
921
|
+
function Stepper({ dynamic = true, children, className, ...props }) {
|
|
856
922
|
return /* @__PURE__ */ jsx("div", {
|
|
857
923
|
className: clsx("tremolo-number-input-stepper", className),
|
|
858
924
|
"data-dynamic": dynamic,
|
|
@@ -860,162 +926,58 @@ function Stepper({ dynamic = true, children, className,...props }) {
|
|
|
860
926
|
children
|
|
861
927
|
});
|
|
862
928
|
}
|
|
863
|
-
|
|
864
|
-
//#endregion
|
|
865
|
-
//#region src/hooks/useInterval.ts
|
|
866
|
-
/**
|
|
867
|
-
* @category hooks
|
|
868
|
-
*/
|
|
869
|
-
function useInterval(callback, delay) {
|
|
870
|
-
const savedCallback = useRef(callback);
|
|
871
|
-
useEffect(() => {
|
|
872
|
-
savedCallback.current = callback;
|
|
873
|
-
}, [callback]);
|
|
874
|
-
useEffect(() => {
|
|
875
|
-
if (delay === null) return;
|
|
876
|
-
const id = setInterval(() => {
|
|
877
|
-
savedCallback.current();
|
|
878
|
-
}, delay);
|
|
879
|
-
return () => {
|
|
880
|
-
clearInterval(id);
|
|
881
|
-
};
|
|
882
|
-
}, [delay]);
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
//#endregion
|
|
886
|
-
//#region src/hooks/useLongPress.ts
|
|
887
|
-
/**
|
|
888
|
-
* @category hooks
|
|
889
|
-
*/
|
|
890
|
-
function useLongPress(callback, initialDelay = 500, interval = 40) {
|
|
891
|
-
const [pressed, setPressed] = useState(false);
|
|
892
|
-
const [delay, setDelay] = useState(initialDelay);
|
|
893
|
-
useInterval(() => {
|
|
894
|
-
callback();
|
|
895
|
-
setDelay(interval);
|
|
896
|
-
}, pressed ? delay : null);
|
|
897
|
-
useEventListener(globalThis.window, "pointerup", () => {
|
|
898
|
-
setPressed(false);
|
|
899
|
-
setDelay(initialDelay);
|
|
900
|
-
});
|
|
901
|
-
return useCallback(() => {
|
|
902
|
-
callback();
|
|
903
|
-
setPressed(true);
|
|
904
|
-
}, [callback]);
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
//#endregion
|
|
908
|
-
//#region src/components/NumberInput/IncrementStepper.tsx
|
|
909
|
-
/** @category NumberInput */
|
|
910
|
-
function IncrementStepper({ size = 12, children, className,...props }) {
|
|
911
|
-
const max = useNumberInputContext((s) => s.max);
|
|
912
|
-
const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
|
|
913
|
-
const readonly = useNumberInputContext((s) => s.readonly);
|
|
914
|
-
const keepWithinRange = useNumberInputContext((s) => s.keepWithinRange);
|
|
915
|
-
const press = useLongPress(useNumberInputContext((s) => s.increment));
|
|
916
|
-
return /* @__PURE__ */ jsx("div", {
|
|
917
|
-
className: clsx("tremolo-number-input-increment-stepper", className),
|
|
918
|
-
role: "button",
|
|
919
|
-
tabIndex: -1,
|
|
920
|
-
"aria-disabled": keepWithinRange && valueAsNumber >= (max ?? Number.MAX_SAFE_INTEGER),
|
|
921
|
-
"aria-readonly": readonly,
|
|
922
|
-
onPointerDown: press,
|
|
923
|
-
...props,
|
|
924
|
-
children: children || /* @__PURE__ */ jsx("svg", {
|
|
925
|
-
width: size,
|
|
926
|
-
height: size,
|
|
927
|
-
viewBox: "0 0 24 24",
|
|
928
|
-
fill: "none",
|
|
929
|
-
stroke: "currentColor",
|
|
930
|
-
strokeWidth: "2",
|
|
931
|
-
strokeLinecap: "round",
|
|
932
|
-
strokeLinejoin: "round",
|
|
933
|
-
children: /* @__PURE__ */ jsx("polyline", { points: "18 15 12 9 6 15" })
|
|
934
|
-
})
|
|
935
|
-
});
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
//#endregion
|
|
939
|
-
//#region src/components/NumberInput/DecrementStepper.tsx
|
|
940
|
-
/** @category NumberInput */
|
|
941
|
-
function DecrementStepper({ size = 12, children, className,...props }) {
|
|
942
|
-
const min = useNumberInputContext((s) => s.min);
|
|
943
|
-
const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
|
|
944
|
-
const readonly = useNumberInputContext((s) => s.readonly);
|
|
945
|
-
const keepWithinRange = useNumberInputContext((s) => s.keepWithinRange);
|
|
946
|
-
const press = useLongPress(useNumberInputContext((s) => s.decrement));
|
|
947
|
-
return /* @__PURE__ */ jsx("div", {
|
|
948
|
-
className: clsx("tremolo-number-input-decrement-stepper", className),
|
|
949
|
-
role: "button",
|
|
950
|
-
tabIndex: -1,
|
|
951
|
-
"aria-disabled": keepWithinRange && valueAsNumber <= (min ?? Number.MIN_SAFE_INTEGER),
|
|
952
|
-
"aria-readonly": readonly,
|
|
953
|
-
onPointerDown: press,
|
|
954
|
-
...props,
|
|
955
|
-
children: children || /* @__PURE__ */ jsx("svg", {
|
|
956
|
-
width: size,
|
|
957
|
-
height: size,
|
|
958
|
-
viewBox: "0 0 24 24",
|
|
959
|
-
fill: "none",
|
|
960
|
-
stroke: "currentColor",
|
|
961
|
-
strokeWidth: "2",
|
|
962
|
-
strokeLinecap: "round",
|
|
963
|
-
strokeLinejoin: "round",
|
|
964
|
-
children: /* @__PURE__ */ jsx("polyline", { points: "6 9 12 15 18 9" })
|
|
965
|
-
})
|
|
966
|
-
});
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
//#endregion
|
|
970
|
-
//#region src/components/NumberInput/index.tsx
|
|
971
929
|
/**
|
|
972
930
|
* Input with some useful functions for entering numerical values.
|
|
973
|
-
* @category NumberInput
|
|
974
931
|
*/
|
|
975
|
-
const NumberInput =
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
});
|
|
1011
|
-
|
|
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
|
+
}),
|
|
969
|
+
Stepper,
|
|
970
|
+
IncrementStepper,
|
|
971
|
+
DecrementStepper
|
|
972
|
+
};
|
|
1012
973
|
//#endregion
|
|
1013
|
-
//#region src/hooks/
|
|
974
|
+
//#region src/hooks/usePianoDrag.ts
|
|
1014
975
|
/**
|
|
1015
|
-
*
|
|
976
|
+
* Internal
|
|
1016
977
|
* @returns [refCallback, pointerDownHandler]
|
|
978
|
+
* @private
|
|
1017
979
|
*/
|
|
1018
|
-
function
|
|
980
|
+
function usePianoDrag({ baseElementRef, onDrag, onDragStart, onDragEnd }) {
|
|
1019
981
|
const dragged = useRef(false);
|
|
1020
982
|
const normalizedX = useRef(0);
|
|
1021
983
|
const normalizedY = useRef(0);
|
|
@@ -1044,9 +1006,11 @@ function useDragWithElement({ baseElementRef, onDrag, onDragStart, onDragEnd })
|
|
|
1044
1006
|
dragged.current = false;
|
|
1045
1007
|
onDragEnd?.(normalizedX.current, normalizedY.current);
|
|
1046
1008
|
});
|
|
1047
|
-
return
|
|
1009
|
+
return {
|
|
1010
|
+
refHandler,
|
|
1011
|
+
pointerDownHandler
|
|
1012
|
+
};
|
|
1048
1013
|
}
|
|
1049
|
-
|
|
1050
1014
|
//#endregion
|
|
1051
1015
|
//#region src/components/Piano/context.tsx
|
|
1052
1016
|
const createPianoStore = (initProps) => {
|
|
@@ -1069,7 +1033,7 @@ const createPianoStore = (initProps) => {
|
|
|
1069
1033
|
}));
|
|
1070
1034
|
};
|
|
1071
1035
|
const PianoContext = createContext(null);
|
|
1072
|
-
function PianoProvider({ children
|
|
1036
|
+
function PianoProvider({ children, ...props }) {
|
|
1073
1037
|
const storeRef = useRef(null);
|
|
1074
1038
|
if (!storeRef.current) storeRef.current = createPianoStore(props);
|
|
1075
1039
|
useEffect(() => {
|
|
@@ -1081,17 +1045,14 @@ function PianoProvider({ children,...props }) {
|
|
|
1081
1045
|
children
|
|
1082
1046
|
});
|
|
1083
1047
|
}
|
|
1084
|
-
/** @category Piano */
|
|
1085
1048
|
function usePianoContext(selector) {
|
|
1086
1049
|
const store = useContext(PianoContext);
|
|
1087
1050
|
if (!store) throw new Error("Missing PianoContext.Provider in the tree");
|
|
1088
1051
|
return useStore(store, selector);
|
|
1089
1052
|
}
|
|
1090
|
-
|
|
1091
1053
|
//#endregion
|
|
1092
1054
|
//#region src/components/Piano/KeyLabel.tsx
|
|
1093
|
-
|
|
1094
|
-
function KeyLabel({ label, className, wrapperClassName, wrapperStyle, __note, __label,...props }) {
|
|
1055
|
+
function KeyLabel({ label, className, wrapperClassName, wrapperStyle, __note, __label, ...props }) {
|
|
1095
1056
|
const index = getNoteRangeArray(usePianoContext((s) => s.noteRange)).indexOf(__note);
|
|
1096
1057
|
const content = label ? label(__note, index) : __label && __label(__note, index);
|
|
1097
1058
|
return (content != void 0 || content != null) && /* @__PURE__ */ jsx("div", {
|
|
@@ -1104,14 +1065,7 @@ function KeyLabel({ label, className, wrapperClassName, wrapperStyle, __note, __
|
|
|
1104
1065
|
})
|
|
1105
1066
|
});
|
|
1106
1067
|
}
|
|
1107
|
-
|
|
1108
|
-
//#endregion
|
|
1109
|
-
//#region src/components/Piano/key.tsx
|
|
1110
|
-
/** @category Piano */
|
|
1111
|
-
const defaultWhiteKeyWidth = 40;
|
|
1112
|
-
/** @category Piano */
|
|
1113
|
-
const defaultBlackKeyWidth = 26;
|
|
1114
|
-
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) => {
|
|
1115
1069
|
const [played, setPlayed] = useState(false);
|
|
1116
1070
|
const midiMax = usePianoContext((s) => s.midiMax);
|
|
1117
1071
|
const fill = usePianoContext((s) => s.fill);
|
|
@@ -1173,8 +1127,7 @@ const KeyImpl = forwardRef(({ keyType, noteNumber, width, height, bg, color, act
|
|
|
1173
1127
|
})
|
|
1174
1128
|
});
|
|
1175
1129
|
});
|
|
1176
|
-
|
|
1177
|
-
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) => {
|
|
1178
1131
|
return /* @__PURE__ */ jsx(KeyImpl, {
|
|
1179
1132
|
ref,
|
|
1180
1133
|
keyType: "white",
|
|
@@ -1184,8 +1137,7 @@ const WhiteKey = forwardRef(({ width = defaultWhiteKeyWidth, height = "100%", on
|
|
|
1184
1137
|
...props
|
|
1185
1138
|
});
|
|
1186
1139
|
});
|
|
1187
|
-
|
|
1188
|
-
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) => {
|
|
1189
1141
|
return /* @__PURE__ */ jsx(KeyImpl, {
|
|
1190
1142
|
ref,
|
|
1191
1143
|
keyType: "black",
|
|
@@ -1195,10 +1147,8 @@ const BlackKey = forwardRef(({ width = defaultBlackKeyWidth, height = "60%", onC
|
|
|
1195
1147
|
...props
|
|
1196
1148
|
});
|
|
1197
1149
|
});
|
|
1198
|
-
|
|
1199
1150
|
//#endregion
|
|
1200
1151
|
//#region src/components/Piano/keyboardShortcuts.ts
|
|
1201
|
-
/** @category Piano */
|
|
1202
1152
|
const SHORTCUTS = { HOME_ROW: { keys: [
|
|
1203
1153
|
"a",
|
|
1204
1154
|
"w",
|
|
@@ -1218,203 +1168,406 @@ const SHORTCUTS = { HOME_ROW: { keys: [
|
|
|
1218
1168
|
"p",
|
|
1219
1169
|
";"
|
|
1220
1170
|
] } };
|
|
1221
|
-
|
|
1222
1171
|
//#endregion
|
|
1223
1172
|
//#region src/components/Piano/index.tsx
|
|
1224
1173
|
/**
|
|
1225
1174
|
* [noteRange.first, noteRange.first + 1 ..., noteRange.last]
|
|
1226
|
-
* @category Piano
|
|
1227
1175
|
*/
|
|
1228
1176
|
function getNoteRangeArray(noteRange) {
|
|
1229
1177
|
return Array.from({ length: noteRange.last - noteRange.first + 1 }, (_, i) => i + noteRange.first);
|
|
1230
1178
|
}
|
|
1231
|
-
const blackPerWhiteWidth =
|
|
1179
|
+
const blackPerWhiteWidth = 26 / 40;
|
|
1232
1180
|
/**
|
|
1233
1181
|
* Customizable piano component.
|
|
1234
|
-
* @category Piano
|
|
1235
1182
|
*/
|
|
1236
|
-
const Piano =
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
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);
|
|
1255
1283
|
};
|
|
1256
|
-
|
|
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();
|
|
1257
1301
|
}
|
|
1258
|
-
return child;
|
|
1259
1302
|
});
|
|
1260
|
-
|
|
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
|
-
return
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
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"
|
|
1297
1388
|
}
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
]);
|
|
1305
|
-
const onDrag = useCallback((perX, perY) => {
|
|
1306
|
-
if (!pianoRef.current) return;
|
|
1307
|
-
const note = getHitKeyIndex(perX * staticWidth, perY * pianoRef.current.clientHeight);
|
|
1308
|
-
const index = noteRangeArray.indexOf(note);
|
|
1309
|
-
if (index == -1) return;
|
|
1310
|
-
if (hitKeyIndex.current != index) {
|
|
1311
|
-
keyRefs.current[hitKeyIndex.current]?.current?.stop();
|
|
1312
|
-
keyRefs.current[index]?.current?.play();
|
|
1313
|
-
hitKeyIndex.current = index;
|
|
1389
|
+
};
|
|
1390
|
+
return createStore()((set) => ({
|
|
1391
|
+
...DEFAULT_PROPS,
|
|
1392
|
+
...initProps,
|
|
1393
|
+
setContainerElementRef: (containerElementRef) => {
|
|
1394
|
+
set(() => ({ containerElementRef }));
|
|
1314
1395
|
}
|
|
1315
|
-
}
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
if (keyRefs.current[hitKeyIndex.current]?.current?.played()) keyRefs.current[hitKeyIndex.current]?.current?.stop();
|
|
1322
|
-
hitKeyIndex.current = -1;
|
|
1323
|
-
}, [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);
|
|
1324
1402
|
useEffect(() => {
|
|
1325
|
-
if (
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
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);
|
|
1337
1446
|
}, [
|
|
1338
|
-
fill,
|
|
1339
|
-
_whiteNoteWidth,
|
|
1340
|
-
whiteNoteCount
|
|
1341
|
-
]);
|
|
1342
|
-
const [touchMoveRefCallback, pointerDownHandler] = useDragWithElement({
|
|
1343
|
-
baseElementRef: pianoRef,
|
|
1344
1447
|
onDrag,
|
|
1345
|
-
|
|
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);
|
|
1346
1462
|
});
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
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));
|
|
1353
1509
|
}
|
|
1354
1510
|
});
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
onStopNote
|
|
1379
|
-
]);
|
|
1380
|
-
return /* @__PURE__ */ jsx(PianoProvider, {
|
|
1381
|
-
notePosition,
|
|
1382
|
-
noteRange,
|
|
1383
|
-
glissando,
|
|
1384
|
-
midiMax,
|
|
1385
|
-
fill,
|
|
1386
|
-
onPlayNote,
|
|
1387
|
-
onStopNote,
|
|
1388
|
-
label,
|
|
1389
|
-
children: /* @__PURE__ */ jsx("div", {
|
|
1390
|
-
ref: (div) => {
|
|
1391
|
-
pianoRef.current = div;
|
|
1392
|
-
touchMoveRefCallback(div);
|
|
1393
|
-
},
|
|
1394
|
-
className: clsx("tremolo-piano", className),
|
|
1395
|
-
style: {
|
|
1396
|
-
width: fill ? "100%" : staticWidth,
|
|
1397
|
-
height,
|
|
1398
|
-
...style
|
|
1399
|
-
},
|
|
1400
|
-
onPointerDown: (event) => {
|
|
1401
|
-
pointerDownHandler(event);
|
|
1402
|
-
onPointerDown?.(event);
|
|
1403
|
-
},
|
|
1404
|
-
...props,
|
|
1405
|
-
children: childrenWithProps || noteRangeArray.map((note, index) => isWhiteKey(note) ? /* @__PURE__ */ jsx(WhiteKey, {
|
|
1406
|
-
ref: keyRefs.current[index],
|
|
1407
|
-
noteNumber: note,
|
|
1408
|
-
__width: whiteNoteWidth
|
|
1409
|
-
}, note) : /* @__PURE__ */ jsx(BlackKey, {
|
|
1410
|
-
ref: keyRefs.current[index],
|
|
1411
|
-
noteNumber: note,
|
|
1412
|
-
__width: whiteNoteWidth * blackPerWhiteWidth
|
|
1413
|
-
}, note))
|
|
1414
|
-
})
|
|
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
|
|
1415
1534
|
});
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1535
|
+
}
|
|
1536
|
+
//#endregion
|
|
1537
|
+
//#region src/components/PointsEditor/index.tsx
|
|
1538
|
+
const defaultExternalStyles$2 = {
|
|
1539
|
+
userSelectNone: true,
|
|
1540
|
+
cursor: "grabbing"
|
|
1541
|
+
};
|
|
1542
|
+
/**
|
|
1543
|
+
* Multiple Point Controller
|
|
1544
|
+
*/
|
|
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
|
+
};
|
|
1418
1571
|
//#endregion
|
|
1419
1572
|
//#region src/components/Slider/context.tsx
|
|
1420
1573
|
const createSliderStore = (initProps) => {
|
|
@@ -1434,7 +1587,7 @@ const createSliderStore = (initProps) => {
|
|
|
1434
1587
|
}));
|
|
1435
1588
|
};
|
|
1436
1589
|
const SliderContext = createContext(null);
|
|
1437
|
-
function SliderProvider({ children
|
|
1590
|
+
function SliderProvider({ children, ...props }) {
|
|
1438
1591
|
const storeRef = useRef(null);
|
|
1439
1592
|
if (!storeRef.current) storeRef.current = createSliderStore(props);
|
|
1440
1593
|
useEffect(() => {
|
|
@@ -1446,24 +1599,21 @@ function SliderProvider({ children,...props }) {
|
|
|
1446
1599
|
children
|
|
1447
1600
|
});
|
|
1448
1601
|
}
|
|
1449
|
-
/** @category Slider */
|
|
1450
1602
|
function useSliderContext(selector) {
|
|
1451
1603
|
const store = useContext(SliderContext);
|
|
1452
1604
|
if (!store) throw new Error("Missing SliderContext.Provider in the tree");
|
|
1453
1605
|
return useStore(store, selector);
|
|
1454
1606
|
}
|
|
1455
|
-
|
|
1456
1607
|
//#endregion
|
|
1457
1608
|
//#region src/components/Slider/ScaleOption.tsx
|
|
1458
|
-
|
|
1459
|
-
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 }) {
|
|
1460
1610
|
const min = useSliderContext((s) => s.min);
|
|
1461
1611
|
const max = useSliderContext((s) => s.max);
|
|
1462
1612
|
const skew = useSliderContext((s) => s.skew);
|
|
1463
1613
|
const vertical = useSliderContext((s) => s.vertical);
|
|
1464
1614
|
const reverse = useSliderContext((s) => s.reverse);
|
|
1465
|
-
const calcPercent = useCallback((rawValue
|
|
1466
|
-
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);
|
|
1467
1617
|
}, [
|
|
1468
1618
|
vertical,
|
|
1469
1619
|
reverse,
|
|
@@ -1500,7 +1650,6 @@ function ScaleOption({ value, type = "mark-number", label, thickness = 1, length
|
|
|
1500
1650
|
})]
|
|
1501
1651
|
});
|
|
1502
1652
|
}
|
|
1503
|
-
|
|
1504
1653
|
//#endregion
|
|
1505
1654
|
//#region src/components/Slider/type.ts
|
|
1506
1655
|
function generateOptionsList(options, min, max, step) {
|
|
@@ -1516,11 +1665,9 @@ function generateOptionsList(options, min, max, step) {
|
|
|
1516
1665
|
}
|
|
1517
1666
|
return optionsList;
|
|
1518
1667
|
}
|
|
1519
|
-
|
|
1520
1668
|
//#endregion
|
|
1521
1669
|
//#region src/components/Slider/Scale.tsx
|
|
1522
|
-
|
|
1523
|
-
function Scale({ gap = 6, options, children, className, style,...props }) {
|
|
1670
|
+
function Scale({ gap = 6, options, children, className, style, ...props }) {
|
|
1524
1671
|
const min = useSliderContext((s) => s.min);
|
|
1525
1672
|
const max = useSliderContext((s) => s.max);
|
|
1526
1673
|
const step = useSliderContext((s) => s.step);
|
|
@@ -1544,13 +1691,7 @@ function Scale({ gap = 6, options, children, className, style,...props }) {
|
|
|
1544
1691
|
}) : children
|
|
1545
1692
|
});
|
|
1546
1693
|
}
|
|
1547
|
-
|
|
1548
|
-
//#endregion
|
|
1549
|
-
//#region src/components/Slider/Thumb.tsx
|
|
1550
|
-
/** @category Slider */
|
|
1551
|
-
const defaultThumbSize = 22;
|
|
1552
|
-
/** @category Slider */
|
|
1553
|
-
const SliderThumb = 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) => {
|
|
1554
1695
|
const wrapperRef = useRef(null);
|
|
1555
1696
|
const vertical = useSliderContext((s) => s.vertical);
|
|
1556
1697
|
const disabled = useSliderContext((s) => s.disabled);
|
|
@@ -1586,13 +1727,7 @@ const SliderThumb = forwardRef(({ size, width = defaultThumbSize, height = defau
|
|
|
1586
1727
|
})
|
|
1587
1728
|
});
|
|
1588
1729
|
});
|
|
1589
|
-
|
|
1590
|
-
//#endregion
|
|
1591
|
-
//#region src/components/Slider/Track.tsx
|
|
1592
|
-
const defaultLength = 140;
|
|
1593
|
-
const defaultThickness = 10;
|
|
1594
|
-
/** @category Slider */
|
|
1595
|
-
function SliderTrack({ 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 }) {
|
|
1596
1731
|
const vertical = useSliderContext((s) => s.vertical);
|
|
1597
1732
|
const reverse = useSliderContext((s) => s.reverse);
|
|
1598
1733
|
const disabled = useSliderContext((s) => s.disabled);
|
|
@@ -1617,207 +1752,259 @@ function SliderTrack({ length = defaultLength, thickness = defaultThickness, act
|
|
|
1617
1752
|
children: [children, __thumb]
|
|
1618
1753
|
});
|
|
1619
1754
|
}
|
|
1620
|
-
|
|
1621
1755
|
//#endregion
|
|
1622
1756
|
//#region src/components/Slider/index.tsx
|
|
1757
|
+
const defaultExternalStyles$1 = {
|
|
1758
|
+
userSelectNone: true,
|
|
1759
|
+
cursor: "pointer"
|
|
1760
|
+
};
|
|
1623
1761
|
/**
|
|
1624
1762
|
* Customizable slider
|
|
1625
|
-
* @category Slider
|
|
1626
1763
|
*/
|
|
1627
|
-
const Slider =
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
let trackProps = {};
|
|
1635
|
-
let thumbProps = {};
|
|
1636
|
-
let scaleComponent = void 0;
|
|
1637
|
-
if (children != void 0) React.Children.forEach(children, (child) => {
|
|
1638
|
-
if (React.isValidElement(child)) if (child.type == SliderThumb) thumbProps = child.props;
|
|
1639
|
-
else if (child.type == SliderTrack) trackProps = child.props;
|
|
1640
|
-
else if (child.type == Scale) scaleComponent = child;
|
|
1641
|
-
else throw new Error("only <SliderThumb> or <SliderTrack>");
|
|
1642
|
-
else throw new Error("children is an invalid element.");
|
|
1643
|
-
});
|
|
1644
|
-
const onDrag = useCallback((nx, ny) => {
|
|
1645
|
-
if (!onChange || readonly) return;
|
|
1646
|
-
const n = vertical ? ny : nx;
|
|
1647
|
-
onChange(clamp(stepValue(rawValue(displayReversed ? 1 - n : n, min, max, skew), step), min, max));
|
|
1648
|
-
}, [
|
|
1649
|
-
displayReversed,
|
|
1650
|
-
max,
|
|
1651
|
-
min,
|
|
1652
|
-
onChange,
|
|
1653
|
-
readonly,
|
|
1654
|
-
skew,
|
|
1655
|
-
step,
|
|
1656
|
-
vertical
|
|
1657
|
-
]);
|
|
1658
|
-
const updateValueByEvent = useCallback((eventType, x) => {
|
|
1659
|
-
let newValue;
|
|
1660
|
-
if (eventType == "normalized") newValue = rawValue(normalizeValue(value, min, max, skew) + x, min, max, skew);
|
|
1661
|
-
else newValue = value + x;
|
|
1662
|
-
return clamp(stepValue(newValue, step), min, max);
|
|
1663
|
-
}, [
|
|
1664
|
-
max,
|
|
1665
|
-
min,
|
|
1666
|
-
skew,
|
|
1667
|
-
step,
|
|
1668
|
-
value
|
|
1669
|
-
]);
|
|
1670
|
-
const handleKeyDown = useCallback((event) => {
|
|
1671
|
-
if (!keyboard || !onChange || readonly) return;
|
|
1672
|
-
const key = event.key;
|
|
1673
|
-
if ([
|
|
1674
|
-
"ArrowRight",
|
|
1675
|
-
"ArrowLeft",
|
|
1676
|
-
"ArrowUp",
|
|
1677
|
-
"ArrowDown"
|
|
1678
|
-
].includes(key)) {
|
|
1679
|
-
event.preventDefault();
|
|
1680
|
-
let x = key == "ArrowRight" || key == "ArrowUp" ? keyboard[1] : -keyboard[1];
|
|
1681
|
-
if (reverse) x *= -1;
|
|
1682
|
-
onChange(updateValueByEvent(keyboard[0], x));
|
|
1683
|
-
}
|
|
1684
|
-
}, [
|
|
1685
|
-
keyboard,
|
|
1686
|
-
onChange,
|
|
1687
|
-
readonly,
|
|
1688
|
-
reverse,
|
|
1689
|
-
updateValueByEvent
|
|
1690
|
-
]);
|
|
1691
|
-
const [touchMoveRefCallback, pointerDownHandler] = useDragWithElement({
|
|
1692
|
-
baseElementRef: trackElementRef,
|
|
1693
|
-
onDrag,
|
|
1694
|
-
onDragStart: (nx, ny) => {
|
|
1695
|
-
if (readonly) return;
|
|
1696
|
-
if (bodyNoSelect) addNoSelect();
|
|
1697
|
-
thumbRef.current?.focus();
|
|
1698
|
-
onDragStart?.(clamp(stepValue(rawValue(vertical ? ny : nx, min, max, skew), step), min, max));
|
|
1699
|
-
},
|
|
1700
|
-
onDragEnd: (nx, ny) => {
|
|
1701
|
-
if (readonly) return;
|
|
1702
|
-
if (bodyNoSelect) removeNoSelect();
|
|
1703
|
-
onDragEnd?.(clamp(stepValue(rawValue(vertical ? ny : nx, min, max, skew), step), min, max));
|
|
1704
|
-
}
|
|
1705
|
-
});
|
|
1706
|
-
const wheelRefCallback = useRefCallbackEvent("wheel", (event) => {
|
|
1707
|
-
if (!wheel || !onChange || readonly) return;
|
|
1708
|
-
event.preventDefault();
|
|
1709
|
-
let x;
|
|
1710
|
-
if (!vertical && event.deltaX != 0) x = event.deltaX > 0 ? wheel[1] : -wheel[1];
|
|
1711
|
-
else {
|
|
1712
|
-
if (event.deltaY == 0) return;
|
|
1713
|
-
x = event.deltaY > 0 ? -wheel[1] : wheel[1];
|
|
1714
|
-
}
|
|
1715
|
-
if (vertical && reverse) x *= -1;
|
|
1716
|
-
onChange(updateValueByEvent(wheel[0], x));
|
|
1717
|
-
}, { passive: false }, [
|
|
1718
|
-
wheel,
|
|
1719
|
-
vertical,
|
|
1720
|
-
readonly,
|
|
1721
|
-
onChange,
|
|
1722
|
-
updateValueByEvent
|
|
1723
|
-
]);
|
|
1724
|
-
useImperativeHandle(forwardedRef, () => {
|
|
1725
|
-
return {
|
|
1726
|
-
focus() {
|
|
1727
|
-
thumbRef.current?.focus();
|
|
1728
|
-
},
|
|
1729
|
-
blur() {
|
|
1730
|
-
thumbRef.current?.blur();
|
|
1731
|
-
}
|
|
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
|
|
1732
1771
|
};
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
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
|
-
|
|
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);
|
|
1770
1840
|
thumbRef.current?.focus();
|
|
1771
|
-
|
|
1772
|
-
},
|
|
1773
|
-
onBlur: (event) => {
|
|
1774
|
-
thumbRef.current?.blur();
|
|
1775
|
-
onBlur?.(event);
|
|
1841
|
+
onDragStart?.(clamp(stepValue(rawValue(vertical ? ny : nx, min, max, skew), step), min, max));
|
|
1776
1842
|
},
|
|
1777
|
-
|
|
1778
|
-
|
|
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,
|
|
1779
1901
|
style: {
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
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);
|
|
1784
1912
|
},
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
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, {
|
|
1792
1933
|
__percent: percent,
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1934
|
+
__thumb: /* @__PURE__ */ jsx(Thumb$1, {
|
|
1935
|
+
ref: thumbRef,
|
|
1936
|
+
__percent: percent,
|
|
1937
|
+
...thumbProps
|
|
1938
|
+
}),
|
|
1939
|
+
...trackProps
|
|
1940
|
+
})
|
|
1941
|
+
}), scaleComponent]
|
|
1942
|
+
})
|
|
1798
1943
|
})
|
|
1799
|
-
})
|
|
1800
|
-
})
|
|
1801
|
-
|
|
1802
|
-
|
|
1944
|
+
});
|
|
1945
|
+
}),
|
|
1946
|
+
Thumb: Thumb$1,
|
|
1947
|
+
Track,
|
|
1948
|
+
Scale,
|
|
1949
|
+
ScaleOption
|
|
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
|
+
}
|
|
1803
1982
|
//#endregion
|
|
1804
1983
|
//#region src/components/WheelObserver/index.tsx
|
|
1805
|
-
/**
|
|
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
|
+
*/
|
|
1806
1995
|
function WheelObserver(props) {
|
|
1807
|
-
const {
|
|
1996
|
+
const { as: Component = "div", children, onWheel, ref, ...attributes } = props;
|
|
1808
1997
|
return /* @__PURE__ */ jsx(Component, {
|
|
1809
|
-
ref: useRefCallbackEvent("wheel", (event) => {
|
|
1998
|
+
ref: composeRefs(ref, useRefCallbackEvent("wheel", (event) => {
|
|
1810
1999
|
if (onWheel) onWheel(event);
|
|
1811
|
-
}, { passive: false }, [onWheel]),
|
|
2000
|
+
}, { passive: false }, [onWheel])),
|
|
1812
2001
|
...attributes,
|
|
1813
2002
|
children
|
|
1814
2003
|
});
|
|
1815
2004
|
}
|
|
1816
|
-
|
|
1817
2005
|
//#endregion
|
|
1818
2006
|
//#region src/components/XYPad/Area.tsx
|
|
1819
|
-
|
|
1820
|
-
function XYPadArea({ width = 120, height = 120, color, children, className, style, __thumb,...props }) {
|
|
2007
|
+
function Area({ width = 120, height = 120, color, children, className, style, __thumb, ...props }) {
|
|
1821
2008
|
return /* @__PURE__ */ jsxs("div", {
|
|
1822
2009
|
className: clsx("tremolo-xy-pad-area", className),
|
|
1823
2010
|
style: {
|
|
@@ -1830,11 +2017,9 @@ function XYPadArea({ width = 120, height = 120, color, children, className, styl
|
|
|
1830
2017
|
children: [children, __thumb]
|
|
1831
2018
|
});
|
|
1832
2019
|
}
|
|
1833
|
-
|
|
1834
2020
|
//#endregion
|
|
1835
2021
|
//#region src/components/XYPad/Thumb.tsx
|
|
1836
|
-
|
|
1837
|
-
const XYPadThumb = forwardRef(({ size, width = 22, height = 22, color, children, wrapperClassName, wrapperStyle, className, style, __disabled, __css,...props }, ref) => {
|
|
2022
|
+
const Thumb = forwardRef(({ size, width = 22, height = 22, color, children, wrapperClassName, wrapperStyle, className, style, __disabled, __readonly, __css, ...props }, ref) => {
|
|
1838
2023
|
const wrapperRef = useRef(null);
|
|
1839
2024
|
useImperativeHandle(ref, () => {
|
|
1840
2025
|
return {
|
|
@@ -1858,6 +2043,7 @@ const XYPadThumb = forwardRef(({ size, width = 22, height = 22, color, children,
|
|
|
1858
2043
|
className: clsx("tremolo-xy-pad-thumb", className),
|
|
1859
2044
|
tabIndex: 0,
|
|
1860
2045
|
"aria-disabled": __disabled,
|
|
2046
|
+
"aria-readonly": __readonly,
|
|
1861
2047
|
style: {
|
|
1862
2048
|
"--color": color,
|
|
1863
2049
|
width: size ?? width,
|
|
@@ -1867,7 +2053,6 @@ const XYPadThumb = forwardRef(({ size, width = 22, height = 22, color, children,
|
|
|
1867
2053
|
})
|
|
1868
2054
|
});
|
|
1869
2055
|
});
|
|
1870
|
-
|
|
1871
2056
|
//#endregion
|
|
1872
2057
|
//#region src/components/XYPad/index.tsx
|
|
1873
2058
|
const defaultValueOptions = {
|
|
@@ -1877,192 +2062,201 @@ const defaultValueOptions = {
|
|
|
1877
2062
|
wheel: ["raw", 1],
|
|
1878
2063
|
keyboard: ["raw", 1]
|
|
1879
2064
|
};
|
|
2065
|
+
const defaultExternalStyles = {
|
|
2066
|
+
userSelectNone: true,
|
|
2067
|
+
cursor: "pointer"
|
|
2068
|
+
};
|
|
1880
2069
|
/**
|
|
1881
2070
|
* Simple XYPad
|
|
1882
|
-
* @category XYPad
|
|
1883
2071
|
*/
|
|
1884
|
-
const XYPad =
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
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
|
|
1895
2092
|
};
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
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
|
-
|
|
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;
|
|
1948
2182
|
event.preventDefault();
|
|
1949
|
-
let delta = target.
|
|
1950
|
-
if (
|
|
2183
|
+
let delta = target.wheel[1];
|
|
2184
|
+
if (event.deltaY < 0) delta *= -1;
|
|
1951
2185
|
if (target.reverse) delta *= -1;
|
|
1952
|
-
const newValue = updateValueByEvent(target.
|
|
1953
|
-
if (
|
|
2186
|
+
const newValue = updateValueByEvent(target.wheel[0], target, delta);
|
|
2187
|
+
if (event.shiftKey) onChange(newValue, y.value);
|
|
1954
2188
|
else onChange(x.value, newValue);
|
|
1955
|
-
}
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
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
|
-
x,
|
|
1995
|
-
y,
|
|
1996
|
-
onChange,
|
|
1997
|
-
readonly,
|
|
1998
|
-
updateValueByEvent
|
|
1999
|
-
]);
|
|
2000
|
-
useImperativeHandle(forwardedRef, () => {
|
|
2001
|
-
return {
|
|
2002
|
-
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) => {
|
|
2003
2228
|
thumbRef.current?.focus();
|
|
2229
|
+
onFocus?.(event);
|
|
2004
2230
|
},
|
|
2005
|
-
|
|
2231
|
+
onBlur: (event) => {
|
|
2006
2232
|
thumbRef.current?.blur();
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
pointerDownHandler(event);
|
|
2027
|
-
onPointerDown?.(event);
|
|
2028
|
-
},
|
|
2029
|
-
onKeyDown: (event) => {
|
|
2030
|
-
handleKeyDown(event);
|
|
2031
|
-
onKeyDown?.(event);
|
|
2032
|
-
},
|
|
2033
|
-
onFocus: (event) => {
|
|
2034
|
-
thumbRef.current?.focus();
|
|
2035
|
-
onFocus?.(event);
|
|
2036
|
-
},
|
|
2037
|
-
onBlur: (event) => {
|
|
2038
|
-
thumbRef.current?.blur();
|
|
2039
|
-
onBlur?.(event);
|
|
2040
|
-
},
|
|
2041
|
-
...props,
|
|
2042
|
-
children: /* @__PURE__ */ jsx("div", {
|
|
2043
|
-
className: "tremolo-xy-pad-area-wrapper",
|
|
2044
|
-
ref: areaElementRef,
|
|
2045
|
-
children: /* @__PURE__ */ jsx(XYPadArea, {
|
|
2046
|
-
__thumb: /* @__PURE__ */ jsx(XYPadThumb, {
|
|
2047
|
-
ref: thumbRef,
|
|
2048
|
-
__disabled: disabled,
|
|
2049
|
-
__css: {
|
|
2050
|
-
top: `${percentY}%`,
|
|
2051
|
-
left: `${percentX}%`
|
|
2052
|
-
},
|
|
2053
|
-
...thumbProps
|
|
2054
|
-
}),
|
|
2055
|
-
...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
|
+
})
|
|
2056
2252
|
})
|
|
2057
|
-
})
|
|
2058
|
-
})
|
|
2059
|
-
|
|
2060
|
-
|
|
2253
|
+
});
|
|
2254
|
+
}),
|
|
2255
|
+
Thumb,
|
|
2256
|
+
Area
|
|
2257
|
+
};
|
|
2061
2258
|
//#endregion
|
|
2062
2259
|
//#region src/hooks/useAnimationFrame.ts
|
|
2063
|
-
/**
|
|
2064
|
-
* @category hooks
|
|
2065
|
-
*/
|
|
2066
2260
|
function useAnimationFrame(callback = () => {}, deps = []) {
|
|
2067
2261
|
const reqIdRef = useRef(-1);
|
|
2068
2262
|
const loop = useCallback(() => {
|
|
@@ -2074,7 +2268,6 @@ function useAnimationFrame(callback = () => {}, deps = []) {
|
|
|
2074
2268
|
return () => cancelAnimationFrame(reqIdRef.current);
|
|
2075
2269
|
}, [loop]);
|
|
2076
2270
|
}
|
|
2077
|
-
|
|
2078
2271
|
//#endregion
|
|
2079
2272
|
//#region src/hooks/useMIDIAccess.ts
|
|
2080
2273
|
/** @private */
|
|
@@ -2083,7 +2276,6 @@ const PERMISSION_DENIED = "PERMISSION_DENIED";
|
|
|
2083
2276
|
const NOT_SUPPORTED = "NOT_SUPPORTED";
|
|
2084
2277
|
/**
|
|
2085
2278
|
* Hooks for requesting MIDI access in the browser. The first argument allows you to choose whether to request access on mount.
|
|
2086
|
-
* @category hooks
|
|
2087
2279
|
*/
|
|
2088
2280
|
function useMIDIAccess(requestOnMount = true) {
|
|
2089
2281
|
const [midiAccess, setMidiAccess] = useState(null);
|
|
@@ -2106,12 +2298,10 @@ function useMIDIAccess(requestOnMount = true) {
|
|
|
2106
2298
|
error
|
|
2107
2299
|
};
|
|
2108
2300
|
}
|
|
2109
|
-
|
|
2110
2301
|
//#endregion
|
|
2111
2302
|
//#region src/hooks/useMIDIMessage.ts
|
|
2112
2303
|
/**
|
|
2113
2304
|
* Hooks for when you want to process MIDI events in more detail than useMIDIInput.
|
|
2114
|
-
* @category hooks
|
|
2115
2305
|
*/
|
|
2116
2306
|
function useMIDIMessage(midiAccess, onMIDIMessage) {
|
|
2117
2307
|
useEffect(() => {
|
|
@@ -2122,7 +2312,6 @@ function useMIDIMessage(midiAccess, onMIDIMessage) {
|
|
|
2122
2312
|
};
|
|
2123
2313
|
}, [midiAccess, onMIDIMessage]);
|
|
2124
2314
|
}
|
|
2125
|
-
|
|
2126
2315
|
//#endregion
|
|
2127
2316
|
//#region src/hooks/useMIDIInput.ts
|
|
2128
2317
|
const MIDI_EVENT_TO_NUMBER = {
|
|
@@ -2132,7 +2321,6 @@ const MIDI_EVENT_TO_NUMBER = {
|
|
|
2132
2321
|
};
|
|
2133
2322
|
/**
|
|
2134
2323
|
* Hooks for handling note on/off events. To be used with useMIDIAccess. Internally uses useMIDIMessage.
|
|
2135
|
-
* @category hooks
|
|
2136
2324
|
*/
|
|
2137
2325
|
function useMIDIInput(midiAccess, onNoteOnEvent, onNoteOffEvent, onPitchBendEvent) {
|
|
2138
2326
|
useMIDIMessage(midiAccess, useCallback((event) => {
|
|
@@ -2147,7 +2335,7 @@ function useMIDIInput(midiAccess, onNoteOnEvent, onNoteOffEvent, onPitchBendEven
|
|
|
2147
2335
|
onPitchBendEvent
|
|
2148
2336
|
]));
|
|
2149
2337
|
}
|
|
2150
|
-
|
|
2151
2338
|
//#endregion
|
|
2152
|
-
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
|
+
|
|
2153
2341
|
//# sourceMappingURL=index.js.map
|