@tremolo-ui/react 0.1.5 → 0.1.6
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 +171 -95
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +79 -32
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +80 -56
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +82 -58
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +160 -91
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/Knob/index.css +54 -23
- package/src/components/Knob/index.tsx +30 -25
- package/src/components/NumberInput/index.tsx +3 -4
- package/src/components/Piano/context.tsx +1 -1
- package/src/components/Piano/index.css +24 -8
- package/src/components/Piano/index.tsx +253 -217
- package/src/components/Piano/key.tsx +3 -3
- package/src/components/Slider/Track.tsx +2 -2
- package/src/components/Slider/index.tsx +2 -2
- package/src/components/XYPad/index.tsx +2 -2
- package/src/hooks/useAnimationFrame.ts +3 -0
- package/src/hooks/useCallbackRef.ts +2 -2
- package/src/hooks/useDrag.ts +2 -1
- package/src/hooks/useDragWithElement.ts +2 -1
- package/src/hooks/useEventListener.ts +3 -0
- package/src/hooks/useInterval.ts +3 -0
- package/src/hooks/useLongPress.ts +3 -0
- package/src/hooks/useMIDIAccess.ts +40 -0
- package/src/hooks/useMIDIInput.ts +50 -0
- package/src/hooks/useMIDIMessage.ts +24 -0
- package/src/hooks/useRefCallbackEvent.ts +4 -0
- package/src/index.ts +19 -12
- package/src/styles/global.css +1 -1
package/dist/index.js
CHANGED
|
@@ -144,8 +144,8 @@ function AnimationCanvas({ draw, init, animate = true, options, width: _width =
|
|
|
144
144
|
//#endregion
|
|
145
145
|
//#region src/hooks/useCallbackRef.ts
|
|
146
146
|
/**
|
|
147
|
-
*
|
|
148
|
-
*
|
|
147
|
+
* Internal
|
|
148
|
+
* @private
|
|
149
149
|
*/
|
|
150
150
|
function useCallbackRef(callback, deps = []) {
|
|
151
151
|
const callbackRef = useRef(() => {
|
|
@@ -176,8 +176,7 @@ function useEventListener(target, event, handler, options) {
|
|
|
176
176
|
handler
|
|
177
177
|
]);
|
|
178
178
|
return () => {
|
|
179
|
-
|
|
180
|
-
node?.removeEventListener(event, listener, options);
|
|
179
|
+
(typeof target === "function" ? target() : target ?? document)?.removeEventListener(event, listener, options);
|
|
181
180
|
};
|
|
182
181
|
}
|
|
183
182
|
|
|
@@ -202,6 +201,7 @@ function useRefCallbackEvent(event, handler, options, deps = []) {
|
|
|
202
201
|
//#endregion
|
|
203
202
|
//#region src/hooks/useDrag.ts
|
|
204
203
|
/**
|
|
204
|
+
* @category hooks
|
|
205
205
|
* @returns [refCallback, pointerDownHandler]
|
|
206
206
|
*/
|
|
207
207
|
function useDrag({ threshold = 1, onDrag, onDragStart, onDragEnd }) {
|
|
@@ -268,9 +268,7 @@ const AnimationKnob = forwardRef(({ value, min, max, defaultValue = min, startVa
|
|
|
268
268
|
const isRelativeSize = typeof (size ?? width) == "string" || typeof height == "string";
|
|
269
269
|
const onDrag = useCallback((_x, y) => {
|
|
270
270
|
if (!onChange) return;
|
|
271
|
-
|
|
272
|
-
const v2 = clamp(stepValue(v, step), min, max);
|
|
273
|
-
onChange(v2);
|
|
271
|
+
onChange(clamp(stepValue(rawValue(valueRef.current - y / 100, min, max, skew), step), min, max));
|
|
274
272
|
}, [
|
|
275
273
|
max,
|
|
276
274
|
min,
|
|
@@ -290,10 +288,8 @@ const AnimationKnob = forwardRef(({ value, min, max, defaultValue = min, startVa
|
|
|
290
288
|
event.preventDefault();
|
|
291
289
|
const x = key == "ArrowRight" || key == "ArrowUp" ? keyboard[1] : -keyboard[1];
|
|
292
290
|
let v;
|
|
293
|
-
if (keyboard[0] == "normalized")
|
|
294
|
-
|
|
295
|
-
v = rawValue(n + x, min, max, skew);
|
|
296
|
-
} else v = value + x;
|
|
291
|
+
if (keyboard[0] == "normalized") v = rawValue(normalizeValue(value, min, max, skew) + x, min, max, skew);
|
|
292
|
+
else v = value + x;
|
|
297
293
|
onChange(clamp(stepValue(v, step), min, max));
|
|
298
294
|
}
|
|
299
295
|
}, [
|
|
@@ -320,10 +316,8 @@ const AnimationKnob = forwardRef(({ value, min, max, defaultValue = min, startVa
|
|
|
320
316
|
event.preventDefault();
|
|
321
317
|
const x = event.deltaY > 0 ? -wheel[1] : wheel[1];
|
|
322
318
|
let v;
|
|
323
|
-
if (wheel[0] == "normalized")
|
|
324
|
-
|
|
325
|
-
v = rawValue(n + x, min, max, skew);
|
|
326
|
-
} else v = value + x;
|
|
319
|
+
if (wheel[0] == "normalized") v = rawValue(normalizeValue(value, min, max, skew) + x, min, max, skew);
|
|
320
|
+
else v = value + x;
|
|
327
321
|
if (onChange) onChange(clamp(stepValue(v, step), min, max));
|
|
328
322
|
}, { passive: false }, [
|
|
329
323
|
wheel,
|
|
@@ -435,20 +429,12 @@ function DragObserver(props) {
|
|
|
435
429
|
*
|
|
436
430
|
* @category Knob
|
|
437
431
|
*/
|
|
438
|
-
const Knob = forwardRef(({ value, min, max, step = 1, skew = 1, defaultValue = min, startValue = min, size = 50, bodyNoSelect = true, wheel = ["raw", 1], keyboard = ["raw", 1], enableDoubleClickDefault = true, disabled = false, readonly = false, activeLine, inactiveLine, thumb, thumbLine, onChange, onKeyDown, onPointerDown, onDoubleClick, className, classes,...props },
|
|
432
|
+
const Knob = forwardRef(({ value, min, max, step = 1, skew = 1, defaultValue = min, startValue = min, size = 50, bodyNoSelect = true, wheel = ["raw", 1], keyboard = ["raw", 1], enableDoubleClickDefault = true, disabled = false, readonly = false, activeLine, inactiveLine, thumb, thumbLine, thumbSize = 84, thumbLineWeight = 6, thumbLineLength = 35, lineWeight = 6, angleRange = 270, onChange, onKeyDown, onPointerDown, onDoubleClick, className, classes,...props }, forwardedRef) => {
|
|
439
433
|
const valueRef = useRef(0);
|
|
440
434
|
const elmRef = useRef(null);
|
|
441
|
-
const padding = 8;
|
|
442
|
-
const thumbLineWeight = 6;
|
|
443
|
-
const thumbLineLength = 35;
|
|
444
|
-
const lineWeight = size * .06;
|
|
445
|
-
const p = normalizeValue(value, min, max, skew);
|
|
446
|
-
const s = normalizeValue(startValue, min, max, skew);
|
|
447
435
|
const onDrag = useCallback((_x, y) => {
|
|
448
436
|
if (!onChange || readonly) return;
|
|
449
|
-
|
|
450
|
-
const v2 = clamp(stepValue(v, step), min, max);
|
|
451
|
-
onChange(v2);
|
|
437
|
+
onChange(clamp(stepValue(rawValue(valueRef.current - y / 100, min, max, skew), step), min, max));
|
|
452
438
|
}, [
|
|
453
439
|
max,
|
|
454
440
|
min,
|
|
@@ -459,10 +445,8 @@ const Knob = forwardRef(({ value, min, max, step = 1, skew = 1, defaultValue = m
|
|
|
459
445
|
]);
|
|
460
446
|
const updateValueByEvent = useCallback((eventType, x) => {
|
|
461
447
|
let newValue;
|
|
462
|
-
if (eventType == "normalized")
|
|
463
|
-
|
|
464
|
-
newValue = rawValue(n + x, min, max, skew);
|
|
465
|
-
} else newValue = value + x;
|
|
448
|
+
if (eventType == "normalized") newValue = rawValue(normalizeValue(value, min, max, skew) + x, min, max, skew);
|
|
449
|
+
else newValue = value + x;
|
|
466
450
|
return clamp(stepValue(newValue, step), min, max);
|
|
467
451
|
}, [
|
|
468
452
|
max,
|
|
@@ -512,7 +496,7 @@ const Knob = forwardRef(({ value, min, max, step = 1, skew = 1, defaultValue = m
|
|
|
512
496
|
readonly,
|
|
513
497
|
updateValueByEvent
|
|
514
498
|
]);
|
|
515
|
-
useImperativeHandle(
|
|
499
|
+
useImperativeHandle(forwardedRef, () => {
|
|
516
500
|
return {
|
|
517
501
|
focus() {
|
|
518
502
|
elmRef.current?.focus();
|
|
@@ -522,11 +506,15 @@ const Knob = forwardRef(({ value, min, max, step = 1, skew = 1, defaultValue = m
|
|
|
522
506
|
}
|
|
523
507
|
};
|
|
524
508
|
}, []);
|
|
525
|
-
const
|
|
526
|
-
const
|
|
527
|
-
const
|
|
528
|
-
const
|
|
529
|
-
const
|
|
509
|
+
const viewBoxSize = 100;
|
|
510
|
+
const center = viewBoxSize / 2;
|
|
511
|
+
const padding = (viewBoxSize - clamp(thumbSize, 0, 100)) / 2;
|
|
512
|
+
const p = normalizeValue(value, min, max, skew);
|
|
513
|
+
const s = normalizeValue(startValue, min, max, skew);
|
|
514
|
+
const r1 = -angleRange / 2;
|
|
515
|
+
const r2 = r1 + Math.min(p, s) * angleRange;
|
|
516
|
+
const r3 = r1 + Math.max(p, s) * angleRange;
|
|
517
|
+
const r4 = angleRange / 2;
|
|
530
518
|
const x1 = center + center * Math.cos(radian(r1 - 90));
|
|
531
519
|
const y1 = center + center * Math.sin(radian(r1 - 90));
|
|
532
520
|
const x2 = center + center * Math.cos(radian(r2 - 90));
|
|
@@ -536,12 +524,13 @@ const Knob = forwardRef(({ value, min, max, step = 1, skew = 1, defaultValue = m
|
|
|
536
524
|
const x4 = center + center * Math.cos(radian(r4 - 90));
|
|
537
525
|
const y4 = center + center * Math.sin(radian(r4 - 90));
|
|
538
526
|
return /* @__PURE__ */ jsxs("svg", {
|
|
539
|
-
className: clsx("tremolo-knob", className),
|
|
540
527
|
ref: (div) => {
|
|
541
528
|
elmRef.current = div;
|
|
542
529
|
wheelRefCallback(div);
|
|
543
530
|
touchMoveRefCallback(div);
|
|
544
531
|
},
|
|
532
|
+
className: clsx("tremolo-knob", className),
|
|
533
|
+
viewBox: `0 0 ${viewBoxSize} ${viewBoxSize}`,
|
|
545
534
|
width: size,
|
|
546
535
|
height: size,
|
|
547
536
|
tabIndex: 0,
|
|
@@ -591,7 +580,7 @@ const Knob = forwardRef(({ value, min, max, step = 1, skew = 1, defaultValue = m
|
|
|
591
580
|
children: [/* @__PURE__ */ jsx("circle", {
|
|
592
581
|
cx: "50%",
|
|
593
582
|
cy: "50%",
|
|
594
|
-
r: `${
|
|
583
|
+
r: `${thumbSize / 2}%`,
|
|
595
584
|
fill: thumb || "currentColor"
|
|
596
585
|
}), /* @__PURE__ */ jsx("line", {
|
|
597
586
|
className: clsx("tremolo-knob-thumb-line", classes?.thumbLine),
|
|
@@ -602,7 +591,7 @@ const Knob = forwardRef(({ value, min, max, step = 1, skew = 1, defaultValue = m
|
|
|
602
591
|
stroke: thumbLine || "currentColor",
|
|
603
592
|
strokeWidth: `${thumbLineWeight}%`,
|
|
604
593
|
style: {
|
|
605
|
-
transform: `rotate(${
|
|
594
|
+
transform: `rotate(${r1 + p * angleRange}deg)`,
|
|
606
595
|
transformOrigin: "50% 50%"
|
|
607
596
|
}
|
|
608
597
|
})]
|
|
@@ -675,8 +664,7 @@ const createNumberInputStore = (initProps) => {
|
|
|
675
664
|
valueAsNumber: parseValue(String(initProps?.value || ""), initProps?.units, initProps?.digit).rawValue,
|
|
676
665
|
increment: () => set((state) => {
|
|
677
666
|
if (state.readonly) return {};
|
|
678
|
-
|
|
679
|
-
let next = current + (state.step ?? 1);
|
|
667
|
+
let next = parseValue(state.value, state.units, state.digit).rawValue + (state.step ?? 1);
|
|
680
668
|
if (state.keepWithinRange) next = safeClamp(next, state.min, state.max);
|
|
681
669
|
const v = parseValue(String(next), state.units, state.digit).formatValue;
|
|
682
670
|
state?.onChange?.(next, v);
|
|
@@ -687,8 +675,7 @@ const createNumberInputStore = (initProps) => {
|
|
|
687
675
|
}),
|
|
688
676
|
decrement: () => set((state) => {
|
|
689
677
|
if (state.readonly) return {};
|
|
690
|
-
|
|
691
|
-
let next = current - (state.step ?? 1);
|
|
678
|
+
let next = parseValue(state.value, state.units, state.digit).rawValue - (state.step ?? 1);
|
|
692
679
|
if (state.keepWithinRange) next = safeClamp(next, state.min, state.max);
|
|
693
680
|
const v = parseValue(String(next), state.units, state.digit).formatValue;
|
|
694
681
|
state?.onChange?.(next, v);
|
|
@@ -760,8 +747,7 @@ const InternalInput = forwardRef(({ disabled, readonly, selectWithFocus, blurOnE
|
|
|
760
747
|
let newValue;
|
|
761
748
|
if (eventType == "normalized") {
|
|
762
749
|
if (!min || !max) throw new Error("required parameter: min, max");
|
|
763
|
-
|
|
764
|
-
newValue = rawValue(n + x, min, max);
|
|
750
|
+
newValue = rawValue(normalizeValue(valueAsNumber, min, max) + x, min, max);
|
|
765
751
|
return clamp(stepValue(newValue, step), min, max);
|
|
766
752
|
} else {
|
|
767
753
|
newValue = valueAsNumber + x;
|
|
@@ -877,6 +863,9 @@ function Stepper({ dynamic = true, children, className,...props }) {
|
|
|
877
863
|
|
|
878
864
|
//#endregion
|
|
879
865
|
//#region src/hooks/useInterval.ts
|
|
866
|
+
/**
|
|
867
|
+
* @category hooks
|
|
868
|
+
*/
|
|
880
869
|
function useInterval(callback, delay) {
|
|
881
870
|
const savedCallback = useRef(callback);
|
|
882
871
|
useEffect(() => {
|
|
@@ -895,6 +884,9 @@ function useInterval(callback, delay) {
|
|
|
895
884
|
|
|
896
885
|
//#endregion
|
|
897
886
|
//#region src/hooks/useLongPress.ts
|
|
887
|
+
/**
|
|
888
|
+
* @category hooks
|
|
889
|
+
*/
|
|
898
890
|
function useLongPress(callback, initialDelay = 500, interval = 40) {
|
|
899
891
|
const [pressed, setPressed] = useState(false);
|
|
900
892
|
const [delay, setDelay] = useState(initialDelay);
|
|
@@ -920,8 +912,7 @@ function IncrementStepper({ size = 12, children, className,...props }) {
|
|
|
920
912
|
const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
|
|
921
913
|
const readonly = useNumberInputContext((s) => s.readonly);
|
|
922
914
|
const keepWithinRange = useNumberInputContext((s) => s.keepWithinRange);
|
|
923
|
-
const
|
|
924
|
-
const press = useLongPress(increment);
|
|
915
|
+
const press = useLongPress(useNumberInputContext((s) => s.increment));
|
|
925
916
|
return /* @__PURE__ */ jsx("div", {
|
|
926
917
|
className: clsx("tremolo-number-input-increment-stepper", className),
|
|
927
918
|
role: "button",
|
|
@@ -952,8 +943,7 @@ function DecrementStepper({ size = 12, children, className,...props }) {
|
|
|
952
943
|
const valueAsNumber = useNumberInputContext((s) => s.valueAsNumber);
|
|
953
944
|
const readonly = useNumberInputContext((s) => s.readonly);
|
|
954
945
|
const keepWithinRange = useNumberInputContext((s) => s.keepWithinRange);
|
|
955
|
-
const
|
|
956
|
-
const press = useLongPress(decrement);
|
|
946
|
+
const press = useLongPress(useNumberInputContext((s) => s.decrement));
|
|
957
947
|
return /* @__PURE__ */ jsx("div", {
|
|
958
948
|
className: clsx("tremolo-number-input-decrement-stepper", className),
|
|
959
949
|
role: "button",
|
|
@@ -982,7 +972,7 @@ function DecrementStepper({ size = 12, children, className,...props }) {
|
|
|
982
972
|
* Input with some useful functions for entering numerical values.
|
|
983
973
|
* @category NumberInput
|
|
984
974
|
*/
|
|
985
|
-
const NumberInput = 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 },
|
|
975
|
+
const NumberInput = 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) => {
|
|
986
976
|
const colors = { "--active-color": activeColor };
|
|
987
977
|
return /* @__PURE__ */ jsx(NumberInputProvider, {
|
|
988
978
|
value: String(value),
|
|
@@ -1001,7 +991,7 @@ const NumberInput = forwardRef(({ value, min, max, step = 1, units, readonly = f
|
|
|
1001
991
|
"data-stepper": !!children,
|
|
1002
992
|
"data-variant": variant,
|
|
1003
993
|
children: [/* @__PURE__ */ jsx(InternalInput, {
|
|
1004
|
-
ref,
|
|
994
|
+
ref: forwardedRef,
|
|
1005
995
|
readonly,
|
|
1006
996
|
disabled,
|
|
1007
997
|
selectWithFocus,
|
|
@@ -1022,6 +1012,7 @@ const NumberInput = forwardRef(({ value, min, max, step = 1, units, readonly = f
|
|
|
1022
1012
|
//#endregion
|
|
1023
1013
|
//#region src/hooks/useDragWithElement.ts
|
|
1024
1014
|
/**
|
|
1015
|
+
* @category hooks
|
|
1025
1016
|
* @returns [refCallback, pointerDownHandler]
|
|
1026
1017
|
*/
|
|
1027
1018
|
function useDragWithElement({ baseElementRef, onDrag, onDragStart, onDragEnd }) {
|
|
@@ -1101,9 +1092,7 @@ function usePianoContext(selector) {
|
|
|
1101
1092
|
//#region src/components/Piano/KeyLabel.tsx
|
|
1102
1093
|
/** @category Piano */
|
|
1103
1094
|
function KeyLabel({ label, className, wrapperClassName, wrapperStyle, __note, __label,...props }) {
|
|
1104
|
-
const
|
|
1105
|
-
const noteRangeArray = getNoteRangeArray(noteRange);
|
|
1106
|
-
const index = noteRangeArray.indexOf(__note);
|
|
1095
|
+
const index = getNoteRangeArray(usePianoContext((s) => s.noteRange)).indexOf(__note);
|
|
1107
1096
|
const content = label ? label(__note, index) : __label && __label(__note, index);
|
|
1108
1097
|
return (content != void 0 || content != null) && /* @__PURE__ */ jsx("div", {
|
|
1109
1098
|
className: clsx("tremolo-piano-key-label-wrapper", wrapperClassName),
|
|
@@ -1129,8 +1118,7 @@ const KeyImpl = forwardRef(({ keyType, noteNumber, width, height, bg, color, act
|
|
|
1129
1118
|
const onPlayNote = usePianoContext((s) => s.onPlayNote);
|
|
1130
1119
|
const onStopNote = usePianoContext((s) => s.onStopNote);
|
|
1131
1120
|
const label = usePianoContext((s) => s.label);
|
|
1132
|
-
const
|
|
1133
|
-
const position = notePosition(noteNumber);
|
|
1121
|
+
const position = usePianoContext((s) => s.notePosition)(noteNumber);
|
|
1134
1122
|
const disabled = noteNumber > midiMax;
|
|
1135
1123
|
const colors = {
|
|
1136
1124
|
"--bg": bg,
|
|
@@ -1140,10 +1128,10 @@ const KeyImpl = forwardRef(({ keyType, noteNumber, width, height, bg, color, act
|
|
|
1140
1128
|
};
|
|
1141
1129
|
useImperativeHandle(ref, () => {
|
|
1142
1130
|
return {
|
|
1143
|
-
play() {
|
|
1131
|
+
play(velocity) {
|
|
1144
1132
|
if (disabled) return;
|
|
1145
1133
|
setPlayed(true);
|
|
1146
|
-
if (onPlayNote) onPlayNote(noteNumber);
|
|
1134
|
+
if (onPlayNote) onPlayNote(noteNumber, velocity);
|
|
1147
1135
|
},
|
|
1148
1136
|
stop() {
|
|
1149
1137
|
setPlayed(false);
|
|
@@ -1234,12 +1222,9 @@ const SHORTCUTS = { HOME_ROW: { keys: [
|
|
|
1234
1222
|
//#endregion
|
|
1235
1223
|
//#region src/components/Piano/index.tsx
|
|
1236
1224
|
/**
|
|
1237
|
-
*
|
|
1238
|
-
*
|
|
1239
|
-
* TODO:
|
|
1240
|
-
* - add scale highlight
|
|
1225
|
+
* [noteRange.first, noteRange.first + 1 ..., noteRange.last]
|
|
1226
|
+
* @category Piano
|
|
1241
1227
|
*/
|
|
1242
|
-
/** @category Piano */
|
|
1243
1228
|
function getNoteRangeArray(noteRange) {
|
|
1244
1229
|
return Array.from({ length: noteRange.last - noteRange.first + 1 }, (_, i) => i + noteRange.first);
|
|
1245
1230
|
}
|
|
@@ -1248,7 +1233,7 @@ const blackPerWhiteWidth = defaultBlackKeyWidth / defaultWhiteKeyWidth;
|
|
|
1248
1233
|
* Customizable piano component.
|
|
1249
1234
|
* @category Piano
|
|
1250
1235
|
*/
|
|
1251
|
-
|
|
1236
|
+
const Piano = forwardRef(({ noteRange, glissando = true, midiMax = 127, keyboardShortcuts, fill = false, height = fill ? "100%" : 160, whiteNoteWidth: _whiteNoteWidth = defaultWhiteKeyWidth, style, className, onPlayNote, onStopNote, label, children, onPointerDown,...props }, forwardedRef) => {
|
|
1252
1237
|
const [whiteNoteWidth, setWhiteNoteWidth] = useState(_whiteNoteWidth);
|
|
1253
1238
|
const keyRefs = useRef([]);
|
|
1254
1239
|
for (let i = 0; i < noteRange.last - noteRange.first + 1; i++) keyRefs.current[i] = createRef();
|
|
@@ -1319,9 +1304,7 @@ function Piano({ noteRange, glissando = true, midiMax = 127, keyboardShortcuts,
|
|
|
1319
1304
|
]);
|
|
1320
1305
|
const onDrag = useCallback((perX, perY) => {
|
|
1321
1306
|
if (!pianoRef.current) return;
|
|
1322
|
-
const
|
|
1323
|
-
const y = perY * pianoRef.current.clientHeight;
|
|
1324
|
-
const note = getHitKeyIndex(x, y);
|
|
1307
|
+
const note = getHitKeyIndex(perX * staticWidth, perY * pianoRef.current.clientHeight);
|
|
1325
1308
|
const index = noteRangeArray.indexOf(note);
|
|
1326
1309
|
if (index == -1) return;
|
|
1327
1310
|
if (hitKeyIndex.current != index) {
|
|
@@ -1375,6 +1358,25 @@ function Piano({ noteRange, glissando = true, midiMax = 127, keyboardShortcuts,
|
|
|
1375
1358
|
const index = keyboardShortcuts.keys.indexOf(e.key);
|
|
1376
1359
|
if (index != -1) keyRefs.current[index]?.current?.stop();
|
|
1377
1360
|
});
|
|
1361
|
+
useImperativeHandle(forwardedRef, () => {
|
|
1362
|
+
return {
|
|
1363
|
+
playNote(note, velocity) {
|
|
1364
|
+
const index = noteRangeArray.indexOf(note);
|
|
1365
|
+
if (index != -1) {
|
|
1366
|
+
if (!keyRefs.current[index]?.current?.played()) keyRefs.current[index]?.current?.play(velocity);
|
|
1367
|
+
} else onPlayNote?.(note, velocity);
|
|
1368
|
+
},
|
|
1369
|
+
stopNote(note) {
|
|
1370
|
+
const index = noteRangeArray.indexOf(note);
|
|
1371
|
+
if (index != -1) keyRefs.current[index]?.current?.stop();
|
|
1372
|
+
else onStopNote?.(note);
|
|
1373
|
+
}
|
|
1374
|
+
};
|
|
1375
|
+
}, [
|
|
1376
|
+
noteRangeArray,
|
|
1377
|
+
onPlayNote,
|
|
1378
|
+
onStopNote
|
|
1379
|
+
]);
|
|
1378
1380
|
return /* @__PURE__ */ jsx(PianoProvider, {
|
|
1379
1381
|
notePosition,
|
|
1380
1382
|
noteRange,
|
|
@@ -1411,7 +1413,7 @@ function Piano({ noteRange, glissando = true, midiMax = 127, keyboardShortcuts,
|
|
|
1411
1413
|
}, note))
|
|
1412
1414
|
})
|
|
1413
1415
|
});
|
|
1414
|
-
}
|
|
1416
|
+
});
|
|
1415
1417
|
|
|
1416
1418
|
//#endregion
|
|
1417
1419
|
//#region src/components/Slider/context.tsx
|
|
@@ -1605,7 +1607,7 @@ function SliderTrack({ length = defaultLength, thickness = defaultThickness, act
|
|
|
1605
1607
|
"data-vertical": vertical,
|
|
1606
1608
|
style: !defaultStyle ? style : {
|
|
1607
1609
|
...colors,
|
|
1608
|
-
background: xor(vertical, reverse) ? `linear-gradient(to ${direction}, var(--inactive
|
|
1610
|
+
background: xor(vertical, reverse) ? `linear-gradient(to ${direction}, var(--inactive) ${__percent}%, var(--active) ${__percent}%)` : `linear-gradient(to ${direction}, var(--active) ${__percent}%, var(--inactive) ${__percent}%)`,
|
|
1609
1611
|
borderRadius: styleHelper(thickness, "/", 2),
|
|
1610
1612
|
width: !vertical ? length : thickness,
|
|
1611
1613
|
height: vertical ? length : thickness,
|
|
@@ -1622,7 +1624,7 @@ function SliderTrack({ length = defaultLength, thickness = defaultThickness, act
|
|
|
1622
1624
|
* Customizable slider
|
|
1623
1625
|
* @category Slider
|
|
1624
1626
|
*/
|
|
1625
|
-
const Slider = forwardRef(({ value, min, max, step = 1, skew = 1, vertical = false, reverse = false, bodyNoSelect = true, wheel = ["raw", 1], keyboard = ["raw", 1], disabled = false, readonly = false, onChange, onDragStart, onDragEnd, className, style, children, onFocus, onBlur, onPointerDown, onKeyDown,...props },
|
|
1627
|
+
const Slider = forwardRef(({ value, min, max, step = 1, skew = 1, vertical = false, reverse = false, bodyNoSelect = true, wheel = ["raw", 1], keyboard = ["raw", 1], disabled = false, readonly = false, onChange, onDragStart, onDragEnd, className, style, children, onFocus, onBlur, onPointerDown, onKeyDown,...props }, forwardedRef) => {
|
|
1626
1628
|
const trackElementRef = useRef(null);
|
|
1627
1629
|
const thumbRef = useRef(null);
|
|
1628
1630
|
const p = toFixed(normalizeValue(value, min, max, skew) * 100);
|
|
@@ -1642,9 +1644,7 @@ const Slider = forwardRef(({ value, min, max, step = 1, skew = 1, vertical = fal
|
|
|
1642
1644
|
const onDrag = useCallback((nx, ny) => {
|
|
1643
1645
|
if (!onChange || readonly) return;
|
|
1644
1646
|
const n = vertical ? ny : nx;
|
|
1645
|
-
|
|
1646
|
-
const v2 = clamp(stepValue(v, step), min, max);
|
|
1647
|
-
onChange(v2);
|
|
1647
|
+
onChange(clamp(stepValue(rawValue(displayReversed ? 1 - n : n, min, max, skew), step), min, max));
|
|
1648
1648
|
}, [
|
|
1649
1649
|
displayReversed,
|
|
1650
1650
|
max,
|
|
@@ -1657,10 +1657,8 @@ const Slider = forwardRef(({ value, min, max, step = 1, skew = 1, vertical = fal
|
|
|
1657
1657
|
]);
|
|
1658
1658
|
const updateValueByEvent = useCallback((eventType, x) => {
|
|
1659
1659
|
let newValue;
|
|
1660
|
-
if (eventType == "normalized")
|
|
1661
|
-
|
|
1662
|
-
newValue = rawValue(n + x, min, max, skew);
|
|
1663
|
-
} else newValue = value + x;
|
|
1660
|
+
if (eventType == "normalized") newValue = rawValue(normalizeValue(value, min, max, skew) + x, min, max, skew);
|
|
1661
|
+
else newValue = value + x;
|
|
1664
1662
|
return clamp(stepValue(newValue, step), min, max);
|
|
1665
1663
|
}, [
|
|
1666
1664
|
max,
|
|
@@ -1723,7 +1721,7 @@ const Slider = forwardRef(({ value, min, max, step = 1, skew = 1, vertical = fal
|
|
|
1723
1721
|
onChange,
|
|
1724
1722
|
updateValueByEvent
|
|
1725
1723
|
]);
|
|
1726
|
-
useImperativeHandle(
|
|
1724
|
+
useImperativeHandle(forwardedRef, () => {
|
|
1727
1725
|
return {
|
|
1728
1726
|
focus() {
|
|
1729
1727
|
thumbRef.current?.focus();
|
|
@@ -1807,11 +1805,10 @@ const Slider = forwardRef(({ value, min, max, step = 1, skew = 1, vertical = fal
|
|
|
1807
1805
|
/** @category WheelObserver */
|
|
1808
1806
|
function WheelObserver(props) {
|
|
1809
1807
|
const { children, onWheel, as: Component = "div",...attributes } = props;
|
|
1810
|
-
const wheelRefCallback = useRefCallbackEvent("wheel", (event) => {
|
|
1811
|
-
if (onWheel) onWheel(event);
|
|
1812
|
-
}, { passive: false }, [onWheel]);
|
|
1813
1808
|
return /* @__PURE__ */ jsx(Component, {
|
|
1814
|
-
ref:
|
|
1809
|
+
ref: useRefCallbackEvent("wheel", (event) => {
|
|
1810
|
+
if (onWheel) onWheel(event);
|
|
1811
|
+
}, { passive: false }, [onWheel]),
|
|
1815
1812
|
...attributes,
|
|
1816
1813
|
children
|
|
1817
1814
|
});
|
|
@@ -1884,7 +1881,7 @@ const defaultValueOptions = {
|
|
|
1884
1881
|
* Simple XYPad
|
|
1885
1882
|
* @category XYPad
|
|
1886
1883
|
*/
|
|
1887
|
-
const XYPad = forwardRef(({ x: _x, y: _y, className, style, bodyNoSelect = true, disabled = false, readonly = false, onChange, onDragStart, onDragEnd, onPointerDown, onKeyDown, onFocus, onBlur, children,...props },
|
|
1884
|
+
const XYPad = forwardRef(({ x: _x, y: _y, className, style, bodyNoSelect = true, disabled = false, readonly = false, onChange, onDragStart, onDragEnd, onPointerDown, onKeyDown, onFocus, onBlur, children,...props }, forwardedRef) => {
|
|
1888
1885
|
const x = useMemo(() => {
|
|
1889
1886
|
return {
|
|
1890
1887
|
...defaultValueOptions,
|
|
@@ -1915,9 +1912,7 @@ const XYPad = forwardRef(({ x: _x, y: _y, className, style, bodyNoSelect = true,
|
|
|
1915
1912
|
if (!onChange || readonly) return;
|
|
1916
1913
|
const vx = rawValue(x.reverse ? 1 - nx$1 : nx$1, x.min, x.max, x.skew);
|
|
1917
1914
|
const vy = rawValue(y.reverse ? 1 - ny$1 : ny$1, y.min, y.max, y.skew);
|
|
1918
|
-
|
|
1919
|
-
const newY = clamp(stepValue(vy, y.step), y.min, y.max);
|
|
1920
|
-
onChange(newX, newY);
|
|
1915
|
+
onChange(clamp(stepValue(vx, x.step), x.min, x.max), clamp(stepValue(vy, y.step), y.min, y.max));
|
|
1921
1916
|
}, [
|
|
1922
1917
|
onChange,
|
|
1923
1918
|
readonly,
|
|
@@ -1934,10 +1929,8 @@ const XYPad = forwardRef(({ x: _x, y: _y, className, style, bodyNoSelect = true,
|
|
|
1934
1929
|
]);
|
|
1935
1930
|
const updateValueByEvent = useCallback((eventType, target, x$1) => {
|
|
1936
1931
|
let v;
|
|
1937
|
-
if (eventType == "normalized")
|
|
1938
|
-
|
|
1939
|
-
v = rawValue(n + x$1, target.min, target.max, target.skew);
|
|
1940
|
-
} else v = target.value + x$1;
|
|
1932
|
+
if (eventType == "normalized") v = rawValue(normalizeValue(target.value, target.min, target.max, target.skew) + x$1, target.min, target.max, target.skew);
|
|
1933
|
+
else v = target.value + x$1;
|
|
1941
1934
|
return clamp(stepValue(v, target.step), target.min, target.max);
|
|
1942
1935
|
}, []);
|
|
1943
1936
|
const handleKeyDown = useCallback((event) => {
|
|
@@ -2004,7 +1997,7 @@ const XYPad = forwardRef(({ x: _x, y: _y, className, style, bodyNoSelect = true,
|
|
|
2004
1997
|
readonly,
|
|
2005
1998
|
updateValueByEvent
|
|
2006
1999
|
]);
|
|
2007
|
-
useImperativeHandle(
|
|
2000
|
+
useImperativeHandle(forwardedRef, () => {
|
|
2008
2001
|
return {
|
|
2009
2002
|
focus() {
|
|
2010
2003
|
thumbRef.current?.focus();
|
|
@@ -2067,6 +2060,9 @@ const XYPad = forwardRef(({ x: _x, y: _y, className, style, bodyNoSelect = true,
|
|
|
2067
2060
|
|
|
2068
2061
|
//#endregion
|
|
2069
2062
|
//#region src/hooks/useAnimationFrame.ts
|
|
2063
|
+
/**
|
|
2064
|
+
* @category hooks
|
|
2065
|
+
*/
|
|
2070
2066
|
function useAnimationFrame(callback = () => {}, deps = []) {
|
|
2071
2067
|
const reqIdRef = useRef(-1);
|
|
2072
2068
|
const loop = useCallback(() => {
|
|
@@ -2080,5 +2076,78 @@ function useAnimationFrame(callback = () => {}, deps = []) {
|
|
|
2080
2076
|
}
|
|
2081
2077
|
|
|
2082
2078
|
//#endregion
|
|
2083
|
-
|
|
2079
|
+
//#region src/hooks/useMIDIAccess.ts
|
|
2080
|
+
/** @private */
|
|
2081
|
+
const PERMISSION_DENIED = "PERMISSION_DENIED";
|
|
2082
|
+
/** @private */
|
|
2083
|
+
const NOT_SUPPORTED = "NOT_SUPPORTED";
|
|
2084
|
+
/**
|
|
2085
|
+
* 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
|
+
*/
|
|
2088
|
+
function useMIDIAccess(requestOnMount = true) {
|
|
2089
|
+
const [midiAccess, setMidiAccess] = useState(null);
|
|
2090
|
+
const [error, setError] = useState(null);
|
|
2091
|
+
const request = useCallback(() => {
|
|
2092
|
+
if (navigator.requestMIDIAccess) navigator.requestMIDIAccess().then((access) => {
|
|
2093
|
+
setMidiAccess(access);
|
|
2094
|
+
setError(null);
|
|
2095
|
+
}).catch(() => {
|
|
2096
|
+
setError(PERMISSION_DENIED);
|
|
2097
|
+
});
|
|
2098
|
+
else setError(NOT_SUPPORTED);
|
|
2099
|
+
}, []);
|
|
2100
|
+
useEffect(() => {
|
|
2101
|
+
if (requestOnMount) request();
|
|
2102
|
+
}, [requestOnMount, request]);
|
|
2103
|
+
return {
|
|
2104
|
+
request,
|
|
2105
|
+
midiAccess,
|
|
2106
|
+
error
|
|
2107
|
+
};
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
//#endregion
|
|
2111
|
+
//#region src/hooks/useMIDIMessage.ts
|
|
2112
|
+
/**
|
|
2113
|
+
* Hooks for when you want to process MIDI events in more detail than useMIDIInput.
|
|
2114
|
+
* @category hooks
|
|
2115
|
+
*/
|
|
2116
|
+
function useMIDIMessage(midiAccess, onMIDIMessage) {
|
|
2117
|
+
useEffect(() => {
|
|
2118
|
+
if (!midiAccess) return;
|
|
2119
|
+
for (const input of midiAccess.inputs.values()) input.addEventListener("midimessage", onMIDIMessage);
|
|
2120
|
+
return () => {
|
|
2121
|
+
for (const input of midiAccess.inputs.values()) input.removeEventListener("midimessage", onMIDIMessage);
|
|
2122
|
+
};
|
|
2123
|
+
}, [midiAccess, onMIDIMessage]);
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
//#endregion
|
|
2127
|
+
//#region src/hooks/useMIDIInput.ts
|
|
2128
|
+
const MIDI_EVENT_TO_NUMBER = {
|
|
2129
|
+
NOTE_ON: 144,
|
|
2130
|
+
NOTE_OFF: 128,
|
|
2131
|
+
PITCH_BEND: 224
|
|
2132
|
+
};
|
|
2133
|
+
/**
|
|
2134
|
+
* Hooks for handling note on/off events. To be used with useMIDIAccess. Internally uses useMIDIMessage.
|
|
2135
|
+
* @category hooks
|
|
2136
|
+
*/
|
|
2137
|
+
function useMIDIInput(midiAccess, onNoteOnEvent, onNoteOffEvent, onPitchBendEvent) {
|
|
2138
|
+
useMIDIMessage(midiAccess, useCallback((event) => {
|
|
2139
|
+
if (!event.data) return;
|
|
2140
|
+
const kind = event.data[0] & 240;
|
|
2141
|
+
if (kind == MIDI_EVENT_TO_NUMBER.NOTE_OFF || kind == MIDI_EVENT_TO_NUMBER.NOTE_ON && event.data[2] == 0) onNoteOffEvent?.(event.data[1]);
|
|
2142
|
+
else if (kind == MIDI_EVENT_TO_NUMBER.NOTE_ON) onNoteOnEvent?.(event.data[1], event.data[2]);
|
|
2143
|
+
else if (kind == MIDI_EVENT_TO_NUMBER.PITCH_BEND) onPitchBendEvent?.(event.data[1], event.data[2]);
|
|
2144
|
+
}, [
|
|
2145
|
+
onNoteOffEvent,
|
|
2146
|
+
onNoteOnEvent,
|
|
2147
|
+
onPitchBendEvent
|
|
2148
|
+
]));
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
//#endregion
|
|
2152
|
+
export { AnimationCanvas, AnimationKnob, BlackKey, DecrementStepper, DragObserver, IncrementStepper, KeyLabel, Knob, NOT_SUPPORTED, NumberInput, PERMISSION_DENIED, Piano, SHORTCUTS, Scale, ScaleOption, Slider, SliderThumb, SliderTrack, Stepper, WheelObserver, WhiteKey, XYPad, XYPadArea, XYPadThumb, getNoteRangeArray, useAnimationFrame, useDrag, useDragWithElement, useEventListener, useInterval, useLongPress, useMIDIAccess, useMIDIInput, useMIDIMessage, useSliderContext };
|
|
2084
2153
|
//# sourceMappingURL=index.js.map
|