@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.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import clsx from "clsx";
|
|
2
|
-
import React, { createContext, createRef, forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useInsertionEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import React, { createContext, createRef, forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useInsertionEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
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
|
+
import { NOT_SUPPORTED, PERMISSION_DENIED, createDrag, createMIDIAccess, createMIDIInput, createMIDIMessage, createWheel } from "@tremolo-ui/dom";
|
|
5
6
|
import { createStore, useStore } from "zustand";
|
|
6
7
|
//#region src/components/AnimationCanvas/canvas.ts
|
|
7
8
|
const drawingState = [
|
|
@@ -154,110 +155,55 @@ function useCallbackRef(callback, deps = []) {
|
|
|
154
155
|
return useCallback((...args) => callbackRef.current?.(...args), deps);
|
|
155
156
|
}
|
|
156
157
|
//#endregion
|
|
157
|
-
//#region src/hooks/
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
//#region src/hooks/useDrag.ts
|
|
159
|
+
/**
|
|
160
|
+
* Track a pointer drag on an element.
|
|
161
|
+
*
|
|
162
|
+
* @returns a ref callback to attach to the element being dragged
|
|
163
|
+
*/
|
|
164
|
+
function useDrag({ threshold = 1, cursor, onDrag, onDragStart, onDragEnd }) {
|
|
165
|
+
const dragHandler = useCallbackRef(onDrag);
|
|
166
|
+
const dragStartHandler = useCallbackRef(onDragStart);
|
|
167
|
+
const dragEndHandler = useCallbackRef(onDragEnd);
|
|
168
|
+
const [node, setNode] = useState(null);
|
|
160
169
|
useEffect(() => {
|
|
161
|
-
const node = typeof target === "function" ? target() : target ?? document;
|
|
162
170
|
if (!node) return;
|
|
163
|
-
node
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
171
|
+
const instance = createDrag(node, {
|
|
172
|
+
threshold,
|
|
173
|
+
cursor,
|
|
174
|
+
onDragStart: () => dragStartHandler(),
|
|
175
|
+
onDrag: ({ x, y, deltaX, deltaY }) => dragHandler(x, y, deltaX, deltaY),
|
|
176
|
+
onDragEnd: () => dragEndHandler()
|
|
177
|
+
});
|
|
178
|
+
return () => instance.destroy();
|
|
167
179
|
}, [
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
180
|
+
node,
|
|
181
|
+
threshold,
|
|
182
|
+
cursor,
|
|
183
|
+
dragHandler,
|
|
184
|
+
dragStartHandler,
|
|
185
|
+
dragEndHandler
|
|
172
186
|
]);
|
|
173
|
-
return
|
|
174
|
-
(typeof target === "function" ? target() : target ?? document)?.removeEventListener(event, listener, options);
|
|
175
|
-
};
|
|
187
|
+
return setNode;
|
|
176
188
|
}
|
|
177
189
|
//#endregion
|
|
178
|
-
//#region src/hooks/
|
|
179
|
-
function useRefCallbackEvent(event, handler, options, deps = []) {
|
|
180
|
-
return useMemo(() => {
|
|
181
|
-
let cleanup;
|
|
182
|
-
return (node) => {
|
|
183
|
-
if (!node) {
|
|
184
|
-
cleanup?.();
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
node.addEventListener(event, handler, options);
|
|
188
|
-
cleanup = () => {
|
|
189
|
-
node.removeEventListener(event, handler, options);
|
|
190
|
-
};
|
|
191
|
-
};
|
|
192
|
-
}, deps);
|
|
193
|
-
}
|
|
194
|
-
//#endregion
|
|
195
|
-
//#region src/hooks/useDrag.ts
|
|
190
|
+
//#region src/hooks/useWheel.ts
|
|
196
191
|
/**
|
|
197
|
-
*
|
|
192
|
+
* Listen to wheel events on an element.
|
|
193
|
+
*
|
|
194
|
+
* The listener is not passive, so the handler may call `preventDefault()`.
|
|
195
|
+
*
|
|
196
|
+
* @returns a ref callback to attach to the element
|
|
198
197
|
*/
|
|
199
|
-
function
|
|
200
|
-
const
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const screenX = isTouch ? event.touches[0].screenX : event.screenX;
|
|
209
|
-
const screenY = isTouch ? event.touches[0].screenY : event.screenY;
|
|
210
|
-
let deltaX = 0;
|
|
211
|
-
let deltaY = 0;
|
|
212
|
-
if (first) {
|
|
213
|
-
dragStartX.current = screenX;
|
|
214
|
-
dragStartY.current = screenY;
|
|
215
|
-
}
|
|
216
|
-
if (dragOffsetX.current) {
|
|
217
|
-
deltaX = screenX - dragOffsetX.current;
|
|
218
|
-
dragOffsetX.current = screenX;
|
|
219
|
-
}
|
|
220
|
-
if (dragOffsetY.current) {
|
|
221
|
-
deltaY = screenY - dragOffsetY.current;
|
|
222
|
-
dragOffsetY.current = screenY;
|
|
223
|
-
}
|
|
224
|
-
if (Math.abs(deltaX) < threshold && Math.abs(deltaY) < threshold) return;
|
|
225
|
-
onDrag?.(screenX - dragStartX.current, screenY - dragStartY.current, deltaX, deltaY);
|
|
226
|
-
}, [threshold, onDrag]);
|
|
227
|
-
const pointerDownHandler = useCallback((event) => {
|
|
228
|
-
dragOffsetX.current = event.screenX;
|
|
229
|
-
dragOffsetY.current = event.screenY;
|
|
230
|
-
handleDrag(event, true);
|
|
231
|
-
onDragStart?.();
|
|
232
|
-
}, [handleDrag, onDragStart]);
|
|
233
|
-
const refHandler = useRefCallbackEvent("touchmove", handleDrag, { passive: false }, [onDrag]);
|
|
234
|
-
useEventListener(globalThis.window, "mousemove", handleDrag);
|
|
235
|
-
useEventListener(globalThis.window, "pointerup", () => {
|
|
236
|
-
dragOffsetX.current = void 0;
|
|
237
|
-
dragOffsetY.current = void 0;
|
|
238
|
-
onDragEnd?.();
|
|
239
|
-
});
|
|
240
|
-
return [refHandler, pointerDownHandler];
|
|
241
|
-
}
|
|
242
|
-
//#endregion
|
|
243
|
-
//#region src/components/DragObserver/index.tsx
|
|
244
|
-
function DragObserver(props) {
|
|
245
|
-
const { as: Component = "div", threshold = 1, onDrag, onDragStart, onDragEnd, children, onPointerDown, ...attributes } = props;
|
|
246
|
-
const [refCallback, pointerDownHandler] = useDrag({
|
|
247
|
-
threshold,
|
|
248
|
-
onDrag,
|
|
249
|
-
onDragStart,
|
|
250
|
-
onDragEnd
|
|
251
|
-
});
|
|
252
|
-
return /* @__PURE__ */ jsx(Component, {
|
|
253
|
-
ref: refCallback,
|
|
254
|
-
onPointerDown: (e) => {
|
|
255
|
-
pointerDownHandler(e);
|
|
256
|
-
onPointerDown?.(e);
|
|
257
|
-
},
|
|
258
|
-
...attributes,
|
|
259
|
-
children
|
|
260
|
-
});
|
|
198
|
+
function useWheel(onWheel) {
|
|
199
|
+
const wheelHandler = useCallbackRef(onWheel);
|
|
200
|
+
const [node, setNode] = useState(null);
|
|
201
|
+
useEffect(() => {
|
|
202
|
+
if (!node) return;
|
|
203
|
+
const instance = createWheel(node, (event) => wheelHandler(event));
|
|
204
|
+
return () => instance.destroy();
|
|
205
|
+
}, [node, wheelHandler]);
|
|
206
|
+
return setNode;
|
|
261
207
|
}
|
|
262
208
|
//#endregion
|
|
263
209
|
//#region src/components/_util/index.ts
|
|
@@ -267,22 +213,6 @@ function addUserSelectNone() {
|
|
|
267
213
|
function removeUserSelectNone() {
|
|
268
214
|
window.document.body.classList.remove("tremolo-user-select-none");
|
|
269
215
|
}
|
|
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);
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
216
|
function calcDrawingState({ value, min, max, skew, startValue, angleRange }) {
|
|
287
217
|
const p = normalizeValue(value, min, max, skew);
|
|
288
218
|
const s = normalizeValue(startValue, min, max, skew);
|
|
@@ -492,32 +422,26 @@ const Knob = {
|
|
|
492
422
|
readonly,
|
|
493
423
|
updateValueByEvent
|
|
494
424
|
]);
|
|
495
|
-
const
|
|
425
|
+
const dragRefCallback = useDrag({
|
|
426
|
+
cursor: readonly ? void 0 : externalStyles.cursor,
|
|
496
427
|
onDrag,
|
|
497
428
|
onDragStart: () => {
|
|
498
429
|
setDragging(true);
|
|
499
430
|
valueRef.current = normalizeValue(value, min, max, skew);
|
|
500
431
|
if (externalStyles.userSelectNone) addUserSelectNone();
|
|
501
|
-
if (externalStyles.cursor) setCursorStyle(externalStyles.cursor);
|
|
502
432
|
},
|
|
503
433
|
onDragEnd: () => {
|
|
504
434
|
setDragging(false);
|
|
505
435
|
if (externalStyles.userSelectNone) removeUserSelectNone();
|
|
506
|
-
if (externalStyles.cursor) resetCursorStyle();
|
|
507
436
|
}
|
|
508
437
|
});
|
|
509
|
-
const wheelRefCallback =
|
|
438
|
+
const wheelRefCallback = useWheel((event) => {
|
|
510
439
|
if (!wheel || readonly) return;
|
|
511
440
|
event.preventDefault();
|
|
512
441
|
if (!onChange || event.deltaY == 0) return;
|
|
513
442
|
const x = Math.sign(event.deltaY) * -wheel[1];
|
|
514
443
|
onChange(updateValueByEvent(wheel[0], x));
|
|
515
|
-
}
|
|
516
|
-
wheel,
|
|
517
|
-
onChange,
|
|
518
|
-
readonly,
|
|
519
|
-
updateValueByEvent
|
|
520
|
-
]);
|
|
444
|
+
});
|
|
521
445
|
useImperativeHandle(forwardedRef, () => {
|
|
522
446
|
return {
|
|
523
447
|
focus() {
|
|
@@ -540,7 +464,7 @@ const Knob = {
|
|
|
540
464
|
ref: (elm) => {
|
|
541
465
|
elmRef.current = elm;
|
|
542
466
|
wheelRefCallback(elm);
|
|
543
|
-
|
|
467
|
+
dragRefCallback(elm);
|
|
544
468
|
},
|
|
545
469
|
className: clsx("tremolo-knob", className),
|
|
546
470
|
tabIndex: 0,
|
|
@@ -556,10 +480,7 @@ const Knob = {
|
|
|
556
480
|
height: size,
|
|
557
481
|
...style
|
|
558
482
|
},
|
|
559
|
-
onPointerDown
|
|
560
|
-
pointerDownHandler(event);
|
|
561
|
-
onPointerDown?.(event);
|
|
562
|
-
},
|
|
483
|
+
onPointerDown,
|
|
563
484
|
onDoubleClick: (event) => {
|
|
564
485
|
if (enableDoubleClickDefault && onChange) onChange(defaultValue);
|
|
565
486
|
onDoubleClick?.(event);
|
|
@@ -696,6 +617,27 @@ function useNumberInputContext(selector) {
|
|
|
696
617
|
return useStore(store, selector);
|
|
697
618
|
}
|
|
698
619
|
//#endregion
|
|
620
|
+
//#region src/hooks/useEventListener.ts
|
|
621
|
+
function useEventListener(target, event, handler, options) {
|
|
622
|
+
const listener = useCallbackRef(handler);
|
|
623
|
+
useEffect(() => {
|
|
624
|
+
const node = typeof target === "function" ? target() : target ?? document;
|
|
625
|
+
if (!node) return;
|
|
626
|
+
node.addEventListener(event, listener, options);
|
|
627
|
+
return () => {
|
|
628
|
+
node.removeEventListener(event, listener, options);
|
|
629
|
+
};
|
|
630
|
+
}, [
|
|
631
|
+
event,
|
|
632
|
+
target,
|
|
633
|
+
options,
|
|
634
|
+
listener
|
|
635
|
+
]);
|
|
636
|
+
return () => {
|
|
637
|
+
(typeof target === "function" ? target() : target ?? document)?.removeEventListener(event, listener, options);
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
//#endregion
|
|
699
641
|
//#region src/hooks/useInterval.ts
|
|
700
642
|
function useInterval(callback, delay) {
|
|
701
643
|
const savedCallback = useRef(callback);
|
|
@@ -841,19 +783,14 @@ const InternalInput = forwardRef(({ disabled, readonly, selectWithFocus, blurOnE
|
|
|
841
783
|
onChange,
|
|
842
784
|
updateValueByEvent
|
|
843
785
|
]);
|
|
844
|
-
const wheelRefCallback =
|
|
786
|
+
const wheelRefCallback = useWheel((event) => {
|
|
845
787
|
if (!wheel || readonly) return;
|
|
846
788
|
event.preventDefault();
|
|
847
789
|
if (!onChange || event.deltaY == 0) return;
|
|
848
790
|
const x = Math.sign(event.deltaY) * -wheel[1];
|
|
849
791
|
const parsed = parseValue(String(updateValueByEvent(wheel[0], x)));
|
|
850
792
|
onChange?.(parsed.rawValue, parsed.formatValue);
|
|
851
|
-
}
|
|
852
|
-
wheel,
|
|
853
|
-
readonly,
|
|
854
|
-
onChange,
|
|
855
|
-
updateValueByEvent
|
|
856
|
-
]);
|
|
793
|
+
});
|
|
857
794
|
useImperativeHandle(ref, () => {
|
|
858
795
|
return {
|
|
859
796
|
focus() {
|
|
@@ -971,6 +908,23 @@ const NumberInput = {
|
|
|
971
908
|
DecrementStepper
|
|
972
909
|
};
|
|
973
910
|
//#endregion
|
|
911
|
+
//#region src/hooks/useRefCallbackEvent.ts
|
|
912
|
+
function useRefCallbackEvent(event, handler, options, deps = []) {
|
|
913
|
+
return useMemo(() => {
|
|
914
|
+
let cleanup;
|
|
915
|
+
return (node) => {
|
|
916
|
+
if (!node) {
|
|
917
|
+
cleanup?.();
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
node.addEventListener(event, handler, options);
|
|
921
|
+
cleanup = () => {
|
|
922
|
+
node.removeEventListener(event, handler, options);
|
|
923
|
+
};
|
|
924
|
+
};
|
|
925
|
+
}, deps);
|
|
926
|
+
}
|
|
927
|
+
//#endregion
|
|
974
928
|
//#region src/hooks/usePianoDrag.ts
|
|
975
929
|
/**
|
|
976
930
|
* Internal
|
|
@@ -1427,42 +1381,57 @@ function Container({ className, children, ...props }) {
|
|
|
1427
1381
|
}
|
|
1428
1382
|
//#endregion
|
|
1429
1383
|
//#region src/hooks/useDragWithElement.ts
|
|
1430
|
-
|
|
1384
|
+
/**
|
|
1385
|
+
* Track a pointer drag, reporting the position normalized against
|
|
1386
|
+
* the bounding rect of `baseElementRef`.
|
|
1387
|
+
*
|
|
1388
|
+
* @returns a ref callback for the element that starts the drag, and whether a drag is in progress
|
|
1389
|
+
*/
|
|
1390
|
+
function useDragWithElement({ baseElementRef, updateOnPointerDown = false, cursor, onDrag, onDragStart, onDragEnd }) {
|
|
1431
1391
|
const [dragging, setDragging] = useState(false);
|
|
1432
1392
|
const normalizedX = useRef(0);
|
|
1433
1393
|
const normalizedY = useRef(0);
|
|
1434
|
-
const
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
const
|
|
1439
|
-
|
|
1440
|
-
const
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1394
|
+
const dragHandler = useCallbackRef(onDrag);
|
|
1395
|
+
const dragStartHandler = useCallbackRef(onDragStart);
|
|
1396
|
+
const dragEndHandler = useCallbackRef(onDragEnd);
|
|
1397
|
+
const update = useCallback((clientX, clientY) => {
|
|
1398
|
+
const base = baseElementRef.current;
|
|
1399
|
+
if (!base) return false;
|
|
1400
|
+
const { left, top, right, bottom } = base.getBoundingClientRect();
|
|
1401
|
+
normalizedX.current = normalizeValue(clientX, left, right);
|
|
1402
|
+
normalizedY.current = normalizeValue(clientY, top, bottom);
|
|
1403
|
+
return true;
|
|
1404
|
+
}, [baseElementRef]);
|
|
1405
|
+
const [node, setNode] = useState(null);
|
|
1406
|
+
useEffect(() => {
|
|
1407
|
+
if (!node) return;
|
|
1408
|
+
const instance = createDrag(node, {
|
|
1409
|
+
cursor,
|
|
1410
|
+
onDragStart: ({ clientX, clientY }) => {
|
|
1411
|
+
setDragging(true);
|
|
1412
|
+
if (update(clientX, clientY) && updateOnPointerDown) dragHandler(normalizedX.current, normalizedY.current);
|
|
1413
|
+
dragStartHandler(normalizedX.current, normalizedY.current);
|
|
1414
|
+
},
|
|
1415
|
+
onDrag: ({ clientX, clientY }) => {
|
|
1416
|
+
if (update(clientX, clientY)) dragHandler(normalizedX.current, normalizedY.current);
|
|
1417
|
+
},
|
|
1418
|
+
onDragEnd: () => {
|
|
1419
|
+
setDragging(false);
|
|
1420
|
+
dragEndHandler(normalizedX.current, normalizedY.current);
|
|
1421
|
+
}
|
|
1422
|
+
});
|
|
1423
|
+
return () => instance.destroy();
|
|
1446
1424
|
}, [
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1425
|
+
node,
|
|
1426
|
+
update,
|
|
1427
|
+
updateOnPointerDown,
|
|
1428
|
+
cursor,
|
|
1429
|
+
dragHandler,
|
|
1430
|
+
dragStartHandler,
|
|
1431
|
+
dragEndHandler
|
|
1450
1432
|
]);
|
|
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);
|
|
1462
|
-
});
|
|
1463
1433
|
return {
|
|
1464
|
-
|
|
1465
|
-
pointerDownHandler,
|
|
1434
|
+
refCallback: setNode,
|
|
1466
1435
|
dragging
|
|
1467
1436
|
};
|
|
1468
1437
|
}
|
|
@@ -1480,8 +1449,9 @@ function Point({ value, min, max, size, width = 16, height = 16, color, disabled
|
|
|
1480
1449
|
const externalStyles = usePointsEditorContext((s) => s.externalStyles);
|
|
1481
1450
|
const __disabled = usePointsEditorContext((s) => s.disabled);
|
|
1482
1451
|
const __readonly = usePointsEditorContext((s) => s.readonly);
|
|
1483
|
-
const {
|
|
1452
|
+
const { refCallback: dragRefCallback, dragging } = useDragWithElement({
|
|
1484
1453
|
baseElementRef: containerElementRef,
|
|
1454
|
+
cursor: readonly ? void 0 : externalStyles.cursor,
|
|
1485
1455
|
onDrag: (x, y) => {
|
|
1486
1456
|
if (readonly) return;
|
|
1487
1457
|
onChange?.(clampPoint({
|
|
@@ -1492,7 +1462,6 @@ function Point({ value, min, max, size, width = 16, height = 16, color, disabled
|
|
|
1492
1462
|
onDragStart: (x, y) => {
|
|
1493
1463
|
if (readonly) return;
|
|
1494
1464
|
if (externalStyles.userSelectNone) addUserSelectNone();
|
|
1495
|
-
if (externalStyles.cursor) setCursorStyle(externalStyles.cursor);
|
|
1496
1465
|
onDragStart?.(clampPoint({
|
|
1497
1466
|
x,
|
|
1498
1467
|
y
|
|
@@ -1501,7 +1470,6 @@ function Point({ value, min, max, size, width = 16, height = 16, color, disabled
|
|
|
1501
1470
|
onDragEnd: (x, y) => {
|
|
1502
1471
|
if (readonly) return;
|
|
1503
1472
|
if (externalStyles.userSelectNone) removeUserSelectNone();
|
|
1504
|
-
if (externalStyles.cursor) resetCursorStyle();
|
|
1505
1473
|
onDragEnd?.(clampPoint({
|
|
1506
1474
|
x,
|
|
1507
1475
|
y
|
|
@@ -1510,9 +1478,7 @@ function Point({ value, min, max, size, width = 16, height = 16, color, disabled
|
|
|
1510
1478
|
});
|
|
1511
1479
|
const colors = { "--color": color };
|
|
1512
1480
|
return /* @__PURE__ */ jsx("div", {
|
|
1513
|
-
ref:
|
|
1514
|
-
touchMoveRefCallback(div);
|
|
1515
|
-
},
|
|
1481
|
+
ref: dragRefCallback,
|
|
1516
1482
|
className: clsx("tremolo-points-editor-point", className),
|
|
1517
1483
|
tabIndex: 0,
|
|
1518
1484
|
"aria-disabled": disabled != void 0 ? disabled : __disabled,
|
|
@@ -1526,10 +1492,7 @@ function Point({ value, min, max, size, width = 16, height = 16, color, disabled
|
|
|
1526
1492
|
top: `${value.y * 100}%`,
|
|
1527
1493
|
...style
|
|
1528
1494
|
},
|
|
1529
|
-
onPointerDown
|
|
1530
|
-
pointerDownHandler(event);
|
|
1531
|
-
onPointerDown?.(event);
|
|
1532
|
-
},
|
|
1495
|
+
onPointerDown,
|
|
1533
1496
|
...props
|
|
1534
1497
|
});
|
|
1535
1498
|
}
|
|
@@ -1830,24 +1793,24 @@ const Slider = {
|
|
|
1830
1793
|
reverse,
|
|
1831
1794
|
updateValueByEvent
|
|
1832
1795
|
]);
|
|
1833
|
-
const {
|
|
1796
|
+
const { refCallback: dragRefCallback } = useDragWithElement({
|
|
1834
1797
|
baseElementRef: trackElementRef,
|
|
1798
|
+
cursor: readonly ? void 0 : externalStyles.cursor,
|
|
1799
|
+
updateOnPointerDown: true,
|
|
1835
1800
|
onDrag,
|
|
1836
1801
|
onDragStart: (nx, ny) => {
|
|
1837
1802
|
if (readonly) return;
|
|
1838
1803
|
if (externalStyles.userSelectNone) addUserSelectNone();
|
|
1839
|
-
if (externalStyles.cursor) setCursorStyle(externalStyles.cursor);
|
|
1840
1804
|
thumbRef.current?.focus();
|
|
1841
1805
|
onDragStart?.(clamp(stepValue(rawValue(vertical ? ny : nx, min, max, skew), step), min, max));
|
|
1842
1806
|
},
|
|
1843
1807
|
onDragEnd: (nx, ny) => {
|
|
1844
1808
|
if (readonly) return;
|
|
1845
1809
|
if (externalStyles.userSelectNone) removeUserSelectNone();
|
|
1846
|
-
if (externalStyles.cursor) resetCursorStyle();
|
|
1847
1810
|
onDragEnd?.(clamp(stepValue(rawValue(vertical ? ny : nx, min, max, skew), step), min, max));
|
|
1848
1811
|
}
|
|
1849
1812
|
});
|
|
1850
|
-
const wheelRefCallback =
|
|
1813
|
+
const wheelRefCallback = useWheel((event) => {
|
|
1851
1814
|
if (!wheel || !onChange || readonly) return;
|
|
1852
1815
|
event.preventDefault();
|
|
1853
1816
|
let x;
|
|
@@ -1858,13 +1821,7 @@ const Slider = {
|
|
|
1858
1821
|
}
|
|
1859
1822
|
if (vertical && reverse) x *= -1;
|
|
1860
1823
|
onChange(updateValueByEvent(wheel[0], x));
|
|
1861
|
-
}
|
|
1862
|
-
wheel,
|
|
1863
|
-
vertical,
|
|
1864
|
-
readonly,
|
|
1865
|
-
onChange,
|
|
1866
|
-
updateValueByEvent
|
|
1867
|
-
]);
|
|
1824
|
+
});
|
|
1868
1825
|
useImperativeHandle(forwardedRef, () => {
|
|
1869
1826
|
return {
|
|
1870
1827
|
focus() {
|
|
@@ -1888,7 +1845,7 @@ const Slider = {
|
|
|
1888
1845
|
className: clsx("tremolo-slider", className),
|
|
1889
1846
|
ref: (div) => {
|
|
1890
1847
|
wheelRefCallback(div);
|
|
1891
|
-
|
|
1848
|
+
dragRefCallback(div);
|
|
1892
1849
|
},
|
|
1893
1850
|
tabIndex: -1,
|
|
1894
1851
|
role: "slider",
|
|
@@ -1902,10 +1859,7 @@ const Slider = {
|
|
|
1902
1859
|
margin: `calc(${styleHelper(thumbProps?.size ?? 22)} / 2)`,
|
|
1903
1860
|
...style
|
|
1904
1861
|
},
|
|
1905
|
-
onPointerDown
|
|
1906
|
-
pointerDownHandler(event);
|
|
1907
|
-
onPointerDown?.(event);
|
|
1908
|
-
},
|
|
1862
|
+
onPointerDown,
|
|
1909
1863
|
onKeyDown: (event) => {
|
|
1910
1864
|
handleKeyDown(event);
|
|
1911
1865
|
onKeyDown?.(event);
|
|
@@ -1980,29 +1934,6 @@ function composeRefs(...refs) {
|
|
|
1980
1934
|
};
|
|
1981
1935
|
}
|
|
1982
1936
|
//#endregion
|
|
1983
|
-
//#region src/components/WheelObserver/index.tsx
|
|
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
|
-
*/
|
|
1995
|
-
function WheelObserver(props) {
|
|
1996
|
-
const { as: Component = "div", children, onWheel, ref, ...attributes } = props;
|
|
1997
|
-
return /* @__PURE__ */ jsx(Component, {
|
|
1998
|
-
ref: composeRefs(ref, useRefCallbackEvent("wheel", (event) => {
|
|
1999
|
-
if (onWheel) onWheel(event);
|
|
2000
|
-
}, { passive: false }, [onWheel])),
|
|
2001
|
-
...attributes,
|
|
2002
|
-
children
|
|
2003
|
-
});
|
|
2004
|
-
}
|
|
2005
|
-
//#endregion
|
|
2006
1937
|
//#region src/components/XYPad/Area.tsx
|
|
2007
1938
|
function Area({ width = 120, height = 120, color, children, className, style, __thumb, ...props }) {
|
|
2008
1939
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -2154,13 +2085,14 @@ const XYPad = {
|
|
|
2154
2085
|
y,
|
|
2155
2086
|
updateValueByEvent
|
|
2156
2087
|
]);
|
|
2157
|
-
const {
|
|
2088
|
+
const { refCallback: dragRefCallback } = useDragWithElement({
|
|
2158
2089
|
baseElementRef: areaElementRef,
|
|
2090
|
+
cursor: readonly ? void 0 : externalStyles.cursor,
|
|
2091
|
+
updateOnPointerDown: true,
|
|
2159
2092
|
onDrag,
|
|
2160
2093
|
onDragStart: (nx, ny) => {
|
|
2161
2094
|
if (readonly) return;
|
|
2162
2095
|
if (externalStyles.userSelectNone) addUserSelectNone();
|
|
2163
|
-
if (externalStyles.cursor) setCursorStyle(externalStyles.cursor);
|
|
2164
2096
|
thumbRef.current?.focus();
|
|
2165
2097
|
const valueX = clamp(stepValue(rawValue(nx, x.min, x.max, x.skew), x.step), x.min, x.max);
|
|
2166
2098
|
const valueY = clamp(stepValue(rawValue(ny, y.min, y.max, y.skew), y.step), y.min, y.max);
|
|
@@ -2169,13 +2101,12 @@ const XYPad = {
|
|
|
2169
2101
|
onDragEnd: (nx, ny) => {
|
|
2170
2102
|
if (readonly) return;
|
|
2171
2103
|
if (externalStyles.userSelectNone) removeUserSelectNone();
|
|
2172
|
-
if (externalStyles.cursor) resetCursorStyle();
|
|
2173
2104
|
const valueX = clamp(stepValue(rawValue(nx, x.min, x.max, x.skew), x.step), x.min, x.max);
|
|
2174
2105
|
const valueY = clamp(stepValue(rawValue(ny, y.min, y.max, y.skew), y.step), y.min, y.max);
|
|
2175
2106
|
onDragEnd?.(valueX, valueY);
|
|
2176
2107
|
}
|
|
2177
2108
|
});
|
|
2178
|
-
const wheelRefCallback =
|
|
2109
|
+
const wheelRefCallback = useWheel((event) => {
|
|
2179
2110
|
if (!onChange || readonly) return;
|
|
2180
2111
|
const target = event.shiftKey ? x : y;
|
|
2181
2112
|
if (!target.wheel) return;
|
|
@@ -2186,13 +2117,7 @@ const XYPad = {
|
|
|
2186
2117
|
const newValue = updateValueByEvent(target.wheel[0], target, delta);
|
|
2187
2118
|
if (event.shiftKey) onChange(newValue, y.value);
|
|
2188
2119
|
else onChange(x.value, newValue);
|
|
2189
|
-
}
|
|
2190
|
-
x,
|
|
2191
|
-
y,
|
|
2192
|
-
onChange,
|
|
2193
|
-
readonly,
|
|
2194
|
-
updateValueByEvent
|
|
2195
|
-
]);
|
|
2120
|
+
});
|
|
2196
2121
|
useImperativeHandle(forwardedRef, () => {
|
|
2197
2122
|
return {
|
|
2198
2123
|
focus() {
|
|
@@ -2206,7 +2131,7 @@ const XYPad = {
|
|
|
2206
2131
|
}, []);
|
|
2207
2132
|
return /* @__PURE__ */ jsx("div", {
|
|
2208
2133
|
className: clsx("tremolo-xy-pad", className),
|
|
2209
|
-
ref: composeRefs(rootRef, wheelRefCallback,
|
|
2134
|
+
ref: composeRefs(rootRef, wheelRefCallback, dragRefCallback),
|
|
2210
2135
|
tabIndex: -1,
|
|
2211
2136
|
"aria-disabled": disabled,
|
|
2212
2137
|
"aria-readonly": readonly,
|
|
@@ -2216,10 +2141,7 @@ const XYPad = {
|
|
|
2216
2141
|
WebkitTapHighlightColor: "transparent",
|
|
2217
2142
|
...style
|
|
2218
2143
|
},
|
|
2219
|
-
onPointerDown
|
|
2220
|
-
pointerDownHandler(event);
|
|
2221
|
-
onPointerDown?.(event);
|
|
2222
|
-
},
|
|
2144
|
+
onPointerDown,
|
|
2223
2145
|
onKeyDown: (event) => {
|
|
2224
2146
|
handleKeyDown(event);
|
|
2225
2147
|
onKeyDown?.(event);
|
|
@@ -2270,72 +2192,56 @@ function useAnimationFrame(callback = () => {}, deps = []) {
|
|
|
2270
2192
|
}
|
|
2271
2193
|
//#endregion
|
|
2272
2194
|
//#region src/hooks/useMIDIAccess.ts
|
|
2273
|
-
/** @private */
|
|
2274
|
-
const PERMISSION_DENIED = "PERMISSION_DENIED";
|
|
2275
|
-
/** @private */
|
|
2276
|
-
const NOT_SUPPORTED = "NOT_SUPPORTED";
|
|
2277
2195
|
/**
|
|
2278
2196
|
* Hooks for requesting MIDI access in the browser. The first argument allows you to choose whether to request access on mount.
|
|
2279
2197
|
*/
|
|
2280
2198
|
function useMIDIAccess(requestOnMount = true) {
|
|
2281
|
-
const
|
|
2282
|
-
const
|
|
2283
|
-
const request = useCallback(() => {
|
|
2284
|
-
if (navigator.requestMIDIAccess) navigator.requestMIDIAccess().then((access) => {
|
|
2285
|
-
setMidiAccess(access);
|
|
2286
|
-
setError(null);
|
|
2287
|
-
}).catch(() => {
|
|
2288
|
-
setError(PERMISSION_DENIED);
|
|
2289
|
-
});
|
|
2290
|
-
else setError(NOT_SUPPORTED);
|
|
2291
|
-
}, []);
|
|
2199
|
+
const instance = useMemo(() => createMIDIAccess(), []);
|
|
2200
|
+
const { midiAccess, error } = useSyncExternalStore(instance.subscribe, instance.getState, instance.getServerState);
|
|
2292
2201
|
useEffect(() => {
|
|
2293
|
-
if (requestOnMount) request();
|
|
2294
|
-
}, [
|
|
2202
|
+
if (requestOnMount) instance.request();
|
|
2203
|
+
}, [instance, requestOnMount]);
|
|
2204
|
+
useEffect(() => {
|
|
2205
|
+
return () => instance.destroy();
|
|
2206
|
+
}, [instance]);
|
|
2295
2207
|
return {
|
|
2296
|
-
request,
|
|
2208
|
+
request: instance.request,
|
|
2297
2209
|
midiAccess,
|
|
2298
2210
|
error
|
|
2299
2211
|
};
|
|
2300
2212
|
}
|
|
2301
2213
|
//#endregion
|
|
2302
|
-
//#region src/hooks/useMIDIMessage.ts
|
|
2303
|
-
/**
|
|
2304
|
-
* Hooks for when you want to process MIDI events in more detail than useMIDIInput.
|
|
2305
|
-
*/
|
|
2306
|
-
function useMIDIMessage(midiAccess, onMIDIMessage) {
|
|
2307
|
-
useEffect(() => {
|
|
2308
|
-
if (!midiAccess) return;
|
|
2309
|
-
for (const input of midiAccess.inputs.values()) input.addEventListener("midimessage", onMIDIMessage);
|
|
2310
|
-
return () => {
|
|
2311
|
-
for (const input of midiAccess.inputs.values()) input.removeEventListener("midimessage", onMIDIMessage);
|
|
2312
|
-
};
|
|
2313
|
-
}, [midiAccess, onMIDIMessage]);
|
|
2314
|
-
}
|
|
2315
|
-
//#endregion
|
|
2316
2214
|
//#region src/hooks/useMIDIInput.ts
|
|
2317
|
-
const MIDI_EVENT_TO_NUMBER = {
|
|
2318
|
-
NOTE_ON: 144,
|
|
2319
|
-
NOTE_OFF: 128,
|
|
2320
|
-
PITCH_BEND: 224
|
|
2321
|
-
};
|
|
2322
2215
|
/**
|
|
2323
2216
|
* Hooks for handling note on/off events. To be used with useMIDIAccess. Internally uses useMIDIMessage.
|
|
2324
2217
|
*/
|
|
2325
2218
|
function useMIDIInput(midiAccess, onNoteOnEvent, onNoteOffEvent, onPitchBendEvent) {
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2219
|
+
useEffect(() => {
|
|
2220
|
+
const instance = createMIDIInput(midiAccess, {
|
|
2221
|
+
onNoteOnEvent,
|
|
2222
|
+
onNoteOffEvent,
|
|
2223
|
+
onPitchBendEvent
|
|
2224
|
+
});
|
|
2225
|
+
return () => instance.destroy();
|
|
2332
2226
|
}, [
|
|
2333
|
-
|
|
2227
|
+
midiAccess,
|
|
2334
2228
|
onNoteOnEvent,
|
|
2229
|
+
onNoteOffEvent,
|
|
2335
2230
|
onPitchBendEvent
|
|
2336
|
-
])
|
|
2231
|
+
]);
|
|
2232
|
+
}
|
|
2233
|
+
//#endregion
|
|
2234
|
+
//#region src/hooks/useMIDIMessage.ts
|
|
2235
|
+
/**
|
|
2236
|
+
* Hooks for when you want to process MIDI events in more detail than useMIDIInput.
|
|
2237
|
+
*/
|
|
2238
|
+
function useMIDIMessage(midiAccess, onMIDIMessage) {
|
|
2239
|
+
useEffect(() => {
|
|
2240
|
+
const instance = createMIDIMessage(midiAccess, onMIDIMessage);
|
|
2241
|
+
return () => instance.destroy();
|
|
2242
|
+
}, [midiAccess, onMIDIMessage]);
|
|
2337
2243
|
}
|
|
2338
2244
|
//#endregion
|
|
2339
|
-
export { AnimationCanvas,
|
|
2245
|
+
export { AnimationCanvas, Knob, NOT_SUPPORTED, NumberInput, PERMISSION_DENIED, Piano, PointsEditor, SHORTCUTS, Slider, XYPad, clampPoint, getNoteRangeArray, useAnimationFrame, useDrag, useDragWithElement, useEventListener, useInterval, useLongPress, useMIDIAccess, useMIDIInput, useMIDIMessage, useSliderContext, useWheel };
|
|
2340
2246
|
|
|
2341
2247
|
//# sourceMappingURL=index.js.map
|