@tremolo-ui/react 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +193 -278
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -59
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +51 -59
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +182 -276
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
- package/src/components/Knob/index.tsx +13 -30
- package/src/components/NumberInput/InternalInput.tsx +10 -15
- package/src/components/PointsEditor/Point.tsx +25 -40
- package/src/components/Slider/index.tsx +22 -37
- package/src/components/XYPad/index.tsx +24 -41
- package/src/hooks/useDrag.ts +41 -76
- package/src/hooks/useDragWithElement.ts +71 -43
- package/src/hooks/useMIDIAccess.ts +20 -27
- package/src/hooks/useMIDIInput.ts +10 -37
- package/src/hooks/useMIDIMessage.ts +4 -11
- package/src/hooks/useWheel.ts +31 -0
- package/src/index.ts +1 -5
- package/src/components/DragObserver/index.tsx +0 -58
- package/src/components/WheelObserver/index.tsx +0 -48
package/dist/index.cjs
CHANGED
|
@@ -27,6 +27,7 @@ let react = require("react");
|
|
|
27
27
|
react = __toESM(react);
|
|
28
28
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
29
29
|
let _tremolo_ui_functions = require("@tremolo-ui/functions");
|
|
30
|
+
let _tremolo_ui_dom = require("@tremolo-ui/dom");
|
|
30
31
|
let zustand = require("zustand");
|
|
31
32
|
//#region src/components/AnimationCanvas/canvas.ts
|
|
32
33
|
const drawingState = [
|
|
@@ -179,110 +180,55 @@ function useCallbackRef(callback, deps = []) {
|
|
|
179
180
|
return (0, react.useCallback)((...args) => callbackRef.current?.(...args), deps);
|
|
180
181
|
}
|
|
181
182
|
//#endregion
|
|
182
|
-
//#region src/hooks/
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
//#region src/hooks/useDrag.ts
|
|
184
|
+
/**
|
|
185
|
+
* Track a pointer drag on an element.
|
|
186
|
+
*
|
|
187
|
+
* @returns a ref callback to attach to the element being dragged
|
|
188
|
+
*/
|
|
189
|
+
function useDrag({ threshold = 1, cursor, onDrag, onDragStart, onDragEnd }) {
|
|
190
|
+
const dragHandler = useCallbackRef(onDrag);
|
|
191
|
+
const dragStartHandler = useCallbackRef(onDragStart);
|
|
192
|
+
const dragEndHandler = useCallbackRef(onDragEnd);
|
|
193
|
+
const [node, setNode] = (0, react.useState)(null);
|
|
185
194
|
(0, react.useEffect)(() => {
|
|
186
|
-
const node = typeof target === "function" ? target() : target ?? document;
|
|
187
195
|
if (!node) return;
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
196
|
+
const instance = (0, _tremolo_ui_dom.createDrag)(node, {
|
|
197
|
+
threshold,
|
|
198
|
+
cursor,
|
|
199
|
+
onDragStart: () => dragStartHandler(),
|
|
200
|
+
onDrag: ({ x, y, deltaX, deltaY }) => dragHandler(x, y, deltaX, deltaY),
|
|
201
|
+
onDragEnd: () => dragEndHandler()
|
|
202
|
+
});
|
|
203
|
+
return () => instance.destroy();
|
|
192
204
|
}, [
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
205
|
+
node,
|
|
206
|
+
threshold,
|
|
207
|
+
cursor,
|
|
208
|
+
dragHandler,
|
|
209
|
+
dragStartHandler,
|
|
210
|
+
dragEndHandler
|
|
197
211
|
]);
|
|
198
|
-
return
|
|
199
|
-
(typeof target === "function" ? target() : target ?? document)?.removeEventListener(event, listener, options);
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
//#endregion
|
|
203
|
-
//#region src/hooks/useRefCallbackEvent.ts
|
|
204
|
-
function useRefCallbackEvent(event, handler, options, deps = []) {
|
|
205
|
-
return (0, react.useMemo)(() => {
|
|
206
|
-
let cleanup;
|
|
207
|
-
return (node) => {
|
|
208
|
-
if (!node) {
|
|
209
|
-
cleanup?.();
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
node.addEventListener(event, handler, options);
|
|
213
|
-
cleanup = () => {
|
|
214
|
-
node.removeEventListener(event, handler, options);
|
|
215
|
-
};
|
|
216
|
-
};
|
|
217
|
-
}, deps);
|
|
212
|
+
return setNode;
|
|
218
213
|
}
|
|
219
214
|
//#endregion
|
|
220
|
-
//#region src/hooks/
|
|
215
|
+
//#region src/hooks/useWheel.ts
|
|
221
216
|
/**
|
|
222
|
-
*
|
|
217
|
+
* Listen to wheel events on an element.
|
|
218
|
+
*
|
|
219
|
+
* The listener is not passive, so the handler may call `preventDefault()`.
|
|
220
|
+
*
|
|
221
|
+
* @returns a ref callback to attach to the element
|
|
223
222
|
*/
|
|
224
|
-
function
|
|
225
|
-
const
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const screenX = isTouch ? event.touches[0].screenX : event.screenX;
|
|
234
|
-
const screenY = isTouch ? event.touches[0].screenY : event.screenY;
|
|
235
|
-
let deltaX = 0;
|
|
236
|
-
let deltaY = 0;
|
|
237
|
-
if (first) {
|
|
238
|
-
dragStartX.current = screenX;
|
|
239
|
-
dragStartY.current = screenY;
|
|
240
|
-
}
|
|
241
|
-
if (dragOffsetX.current) {
|
|
242
|
-
deltaX = screenX - dragOffsetX.current;
|
|
243
|
-
dragOffsetX.current = screenX;
|
|
244
|
-
}
|
|
245
|
-
if (dragOffsetY.current) {
|
|
246
|
-
deltaY = screenY - dragOffsetY.current;
|
|
247
|
-
dragOffsetY.current = screenY;
|
|
248
|
-
}
|
|
249
|
-
if (Math.abs(deltaX) < threshold && Math.abs(deltaY) < threshold) return;
|
|
250
|
-
onDrag?.(screenX - dragStartX.current, screenY - dragStartY.current, deltaX, deltaY);
|
|
251
|
-
}, [threshold, onDrag]);
|
|
252
|
-
const pointerDownHandler = (0, react.useCallback)((event) => {
|
|
253
|
-
dragOffsetX.current = event.screenX;
|
|
254
|
-
dragOffsetY.current = event.screenY;
|
|
255
|
-
handleDrag(event, true);
|
|
256
|
-
onDragStart?.();
|
|
257
|
-
}, [handleDrag, onDragStart]);
|
|
258
|
-
const refHandler = useRefCallbackEvent("touchmove", handleDrag, { passive: false }, [onDrag]);
|
|
259
|
-
useEventListener(globalThis.window, "mousemove", handleDrag);
|
|
260
|
-
useEventListener(globalThis.window, "pointerup", () => {
|
|
261
|
-
dragOffsetX.current = void 0;
|
|
262
|
-
dragOffsetY.current = void 0;
|
|
263
|
-
onDragEnd?.();
|
|
264
|
-
});
|
|
265
|
-
return [refHandler, pointerDownHandler];
|
|
266
|
-
}
|
|
267
|
-
//#endregion
|
|
268
|
-
//#region src/components/DragObserver/index.tsx
|
|
269
|
-
function DragObserver(props) {
|
|
270
|
-
const { as: Component = "div", threshold = 1, onDrag, onDragStart, onDragEnd, children, onPointerDown, ...attributes } = props;
|
|
271
|
-
const [refCallback, pointerDownHandler] = useDrag({
|
|
272
|
-
threshold,
|
|
273
|
-
onDrag,
|
|
274
|
-
onDragStart,
|
|
275
|
-
onDragEnd
|
|
276
|
-
});
|
|
277
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, {
|
|
278
|
-
ref: refCallback,
|
|
279
|
-
onPointerDown: (e) => {
|
|
280
|
-
pointerDownHandler(e);
|
|
281
|
-
onPointerDown?.(e);
|
|
282
|
-
},
|
|
283
|
-
...attributes,
|
|
284
|
-
children
|
|
285
|
-
});
|
|
223
|
+
function useWheel(onWheel) {
|
|
224
|
+
const wheelHandler = useCallbackRef(onWheel);
|
|
225
|
+
const [node, setNode] = (0, react.useState)(null);
|
|
226
|
+
(0, react.useEffect)(() => {
|
|
227
|
+
if (!node) return;
|
|
228
|
+
const instance = (0, _tremolo_ui_dom.createWheel)(node, (event) => wheelHandler(event));
|
|
229
|
+
return () => instance.destroy();
|
|
230
|
+
}, [node, wheelHandler]);
|
|
231
|
+
return setNode;
|
|
286
232
|
}
|
|
287
233
|
//#endregion
|
|
288
234
|
//#region src/components/_util/index.ts
|
|
@@ -292,22 +238,6 @@ function addUserSelectNone() {
|
|
|
292
238
|
function removeUserSelectNone() {
|
|
293
239
|
window.document.body.classList.remove("tremolo-user-select-none");
|
|
294
240
|
}
|
|
295
|
-
const cursorStyles = {
|
|
296
|
-
grabbing: "tremolo-cursor-grabbing",
|
|
297
|
-
grab: "tremolo-cursor-grab",
|
|
298
|
-
pointer: "tremolo-cursor-pointer",
|
|
299
|
-
move: "tremolo-cursor-move",
|
|
300
|
-
none: "tremolo-cursor-none"
|
|
301
|
-
};
|
|
302
|
-
function setCursorStyle(type) {
|
|
303
|
-
resetCursorStyle();
|
|
304
|
-
window.document.body.classList.add(cursorStyles[type]);
|
|
305
|
-
}
|
|
306
|
-
function resetCursorStyle() {
|
|
307
|
-
Object.values(cursorStyles).forEach((s) => {
|
|
308
|
-
window.document.body.classList.remove(s);
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
241
|
function calcDrawingState({ value, min, max, skew, startValue, angleRange }) {
|
|
312
242
|
const p = (0, _tremolo_ui_functions.normalizeValue)(value, min, max, skew);
|
|
313
243
|
const s = (0, _tremolo_ui_functions.normalizeValue)(startValue, min, max, skew);
|
|
@@ -517,32 +447,26 @@ const Knob = {
|
|
|
517
447
|
readonly,
|
|
518
448
|
updateValueByEvent
|
|
519
449
|
]);
|
|
520
|
-
const
|
|
450
|
+
const dragRefCallback = useDrag({
|
|
451
|
+
cursor: readonly ? void 0 : externalStyles.cursor,
|
|
521
452
|
onDrag,
|
|
522
453
|
onDragStart: () => {
|
|
523
454
|
setDragging(true);
|
|
524
455
|
valueRef.current = (0, _tremolo_ui_functions.normalizeValue)(value, min, max, skew);
|
|
525
456
|
if (externalStyles.userSelectNone) addUserSelectNone();
|
|
526
|
-
if (externalStyles.cursor) setCursorStyle(externalStyles.cursor);
|
|
527
457
|
},
|
|
528
458
|
onDragEnd: () => {
|
|
529
459
|
setDragging(false);
|
|
530
460
|
if (externalStyles.userSelectNone) removeUserSelectNone();
|
|
531
|
-
if (externalStyles.cursor) resetCursorStyle();
|
|
532
461
|
}
|
|
533
462
|
});
|
|
534
|
-
const wheelRefCallback =
|
|
463
|
+
const wheelRefCallback = useWheel((event) => {
|
|
535
464
|
if (!wheel || readonly) return;
|
|
536
465
|
event.preventDefault();
|
|
537
466
|
if (!onChange || event.deltaY == 0) return;
|
|
538
467
|
const x = Math.sign(event.deltaY) * -wheel[1];
|
|
539
468
|
onChange(updateValueByEvent(wheel[0], x));
|
|
540
|
-
}
|
|
541
|
-
wheel,
|
|
542
|
-
onChange,
|
|
543
|
-
readonly,
|
|
544
|
-
updateValueByEvent
|
|
545
|
-
]);
|
|
469
|
+
});
|
|
546
470
|
(0, react.useImperativeHandle)(forwardedRef, () => {
|
|
547
471
|
return {
|
|
548
472
|
focus() {
|
|
@@ -565,7 +489,7 @@ const Knob = {
|
|
|
565
489
|
ref: (elm) => {
|
|
566
490
|
elmRef.current = elm;
|
|
567
491
|
wheelRefCallback(elm);
|
|
568
|
-
|
|
492
|
+
dragRefCallback(elm);
|
|
569
493
|
},
|
|
570
494
|
className: (0, clsx.default)("tremolo-knob", className),
|
|
571
495
|
tabIndex: 0,
|
|
@@ -581,10 +505,7 @@ const Knob = {
|
|
|
581
505
|
height: size,
|
|
582
506
|
...style
|
|
583
507
|
},
|
|
584
|
-
onPointerDown
|
|
585
|
-
pointerDownHandler(event);
|
|
586
|
-
onPointerDown?.(event);
|
|
587
|
-
},
|
|
508
|
+
onPointerDown,
|
|
588
509
|
onDoubleClick: (event) => {
|
|
589
510
|
if (enableDoubleClickDefault && onChange) onChange(defaultValue);
|
|
590
511
|
onDoubleClick?.(event);
|
|
@@ -721,6 +642,27 @@ function useNumberInputContext(selector) {
|
|
|
721
642
|
return (0, zustand.useStore)(store, selector);
|
|
722
643
|
}
|
|
723
644
|
//#endregion
|
|
645
|
+
//#region src/hooks/useEventListener.ts
|
|
646
|
+
function useEventListener(target, event, handler, options) {
|
|
647
|
+
const listener = useCallbackRef(handler);
|
|
648
|
+
(0, react.useEffect)(() => {
|
|
649
|
+
const node = typeof target === "function" ? target() : target ?? document;
|
|
650
|
+
if (!node) return;
|
|
651
|
+
node.addEventListener(event, listener, options);
|
|
652
|
+
return () => {
|
|
653
|
+
node.removeEventListener(event, listener, options);
|
|
654
|
+
};
|
|
655
|
+
}, [
|
|
656
|
+
event,
|
|
657
|
+
target,
|
|
658
|
+
options,
|
|
659
|
+
listener
|
|
660
|
+
]);
|
|
661
|
+
return () => {
|
|
662
|
+
(typeof target === "function" ? target() : target ?? document)?.removeEventListener(event, listener, options);
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
//#endregion
|
|
724
666
|
//#region src/hooks/useInterval.ts
|
|
725
667
|
function useInterval(callback, delay) {
|
|
726
668
|
const savedCallback = (0, react.useRef)(callback);
|
|
@@ -866,19 +808,14 @@ const InternalInput = (0, react.forwardRef)(({ disabled, readonly, selectWithFoc
|
|
|
866
808
|
onChange,
|
|
867
809
|
updateValueByEvent
|
|
868
810
|
]);
|
|
869
|
-
const wheelRefCallback =
|
|
811
|
+
const wheelRefCallback = useWheel((event) => {
|
|
870
812
|
if (!wheel || readonly) return;
|
|
871
813
|
event.preventDefault();
|
|
872
814
|
if (!onChange || event.deltaY == 0) return;
|
|
873
815
|
const x = Math.sign(event.deltaY) * -wheel[1];
|
|
874
816
|
const parsed = parseValue(String(updateValueByEvent(wheel[0], x)));
|
|
875
817
|
onChange?.(parsed.rawValue, parsed.formatValue);
|
|
876
|
-
}
|
|
877
|
-
wheel,
|
|
878
|
-
readonly,
|
|
879
|
-
onChange,
|
|
880
|
-
updateValueByEvent
|
|
881
|
-
]);
|
|
818
|
+
});
|
|
882
819
|
(0, react.useImperativeHandle)(ref, () => {
|
|
883
820
|
return {
|
|
884
821
|
focus() {
|
|
@@ -996,6 +933,23 @@ const NumberInput = {
|
|
|
996
933
|
DecrementStepper
|
|
997
934
|
};
|
|
998
935
|
//#endregion
|
|
936
|
+
//#region src/hooks/useRefCallbackEvent.ts
|
|
937
|
+
function useRefCallbackEvent(event, handler, options, deps = []) {
|
|
938
|
+
return (0, react.useMemo)(() => {
|
|
939
|
+
let cleanup;
|
|
940
|
+
return (node) => {
|
|
941
|
+
if (!node) {
|
|
942
|
+
cleanup?.();
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
node.addEventListener(event, handler, options);
|
|
946
|
+
cleanup = () => {
|
|
947
|
+
node.removeEventListener(event, handler, options);
|
|
948
|
+
};
|
|
949
|
+
};
|
|
950
|
+
}, deps);
|
|
951
|
+
}
|
|
952
|
+
//#endregion
|
|
999
953
|
//#region src/hooks/usePianoDrag.ts
|
|
1000
954
|
/**
|
|
1001
955
|
* Internal
|
|
@@ -1452,42 +1406,57 @@ function Container({ className, children, ...props }) {
|
|
|
1452
1406
|
}
|
|
1453
1407
|
//#endregion
|
|
1454
1408
|
//#region src/hooks/useDragWithElement.ts
|
|
1455
|
-
|
|
1409
|
+
/**
|
|
1410
|
+
* Track a pointer drag, reporting the position normalized against
|
|
1411
|
+
* the bounding rect of `baseElementRef`.
|
|
1412
|
+
*
|
|
1413
|
+
* @returns a ref callback for the element that starts the drag, and whether a drag is in progress
|
|
1414
|
+
*/
|
|
1415
|
+
function useDragWithElement({ baseElementRef, updateOnPointerDown = false, cursor, onDrag, onDragStart, onDragEnd }) {
|
|
1456
1416
|
const [dragging, setDragging] = (0, react.useState)(false);
|
|
1457
1417
|
const normalizedX = (0, react.useRef)(0);
|
|
1458
1418
|
const normalizedY = (0, react.useRef)(0);
|
|
1459
|
-
const
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
const
|
|
1464
|
-
|
|
1465
|
-
const
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1419
|
+
const dragHandler = useCallbackRef(onDrag);
|
|
1420
|
+
const dragStartHandler = useCallbackRef(onDragStart);
|
|
1421
|
+
const dragEndHandler = useCallbackRef(onDragEnd);
|
|
1422
|
+
const update = (0, react.useCallback)((clientX, clientY) => {
|
|
1423
|
+
const base = baseElementRef.current;
|
|
1424
|
+
if (!base) return false;
|
|
1425
|
+
const { left, top, right, bottom } = base.getBoundingClientRect();
|
|
1426
|
+
normalizedX.current = (0, _tremolo_ui_functions.normalizeValue)(clientX, left, right);
|
|
1427
|
+
normalizedY.current = (0, _tremolo_ui_functions.normalizeValue)(clientY, top, bottom);
|
|
1428
|
+
return true;
|
|
1429
|
+
}, [baseElementRef]);
|
|
1430
|
+
const [node, setNode] = (0, react.useState)(null);
|
|
1431
|
+
(0, react.useEffect)(() => {
|
|
1432
|
+
if (!node) return;
|
|
1433
|
+
const instance = (0, _tremolo_ui_dom.createDrag)(node, {
|
|
1434
|
+
cursor,
|
|
1435
|
+
onDragStart: ({ clientX, clientY }) => {
|
|
1436
|
+
setDragging(true);
|
|
1437
|
+
if (update(clientX, clientY) && updateOnPointerDown) dragHandler(normalizedX.current, normalizedY.current);
|
|
1438
|
+
dragStartHandler(normalizedX.current, normalizedY.current);
|
|
1439
|
+
},
|
|
1440
|
+
onDrag: ({ clientX, clientY }) => {
|
|
1441
|
+
if (update(clientX, clientY)) dragHandler(normalizedX.current, normalizedY.current);
|
|
1442
|
+
},
|
|
1443
|
+
onDragEnd: () => {
|
|
1444
|
+
setDragging(false);
|
|
1445
|
+
dragEndHandler(normalizedX.current, normalizedY.current);
|
|
1446
|
+
}
|
|
1447
|
+
});
|
|
1448
|
+
return () => instance.destroy();
|
|
1471
1449
|
}, [
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1450
|
+
node,
|
|
1451
|
+
update,
|
|
1452
|
+
updateOnPointerDown,
|
|
1453
|
+
cursor,
|
|
1454
|
+
dragHandler,
|
|
1455
|
+
dragStartHandler,
|
|
1456
|
+
dragEndHandler
|
|
1475
1457
|
]);
|
|
1476
|
-
const refHandler = useRefCallbackEvent("touchmove", handleDrag, { passive: false }, [onDrag]);
|
|
1477
|
-
const pointerDownHandler = (0, react.useCallback)((event) => {
|
|
1478
|
-
setDragging(true);
|
|
1479
|
-
handleDrag(event);
|
|
1480
|
-
onDragStart?.(normalizedX.current, normalizedY.current);
|
|
1481
|
-
}, [handleDrag, onDragStart]);
|
|
1482
|
-
useEventListener(globalThis.window, "pointermove", handleDrag);
|
|
1483
|
-
useEventListener(globalThis.window, "pointerup", () => {
|
|
1484
|
-
if (!dragging) return;
|
|
1485
|
-
setDragging(false);
|
|
1486
|
-
onDragEnd?.(normalizedX.current, normalizedY.current);
|
|
1487
|
-
});
|
|
1488
1458
|
return {
|
|
1489
|
-
|
|
1490
|
-
pointerDownHandler,
|
|
1459
|
+
refCallback: setNode,
|
|
1491
1460
|
dragging
|
|
1492
1461
|
};
|
|
1493
1462
|
}
|
|
@@ -1505,8 +1474,9 @@ function Point({ value, min, max, size, width = 16, height = 16, color, disabled
|
|
|
1505
1474
|
const externalStyles = usePointsEditorContext((s) => s.externalStyles);
|
|
1506
1475
|
const __disabled = usePointsEditorContext((s) => s.disabled);
|
|
1507
1476
|
const __readonly = usePointsEditorContext((s) => s.readonly);
|
|
1508
|
-
const {
|
|
1477
|
+
const { refCallback: dragRefCallback, dragging } = useDragWithElement({
|
|
1509
1478
|
baseElementRef: containerElementRef,
|
|
1479
|
+
cursor: readonly ? void 0 : externalStyles.cursor,
|
|
1510
1480
|
onDrag: (x, y) => {
|
|
1511
1481
|
if (readonly) return;
|
|
1512
1482
|
onChange?.(clampPoint({
|
|
@@ -1517,7 +1487,6 @@ function Point({ value, min, max, size, width = 16, height = 16, color, disabled
|
|
|
1517
1487
|
onDragStart: (x, y) => {
|
|
1518
1488
|
if (readonly) return;
|
|
1519
1489
|
if (externalStyles.userSelectNone) addUserSelectNone();
|
|
1520
|
-
if (externalStyles.cursor) setCursorStyle(externalStyles.cursor);
|
|
1521
1490
|
onDragStart?.(clampPoint({
|
|
1522
1491
|
x,
|
|
1523
1492
|
y
|
|
@@ -1526,7 +1495,6 @@ function Point({ value, min, max, size, width = 16, height = 16, color, disabled
|
|
|
1526
1495
|
onDragEnd: (x, y) => {
|
|
1527
1496
|
if (readonly) return;
|
|
1528
1497
|
if (externalStyles.userSelectNone) removeUserSelectNone();
|
|
1529
|
-
if (externalStyles.cursor) resetCursorStyle();
|
|
1530
1498
|
onDragEnd?.(clampPoint({
|
|
1531
1499
|
x,
|
|
1532
1500
|
y
|
|
@@ -1535,9 +1503,7 @@ function Point({ value, min, max, size, width = 16, height = 16, color, disabled
|
|
|
1535
1503
|
});
|
|
1536
1504
|
const colors = { "--color": color };
|
|
1537
1505
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1538
|
-
ref:
|
|
1539
|
-
touchMoveRefCallback(div);
|
|
1540
|
-
},
|
|
1506
|
+
ref: dragRefCallback,
|
|
1541
1507
|
className: (0, clsx.default)("tremolo-points-editor-point", className),
|
|
1542
1508
|
tabIndex: 0,
|
|
1543
1509
|
"aria-disabled": disabled != void 0 ? disabled : __disabled,
|
|
@@ -1551,10 +1517,7 @@ function Point({ value, min, max, size, width = 16, height = 16, color, disabled
|
|
|
1551
1517
|
top: `${value.y * 100}%`,
|
|
1552
1518
|
...style
|
|
1553
1519
|
},
|
|
1554
|
-
onPointerDown
|
|
1555
|
-
pointerDownHandler(event);
|
|
1556
|
-
onPointerDown?.(event);
|
|
1557
|
-
},
|
|
1520
|
+
onPointerDown,
|
|
1558
1521
|
...props
|
|
1559
1522
|
});
|
|
1560
1523
|
}
|
|
@@ -1855,24 +1818,24 @@ const Slider = {
|
|
|
1855
1818
|
reverse,
|
|
1856
1819
|
updateValueByEvent
|
|
1857
1820
|
]);
|
|
1858
|
-
const {
|
|
1821
|
+
const { refCallback: dragRefCallback } = useDragWithElement({
|
|
1859
1822
|
baseElementRef: trackElementRef,
|
|
1823
|
+
cursor: readonly ? void 0 : externalStyles.cursor,
|
|
1824
|
+
updateOnPointerDown: true,
|
|
1860
1825
|
onDrag,
|
|
1861
1826
|
onDragStart: (nx, ny) => {
|
|
1862
1827
|
if (readonly) return;
|
|
1863
1828
|
if (externalStyles.userSelectNone) addUserSelectNone();
|
|
1864
|
-
if (externalStyles.cursor) setCursorStyle(externalStyles.cursor);
|
|
1865
1829
|
thumbRef.current?.focus();
|
|
1866
1830
|
onDragStart?.((0, _tremolo_ui_functions.clamp)((0, _tremolo_ui_functions.stepValue)((0, _tremolo_ui_functions.rawValue)(vertical ? ny : nx, min, max, skew), step), min, max));
|
|
1867
1831
|
},
|
|
1868
1832
|
onDragEnd: (nx, ny) => {
|
|
1869
1833
|
if (readonly) return;
|
|
1870
1834
|
if (externalStyles.userSelectNone) removeUserSelectNone();
|
|
1871
|
-
if (externalStyles.cursor) resetCursorStyle();
|
|
1872
1835
|
onDragEnd?.((0, _tremolo_ui_functions.clamp)((0, _tremolo_ui_functions.stepValue)((0, _tremolo_ui_functions.rawValue)(vertical ? ny : nx, min, max, skew), step), min, max));
|
|
1873
1836
|
}
|
|
1874
1837
|
});
|
|
1875
|
-
const wheelRefCallback =
|
|
1838
|
+
const wheelRefCallback = useWheel((event) => {
|
|
1876
1839
|
if (!wheel || !onChange || readonly) return;
|
|
1877
1840
|
event.preventDefault();
|
|
1878
1841
|
let x;
|
|
@@ -1883,13 +1846,7 @@ const Slider = {
|
|
|
1883
1846
|
}
|
|
1884
1847
|
if (vertical && reverse) x *= -1;
|
|
1885
1848
|
onChange(updateValueByEvent(wheel[0], x));
|
|
1886
|
-
}
|
|
1887
|
-
wheel,
|
|
1888
|
-
vertical,
|
|
1889
|
-
readonly,
|
|
1890
|
-
onChange,
|
|
1891
|
-
updateValueByEvent
|
|
1892
|
-
]);
|
|
1849
|
+
});
|
|
1893
1850
|
(0, react.useImperativeHandle)(forwardedRef, () => {
|
|
1894
1851
|
return {
|
|
1895
1852
|
focus() {
|
|
@@ -1913,7 +1870,7 @@ const Slider = {
|
|
|
1913
1870
|
className: (0, clsx.default)("tremolo-slider", className),
|
|
1914
1871
|
ref: (div) => {
|
|
1915
1872
|
wheelRefCallback(div);
|
|
1916
|
-
|
|
1873
|
+
dragRefCallback(div);
|
|
1917
1874
|
},
|
|
1918
1875
|
tabIndex: -1,
|
|
1919
1876
|
role: "slider",
|
|
@@ -1927,10 +1884,7 @@ const Slider = {
|
|
|
1927
1884
|
margin: `calc(${(0, _tremolo_ui_functions.styleHelper)(thumbProps?.size ?? 22)} / 2)`,
|
|
1928
1885
|
...style
|
|
1929
1886
|
},
|
|
1930
|
-
onPointerDown
|
|
1931
|
-
pointerDownHandler(event);
|
|
1932
|
-
onPointerDown?.(event);
|
|
1933
|
-
},
|
|
1887
|
+
onPointerDown,
|
|
1934
1888
|
onKeyDown: (event) => {
|
|
1935
1889
|
handleKeyDown(event);
|
|
1936
1890
|
onKeyDown?.(event);
|
|
@@ -2005,29 +1959,6 @@ function composeRefs(...refs) {
|
|
|
2005
1959
|
};
|
|
2006
1960
|
}
|
|
2007
1961
|
//#endregion
|
|
2008
|
-
//#region src/components/WheelObserver/index.tsx
|
|
2009
|
-
/**
|
|
2010
|
-
* @example
|
|
2011
|
-
* <WheelObserver
|
|
2012
|
-
* onWheel={(event) => {
|
|
2013
|
-
* event.preventDefault()
|
|
2014
|
-
* console.log(event.deltaY)
|
|
2015
|
-
* }
|
|
2016
|
-
* >
|
|
2017
|
-
* <div>Wheel here</div>
|
|
2018
|
-
* </WheelObserver>
|
|
2019
|
-
*/
|
|
2020
|
-
function WheelObserver(props) {
|
|
2021
|
-
const { as: Component = "div", children, onWheel, ref, ...attributes } = props;
|
|
2022
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, {
|
|
2023
|
-
ref: composeRefs(ref, useRefCallbackEvent("wheel", (event) => {
|
|
2024
|
-
if (onWheel) onWheel(event);
|
|
2025
|
-
}, { passive: false }, [onWheel])),
|
|
2026
|
-
...attributes,
|
|
2027
|
-
children
|
|
2028
|
-
});
|
|
2029
|
-
}
|
|
2030
|
-
//#endregion
|
|
2031
1962
|
//#region src/components/XYPad/Area.tsx
|
|
2032
1963
|
function Area({ width = 120, height = 120, color, children, className, style, __thumb, ...props }) {
|
|
2033
1964
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -2179,13 +2110,14 @@ const XYPad = {
|
|
|
2179
2110
|
y,
|
|
2180
2111
|
updateValueByEvent
|
|
2181
2112
|
]);
|
|
2182
|
-
const {
|
|
2113
|
+
const { refCallback: dragRefCallback } = useDragWithElement({
|
|
2183
2114
|
baseElementRef: areaElementRef,
|
|
2115
|
+
cursor: readonly ? void 0 : externalStyles.cursor,
|
|
2116
|
+
updateOnPointerDown: true,
|
|
2184
2117
|
onDrag,
|
|
2185
2118
|
onDragStart: (nx, ny) => {
|
|
2186
2119
|
if (readonly) return;
|
|
2187
2120
|
if (externalStyles.userSelectNone) addUserSelectNone();
|
|
2188
|
-
if (externalStyles.cursor) setCursorStyle(externalStyles.cursor);
|
|
2189
2121
|
thumbRef.current?.focus();
|
|
2190
2122
|
const valueX = (0, _tremolo_ui_functions.clamp)((0, _tremolo_ui_functions.stepValue)((0, _tremolo_ui_functions.rawValue)(nx, x.min, x.max, x.skew), x.step), x.min, x.max);
|
|
2191
2123
|
const valueY = (0, _tremolo_ui_functions.clamp)((0, _tremolo_ui_functions.stepValue)((0, _tremolo_ui_functions.rawValue)(ny, y.min, y.max, y.skew), y.step), y.min, y.max);
|
|
@@ -2194,13 +2126,12 @@ const XYPad = {
|
|
|
2194
2126
|
onDragEnd: (nx, ny) => {
|
|
2195
2127
|
if (readonly) return;
|
|
2196
2128
|
if (externalStyles.userSelectNone) removeUserSelectNone();
|
|
2197
|
-
if (externalStyles.cursor) resetCursorStyle();
|
|
2198
2129
|
const valueX = (0, _tremolo_ui_functions.clamp)((0, _tremolo_ui_functions.stepValue)((0, _tremolo_ui_functions.rawValue)(nx, x.min, x.max, x.skew), x.step), x.min, x.max);
|
|
2199
2130
|
const valueY = (0, _tremolo_ui_functions.clamp)((0, _tremolo_ui_functions.stepValue)((0, _tremolo_ui_functions.rawValue)(ny, y.min, y.max, y.skew), y.step), y.min, y.max);
|
|
2200
2131
|
onDragEnd?.(valueX, valueY);
|
|
2201
2132
|
}
|
|
2202
2133
|
});
|
|
2203
|
-
const wheelRefCallback =
|
|
2134
|
+
const wheelRefCallback = useWheel((event) => {
|
|
2204
2135
|
if (!onChange || readonly) return;
|
|
2205
2136
|
const target = event.shiftKey ? x : y;
|
|
2206
2137
|
if (!target.wheel) return;
|
|
@@ -2211,13 +2142,7 @@ const XYPad = {
|
|
|
2211
2142
|
const newValue = updateValueByEvent(target.wheel[0], target, delta);
|
|
2212
2143
|
if (event.shiftKey) onChange(newValue, y.value);
|
|
2213
2144
|
else onChange(x.value, newValue);
|
|
2214
|
-
}
|
|
2215
|
-
x,
|
|
2216
|
-
y,
|
|
2217
|
-
onChange,
|
|
2218
|
-
readonly,
|
|
2219
|
-
updateValueByEvent
|
|
2220
|
-
]);
|
|
2145
|
+
});
|
|
2221
2146
|
(0, react.useImperativeHandle)(forwardedRef, () => {
|
|
2222
2147
|
return {
|
|
2223
2148
|
focus() {
|
|
@@ -2231,7 +2156,7 @@ const XYPad = {
|
|
|
2231
2156
|
}, []);
|
|
2232
2157
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2233
2158
|
className: (0, clsx.default)("tremolo-xy-pad", className),
|
|
2234
|
-
ref: composeRefs(rootRef, wheelRefCallback,
|
|
2159
|
+
ref: composeRefs(rootRef, wheelRefCallback, dragRefCallback),
|
|
2235
2160
|
tabIndex: -1,
|
|
2236
2161
|
"aria-disabled": disabled,
|
|
2237
2162
|
"aria-readonly": readonly,
|
|
@@ -2241,10 +2166,7 @@ const XYPad = {
|
|
|
2241
2166
|
WebkitTapHighlightColor: "transparent",
|
|
2242
2167
|
...style
|
|
2243
2168
|
},
|
|
2244
|
-
onPointerDown
|
|
2245
|
-
pointerDownHandler(event);
|
|
2246
|
-
onPointerDown?.(event);
|
|
2247
|
-
},
|
|
2169
|
+
onPointerDown,
|
|
2248
2170
|
onKeyDown: (event) => {
|
|
2249
2171
|
handleKeyDown(event);
|
|
2250
2172
|
onKeyDown?.(event);
|
|
@@ -2295,83 +2217,75 @@ function useAnimationFrame(callback = () => {}, deps = []) {
|
|
|
2295
2217
|
}
|
|
2296
2218
|
//#endregion
|
|
2297
2219
|
//#region src/hooks/useMIDIAccess.ts
|
|
2298
|
-
/** @private */
|
|
2299
|
-
const PERMISSION_DENIED = "PERMISSION_DENIED";
|
|
2300
|
-
/** @private */
|
|
2301
|
-
const NOT_SUPPORTED = "NOT_SUPPORTED";
|
|
2302
2220
|
/**
|
|
2303
2221
|
* Hooks for requesting MIDI access in the browser. The first argument allows you to choose whether to request access on mount.
|
|
2304
2222
|
*/
|
|
2305
2223
|
function useMIDIAccess(requestOnMount = true) {
|
|
2306
|
-
const
|
|
2307
|
-
const
|
|
2308
|
-
const request = (0, react.useCallback)(() => {
|
|
2309
|
-
if (navigator.requestMIDIAccess) navigator.requestMIDIAccess().then((access) => {
|
|
2310
|
-
setMidiAccess(access);
|
|
2311
|
-
setError(null);
|
|
2312
|
-
}).catch(() => {
|
|
2313
|
-
setError(PERMISSION_DENIED);
|
|
2314
|
-
});
|
|
2315
|
-
else setError(NOT_SUPPORTED);
|
|
2316
|
-
}, []);
|
|
2224
|
+
const instance = (0, react.useMemo)(() => (0, _tremolo_ui_dom.createMIDIAccess)(), []);
|
|
2225
|
+
const { midiAccess, error } = (0, react.useSyncExternalStore)(instance.subscribe, instance.getState, instance.getServerState);
|
|
2317
2226
|
(0, react.useEffect)(() => {
|
|
2318
|
-
if (requestOnMount) request();
|
|
2319
|
-
}, [
|
|
2227
|
+
if (requestOnMount) instance.request();
|
|
2228
|
+
}, [instance, requestOnMount]);
|
|
2229
|
+
(0, react.useEffect)(() => {
|
|
2230
|
+
return () => instance.destroy();
|
|
2231
|
+
}, [instance]);
|
|
2320
2232
|
return {
|
|
2321
|
-
request,
|
|
2233
|
+
request: instance.request,
|
|
2322
2234
|
midiAccess,
|
|
2323
2235
|
error
|
|
2324
2236
|
};
|
|
2325
2237
|
}
|
|
2326
2238
|
//#endregion
|
|
2327
|
-
//#region src/hooks/useMIDIMessage.ts
|
|
2328
|
-
/**
|
|
2329
|
-
* Hooks for when you want to process MIDI events in more detail than useMIDIInput.
|
|
2330
|
-
*/
|
|
2331
|
-
function useMIDIMessage(midiAccess, onMIDIMessage) {
|
|
2332
|
-
(0, react.useEffect)(() => {
|
|
2333
|
-
if (!midiAccess) return;
|
|
2334
|
-
for (const input of midiAccess.inputs.values()) input.addEventListener("midimessage", onMIDIMessage);
|
|
2335
|
-
return () => {
|
|
2336
|
-
for (const input of midiAccess.inputs.values()) input.removeEventListener("midimessage", onMIDIMessage);
|
|
2337
|
-
};
|
|
2338
|
-
}, [midiAccess, onMIDIMessage]);
|
|
2339
|
-
}
|
|
2340
|
-
//#endregion
|
|
2341
2239
|
//#region src/hooks/useMIDIInput.ts
|
|
2342
|
-
const MIDI_EVENT_TO_NUMBER = {
|
|
2343
|
-
NOTE_ON: 144,
|
|
2344
|
-
NOTE_OFF: 128,
|
|
2345
|
-
PITCH_BEND: 224
|
|
2346
|
-
};
|
|
2347
2240
|
/**
|
|
2348
2241
|
* Hooks for handling note on/off events. To be used with useMIDIAccess. Internally uses useMIDIMessage.
|
|
2349
2242
|
*/
|
|
2350
2243
|
function useMIDIInput(midiAccess, onNoteOnEvent, onNoteOffEvent, onPitchBendEvent) {
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2244
|
+
(0, react.useEffect)(() => {
|
|
2245
|
+
const instance = (0, _tremolo_ui_dom.createMIDIInput)(midiAccess, {
|
|
2246
|
+
onNoteOnEvent,
|
|
2247
|
+
onNoteOffEvent,
|
|
2248
|
+
onPitchBendEvent
|
|
2249
|
+
});
|
|
2250
|
+
return () => instance.destroy();
|
|
2357
2251
|
}, [
|
|
2358
|
-
|
|
2252
|
+
midiAccess,
|
|
2359
2253
|
onNoteOnEvent,
|
|
2254
|
+
onNoteOffEvent,
|
|
2360
2255
|
onPitchBendEvent
|
|
2361
|
-
])
|
|
2256
|
+
]);
|
|
2257
|
+
}
|
|
2258
|
+
//#endregion
|
|
2259
|
+
//#region src/hooks/useMIDIMessage.ts
|
|
2260
|
+
/**
|
|
2261
|
+
* Hooks for when you want to process MIDI events in more detail than useMIDIInput.
|
|
2262
|
+
*/
|
|
2263
|
+
function useMIDIMessage(midiAccess, onMIDIMessage) {
|
|
2264
|
+
(0, react.useEffect)(() => {
|
|
2265
|
+
const instance = (0, _tremolo_ui_dom.createMIDIMessage)(midiAccess, onMIDIMessage);
|
|
2266
|
+
return () => instance.destroy();
|
|
2267
|
+
}, [midiAccess, onMIDIMessage]);
|
|
2362
2268
|
}
|
|
2363
2269
|
//#endregion
|
|
2364
2270
|
exports.AnimationCanvas = AnimationCanvas;
|
|
2365
|
-
exports.DragObserver = DragObserver;
|
|
2366
2271
|
exports.Knob = Knob;
|
|
2367
|
-
exports
|
|
2272
|
+
Object.defineProperty(exports, "NOT_SUPPORTED", {
|
|
2273
|
+
enumerable: true,
|
|
2274
|
+
get: function() {
|
|
2275
|
+
return _tremolo_ui_dom.NOT_SUPPORTED;
|
|
2276
|
+
}
|
|
2277
|
+
});
|
|
2368
2278
|
exports.NumberInput = NumberInput;
|
|
2369
|
-
exports
|
|
2279
|
+
Object.defineProperty(exports, "PERMISSION_DENIED", {
|
|
2280
|
+
enumerable: true,
|
|
2281
|
+
get: function() {
|
|
2282
|
+
return _tremolo_ui_dom.PERMISSION_DENIED;
|
|
2283
|
+
}
|
|
2284
|
+
});
|
|
2370
2285
|
exports.Piano = Piano;
|
|
2371
2286
|
exports.PointsEditor = PointsEditor;
|
|
2372
2287
|
exports.SHORTCUTS = SHORTCUTS;
|
|
2373
2288
|
exports.Slider = Slider;
|
|
2374
|
-
exports.WheelObserver = WheelObserver;
|
|
2375
2289
|
exports.XYPad = XYPad;
|
|
2376
2290
|
exports.clampPoint = clampPoint;
|
|
2377
2291
|
exports.getNoteRangeArray = getNoteRangeArray;
|
|
@@ -2385,5 +2299,6 @@ exports.useMIDIAccess = useMIDIAccess;
|
|
|
2385
2299
|
exports.useMIDIInput = useMIDIInput;
|
|
2386
2300
|
exports.useMIDIMessage = useMIDIMessage;
|
|
2387
2301
|
exports.useSliderContext = useSliderContext;
|
|
2302
|
+
exports.useWheel = useWheel;
|
|
2388
2303
|
|
|
2389
2304
|
//# sourceMappingURL=index.cjs.map
|