@tremolo-ui/react 0.0.12 → 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/cjs/index.js +320 -233
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/types/components/Knob/index.d.ts +12 -0
  4. package/dist/cjs/types/components/Knob/index.d.ts.map +1 -1
  5. package/dist/cjs/types/components/Piano/KeyLabel.d.ts +1 -3
  6. package/dist/cjs/types/components/Piano/KeyLabel.d.ts.map +1 -1
  7. package/dist/cjs/types/components/Piano/context.d.ts +24 -0
  8. package/dist/cjs/types/components/Piano/context.d.ts.map +1 -0
  9. package/dist/cjs/types/components/Piano/index.d.ts +9 -9
  10. package/dist/cjs/types/components/Piano/index.d.ts.map +1 -1
  11. package/dist/cjs/types/components/Piano/key.d.ts +10 -19
  12. package/dist/cjs/types/components/Piano/key.d.ts.map +1 -1
  13. package/dist/cjs/types/components/Slider/index.d.ts.map +1 -1
  14. package/dist/cjs/types/components/XYPad/index.d.ts.map +1 -1
  15. package/dist/cjs/types/hooks/useDrag.d.ts.map +1 -1
  16. package/dist/cjs/types/hooks/useDragWithElement.d.ts +16 -0
  17. package/dist/cjs/types/hooks/useDragWithElement.d.ts.map +1 -0
  18. package/dist/cjs/types/index.d.ts +1 -0
  19. package/dist/cjs/types/index.d.ts.map +1 -1
  20. package/dist/esm/index.js +319 -234
  21. package/dist/esm/index.js.map +1 -1
  22. package/dist/esm/types/components/Knob/index.d.ts +12 -0
  23. package/dist/esm/types/components/Knob/index.d.ts.map +1 -1
  24. package/dist/esm/types/components/Piano/KeyLabel.d.ts +1 -3
  25. package/dist/esm/types/components/Piano/KeyLabel.d.ts.map +1 -1
  26. package/dist/esm/types/components/Piano/context.d.ts +24 -0
  27. package/dist/esm/types/components/Piano/context.d.ts.map +1 -0
  28. package/dist/esm/types/components/Piano/index.d.ts +9 -9
  29. package/dist/esm/types/components/Piano/index.d.ts.map +1 -1
  30. package/dist/esm/types/components/Piano/key.d.ts +10 -19
  31. package/dist/esm/types/components/Piano/key.d.ts.map +1 -1
  32. package/dist/esm/types/components/Slider/index.d.ts.map +1 -1
  33. package/dist/esm/types/components/XYPad/index.d.ts.map +1 -1
  34. package/dist/esm/types/hooks/useDrag.d.ts.map +1 -1
  35. package/dist/esm/types/hooks/useDragWithElement.d.ts +16 -0
  36. package/dist/esm/types/hooks/useDragWithElement.d.ts.map +1 -0
  37. package/dist/esm/types/index.d.ts +1 -0
  38. package/dist/esm/types/index.d.ts.map +1 -1
  39. package/dist/styles/index.css +2 -0
  40. package/package.json +2 -1
  41. package/src/components/AnimationCanvas/index.tsx +3 -1
  42. package/src/components/Knob/index.tsx +14 -0
  43. package/src/components/Piano/KeyLabel.tsx +9 -5
  44. package/src/components/Piano/context.tsx +108 -0
  45. package/src/components/Piano/index.css +2 -0
  46. package/src/components/Piano/index.tsx +186 -114
  47. package/src/components/Piano/key.tsx +215 -0
  48. package/src/components/Slider/index.tsx +40 -52
  49. package/src/components/XYPad/index.tsx +64 -54
  50. package/src/hooks/useDrag.ts +6 -7
  51. package/src/hooks/useDragWithElement.ts +76 -0
  52. package/src/index.ts +1 -0
  53. package/dist/cjs/types/components/Piano/BlackKey.d.ts +0 -5
  54. package/dist/cjs/types/components/Piano/BlackKey.d.ts.map +0 -1
  55. package/dist/cjs/types/components/Piano/WhiteKey.d.ts +0 -7
  56. package/dist/cjs/types/components/Piano/WhiteKey.d.ts.map +0 -1
  57. package/dist/esm/types/components/Piano/BlackKey.d.ts +0 -5
  58. package/dist/esm/types/components/Piano/BlackKey.d.ts.map +0 -1
  59. package/dist/esm/types/components/Piano/WhiteKey.d.ts +0 -7
  60. package/dist/esm/types/components/Piano/WhiteKey.d.ts.map +0 -1
  61. package/src/components/Piano/BlackKey.tsx +0 -132
  62. package/src/components/Piano/WhiteKey.tsx +0 -134
  63. package/src/components/Piano/key.ts +0 -51
package/dist/esm/index.js CHANGED
@@ -108,7 +108,9 @@ function AnimationCanvas(_a) {
108
108
  memoCanvas.width = w * dpr;
109
109
  memoCanvas.height = h * dpr;
110
110
  memoContext.scale(1 / dpr, 1 / dpr);
111
- memoContext.drawImage(context.canvas, 0, 0);
111
+ if (canvas.width > 0 && canvas.height > 0) {
112
+ memoContext.drawImage(canvas, 0, 0);
113
+ }
112
114
  }
113
115
  setDprConfig(canvas, context, w, h, dpr);
114
116
  widthRef.current = w;
@@ -319,6 +321,13 @@ function isWhiteKey(note) {
319
321
  mod(n, 12) == 9 ||
320
322
  mod(n, 12) == 11);
321
323
  }
324
+ /**
325
+ * @param note noteNumber: 0 ~ 127 or noteName e.g. 'C3'
326
+ * @category MIDI
327
+ */
328
+ function isBlackKey(note) {
329
+ return !isWhiteKey(note);
330
+ }
322
331
 
323
332
  /**
324
333
  * This hook is user-land implementation of the experimental `useEffectEvent` hook.
@@ -399,8 +408,7 @@ function useDrag({ threshold = 1, onDrag, onDragStart, onDragEnd, }) {
399
408
  }
400
409
  if (Math.abs(deltaX) < threshold && Math.abs(deltaY) < threshold)
401
410
  return;
402
- if (onDrag)
403
- onDrag(screenX - dragStartX.current, screenY - dragStartY.current, deltaX, deltaY);
411
+ onDrag(screenX - dragStartX.current, screenY - dragStartY.current, deltaX, deltaY);
404
412
  }, [threshold, onDrag]);
405
413
  const refHandler = useRefCallbackEvent('touchmove', handleDrag, { passive: false }, [onDrag]);
406
414
  const pointerDownHandler = useCallback((event) => {
@@ -667,7 +675,7 @@ const Knob = forwardRef((_a, ref) => {
667
675
  elmRef.current = div;
668
676
  wheelRefCallback(div);
669
677
  touchMoveRefCallback(div);
670
- }, width: size, height: size, tabIndex: 0, "aria-valuenow": value, "aria-valuemin": min, "aria-valuemax": max, "aria-disabled": disabled, "aria-readonly": readonly, onPointerDown: (event) => {
678
+ }, width: size, height: size, tabIndex: 0, role: "slider", "aria-valuenow": value, "aria-valuemin": min, "aria-valuemax": max, "aria-disabled": disabled, "aria-readonly": readonly, onPointerDown: (event) => {
671
679
  pointerDownHandler(event);
672
680
  onPointerDown === null || onPointerDown === void 0 ? void 0 : onPointerDown(event);
673
681
  }, onDoubleClick: (event) => {
@@ -1057,21 +1065,73 @@ const NumberInput = forwardRef((_a, ref) => {
1057
1065
  tabIndex: 0, style: Object.assign({}, colors), "data-stepper": !!children, "data-variant": variant, children: [jsx(InternalInput, Object.assign({ ref: ref, readonly: readonly, disabled: disabled, selectWithFocus: selectWithFocus, blurOnEnter: blurOnEnter, clampValueOnBlur: clampValueOnBlur, wheel: wheel, keyboard: keyboard, className: className, onChange: onChange, onFocus: onFocus, onBlur: onBlur }, props)), children] }) }));
1058
1066
  });
1059
1067
 
1068
+ /**
1069
+ * @returns [refCallback, pointerDownHandler]
1070
+ */
1071
+ function useDragWithElement({ baseElementRef, onDrag, onDragStart, onDragEnd, }) {
1072
+ const dragged = useRef(false);
1073
+ const normalizedX = useRef(0);
1074
+ const normalizedY = useRef(0);
1075
+ const handleDrag = useCallback((event) => {
1076
+ if (!baseElementRef.current || !dragged.current) {
1077
+ return;
1078
+ }
1079
+ const isTouch = event instanceof TouchEvent;
1080
+ if (isTouch && event.cancelable)
1081
+ event.preventDefault();
1082
+ const { left, top, right, bottom } = baseElementRef.current.getBoundingClientRect();
1083
+ const mouseX = isTouch ? event.touches[0].clientX : event.clientX;
1084
+ const mouseY = isTouch ? event.touches[0].clientY : event.clientY;
1085
+ const nx = normalizeValue(mouseX, left, right);
1086
+ const ny = normalizeValue(mouseY, top, bottom);
1087
+ normalizedX.current = nx;
1088
+ normalizedY.current = ny;
1089
+ onDrag(nx, ny);
1090
+ }, [onDrag, baseElementRef]);
1091
+ const refHandler = useRefCallbackEvent('touchmove', handleDrag, { passive: false }, [onDrag]);
1092
+ const pointerDownHandler = useCallback((event) => {
1093
+ dragged.current = true;
1094
+ handleDrag(event);
1095
+ onDragStart === null || onDragStart === void 0 ? void 0 : onDragStart(normalizedX.current, normalizedY.current);
1096
+ }, [handleDrag, onDragStart]);
1097
+ useEventListener(globalThis.window, 'pointermove', handleDrag);
1098
+ useEventListener(globalThis.window, 'pointerup', () => {
1099
+ if (!dragged.current)
1100
+ return;
1101
+ dragged.current = false;
1102
+ onDragEnd === null || onDragEnd === void 0 ? void 0 : onDragEnd(normalizedX.current, normalizedY.current);
1103
+ });
1104
+ return [refHandler, pointerDownHandler];
1105
+ }
1106
+
1060
1107
  /** @category Piano */
1061
1108
  function KeyLabel(_a) {
1062
- var { label, className, wrapperClassName, wrapperStyle, __note, __index, __label } = _a, props = __rest(_a, ["label", "className", "wrapperClassName", "wrapperStyle", "__note", "__index", "__label"]);
1109
+ var { label, className, wrapperClassName, wrapperStyle, __note, __label } = _a, props = __rest(_a, ["label", "className", "wrapperClassName", "wrapperStyle", "__note", "__label"]);
1110
+ const noteRange = usePianoContext((s) => s.noteRange);
1111
+ const noteRangeArray = getNoteRangeArray(noteRange);
1112
+ const index = noteRangeArray.indexOf(__note);
1063
1113
  const content = label
1064
- ? label(__note, __index)
1065
- : __label && __label(__note, __index);
1114
+ ? label(__note, index)
1115
+ : __label && __label(__note, index);
1066
1116
  return ((content != undefined || content != null) && (jsx("div", { className: clsx('tremolo-piano-key-label-wrapper', wrapperClassName), style: wrapperStyle, children: jsx("div", Object.assign({ className: clsx('tremolo-piano-key-label', className) }, props, { children: content })) })));
1067
1117
  }
1068
1118
 
1069
1119
  /** @category Piano */
1070
1120
  const defaultWhiteKeyWidth = 40;
1071
1121
  /** @category Piano */
1072
- const WhiteKey = forwardRef((_a, ref) => {
1073
- var { width = defaultWhiteKeyWidth, height = '100%', bg, color, activeBg, activeColor, className, style, children, __glissando, __position, __note, __disabled, __index, __fill, __width, __onPlayNote, __onStopNote, __label, onContextMenu = (e) => e.preventDefault() } = _a, props = __rest(_a, ["width", "height", "bg", "color", "activeBg", "activeColor", "className", "style", "children", "__glissando", "__position", "__note", "__disabled", "__index", "__fill", "__width", "__onPlayNote", "__onStopNote", "__label", "onContextMenu"]);
1122
+ const defaultBlackKeyWidth = 26;
1123
+ const KeyImpl = forwardRef((_a, ref) => {
1124
+ var { keyType, noteNumber, width, height, bg, color, activeBg, activeColor, className, style, children, __width } = _a, props = __rest(_a, ["keyType", "noteNumber", "width", "height", "bg", "color", "activeBg", "activeColor", "className", "style", "children", "__width"]);
1074
1125
  const [played, setPlayed] = useState(false);
1126
+ const midiMax = usePianoContext((s) => s.midiMax);
1127
+ const fill = usePianoContext((s) => s.fill);
1128
+ // const glissando = usePianoContext(s => s.glissando)
1129
+ const onPlayNote = usePianoContext((s) => s.onPlayNote);
1130
+ const onStopNote = usePianoContext((s) => s.onStopNote);
1131
+ const label = usePianoContext((s) => s.label);
1132
+ const notePosition = usePianoContext((s) => s.notePosition);
1133
+ const position = notePosition(noteNumber);
1134
+ const disabled = noteNumber > midiMax;
1075
1135
  const colors = {
1076
1136
  '--bg': bg,
1077
1137
  '--color': color,
@@ -1081,22 +1141,22 @@ const WhiteKey = forwardRef((_a, ref) => {
1081
1141
  useImperativeHandle(ref, () => {
1082
1142
  return {
1083
1143
  play() {
1084
- if (__disabled)
1144
+ if (disabled)
1085
1145
  return;
1086
1146
  setPlayed(true);
1087
- if (__onPlayNote)
1088
- __onPlayNote(__note);
1147
+ if (onPlayNote)
1148
+ onPlayNote(noteNumber);
1089
1149
  },
1090
1150
  stop() {
1091
1151
  setPlayed(false);
1092
- if (__onStopNote)
1093
- __onStopNote(__note);
1152
+ if (onStopNote)
1153
+ onStopNote(noteNumber);
1094
1154
  },
1095
1155
  played() {
1096
1156
  return played;
1097
1157
  },
1098
1158
  };
1099
- }, [__disabled, __note, __onPlayNote, __onStopNote, played]);
1159
+ }, [disabled, noteNumber, onPlayNote, onStopNote, played]);
1100
1160
  let keyLabelProps = {};
1101
1161
  if (children != undefined) {
1102
1162
  React.Children.forEach(children, (child) => {
@@ -1113,112 +1173,82 @@ const WhiteKey = forwardRef((_a, ref) => {
1113
1173
  }
1114
1174
  });
1115
1175
  }
1116
- return (jsx("div", Object.assign({ className: clsx(`tremolo-piano-white-key`, className), "aria-disabled": __disabled, "data-active": played, style: Object.assign(Object.assign(Object.assign({}, colors), { left: __position, width: __fill ? __width : (width !== null && width !== void 0 ? width : __width), height: height }), style), onPointerDown: () => {
1117
- if (__disabled)
1118
- return;
1119
- setPlayed(true);
1120
- if (__onPlayNote)
1121
- __onPlayNote(__note);
1122
- }, onPointerEnter: () => {
1123
- if (__disabled)
1124
- return;
1125
- if (__glissando) {
1126
- setPlayed(true);
1127
- if (__onPlayNote)
1128
- __onPlayNote(__note);
1129
- }
1130
- }, onPointerUp: () => {
1131
- if (__disabled)
1132
- return;
1133
- setPlayed(false);
1134
- if (__onStopNote)
1135
- __onStopNote(__note);
1136
- }, onPointerLeave: () => {
1137
- if (__disabled)
1138
- return;
1139
- if (__glissando) {
1140
- setPlayed(false);
1141
- if (__onStopNote)
1142
- __onStopNote(__note);
1143
- }
1144
- }, onContextMenu: onContextMenu }, props, { children: jsx(KeyLabel, Object.assign({ __note: __note, __label: __label, __index: __index }, keyLabelProps)) })));
1176
+ return (jsx("div", Object.assign({ className: clsx(`tremolo-piano-${keyType}-key`, className), "aria-disabled": disabled, "data-active": played, style: Object.assign(Object.assign(Object.assign({}, colors), { left: position, width: fill ? __width : width, height: height }), style) }, props, { children: jsx(KeyLabel, Object.assign({ label: label, __note: noteNumber }, keyLabelProps)) })));
1177
+ });
1178
+ /** @category Piano */
1179
+ const WhiteKey = forwardRef((_a, ref) => {
1180
+ var { width = defaultWhiteKeyWidth, height = '100%', onContextMenu = (e) => e.preventDefault() } = _a, props = __rest(_a, ["width", "height", "onContextMenu"]);
1181
+ return (jsx(KeyImpl, Object.assign({ ref: ref, keyType: "white", width: width, height: height, onContextMenu: onContextMenu }, props)));
1145
1182
  });
1146
-
1147
1183
  /** @category Piano */
1148
1184
  const BlackKey = forwardRef((_a, ref) => {
1149
- var { width = defaultWhiteKeyWidth * 0.65, height = '60%', bg, color, activeBg, activeColor, className, style, children, __glissando, __position, __note, __disabled, __index, __fill, __width, __onPlayNote, __onStopNote, __label, onContextMenu = (e) => e.preventDefault() } = _a, props = __rest(_a, ["width", "height", "bg", "color", "activeBg", "activeColor", "className", "style", "children", "__glissando", "__position", "__note", "__disabled", "__index", "__fill", "__width", "__onPlayNote", "__onStopNote", "__label", "onContextMenu"]);
1150
- const [played, setPlayed] = useState(false);
1151
- const colors = {
1152
- '--bg': bg,
1153
- '--color': color,
1154
- '--active-bg': activeBg,
1155
- '--active-color': activeColor,
1185
+ var { width = defaultBlackKeyWidth, height = '60%', onContextMenu = (e) => e.preventDefault() } = _a, props = __rest(_a, ["width", "height", "onContextMenu"]);
1186
+ return (jsx(KeyImpl, Object.assign({ ref: ref, keyType: "black", width: width, height: height, onContextMenu: onContextMenu }, props)));
1187
+ });
1188
+
1189
+ const createPianoStore = (initProps) => {
1190
+ const noteRange = (initProps === null || initProps === void 0 ? void 0 : initProps.noteRange) || { first: 0};
1191
+ const DEFAULT_PROPS = {
1192
+ noteRange: { first: 0, last: 127 },
1193
+ glissando: true,
1194
+ midiMax: 127,
1195
+ fill: false,
1196
+ label: () => undefined,
1197
+ onPlayNote: () => { },
1198
+ onStopNote: () => { },
1156
1199
  };
1157
- useImperativeHandle(ref, () => {
1158
- return {
1159
- play() {
1160
- if (__disabled)
1161
- return;
1162
- setPlayed(true);
1163
- if (__onPlayNote)
1164
- __onPlayNote(__note);
1165
- },
1166
- stop() {
1167
- setPlayed(false);
1168
- if (__onStopNote)
1169
- __onStopNote(__note);
1170
- },
1171
- played() {
1172
- return played;
1173
- },
1174
- };
1175
- }, [__disabled, __note, __onPlayNote, __onStopNote, played]);
1176
- let keyLabelProps = {};
1177
- if (children != undefined) {
1178
- React.Children.forEach(children, (child) => {
1179
- if (React.isValidElement(child)) {
1180
- if (child.type == KeyLabel) {
1181
- keyLabelProps = child.props;
1182
- }
1183
- else {
1184
- throw new Error('only <KeyLabel>');
1185
- }
1186
- }
1187
- else {
1188
- throw new Error('children is an invalid element.');
1189
- }
1190
- });
1200
+ return createStore()(() => (Object.assign(Object.assign(Object.assign({}, DEFAULT_PROPS), initProps), { notePosition: (note) => {
1201
+ const pitchPositions = {
1202
+ C: 0,
1203
+ 'C#': 1,
1204
+ D: 1,
1205
+ 'D#': 2,
1206
+ E: 2,
1207
+ F: 3,
1208
+ 'F#': 4,
1209
+ G: 4,
1210
+ 'G#': 5,
1211
+ A: 5,
1212
+ 'A#': 6,
1213
+ B: 6,
1214
+ };
1215
+ const whiteNoteWidth = defaultWhiteKeyWidth;
1216
+ const blackNoteWidth = defaultBlackKeyWidth;
1217
+ const padding = 1;
1218
+ const targetNoteKey = noteKey(note);
1219
+ const firstNoteKey = noteKey(noteRange.first);
1220
+ const octave = Math.floor((note - noteRange.first) / 12);
1221
+ const octaveOffset = noteKeys.indexOf(firstNoteKey) > noteKeys.indexOf(targetNoteKey) ? 1 : 0;
1222
+ const w = whiteNoteWidth + padding;
1223
+ const pos = pitchPositions[targetNoteKey] - pitchPositions[firstNoteKey];
1224
+ const blackKeyOffset = isBlackKey(note) ? blackNoteWidth / 2 : 0;
1225
+ return pos * w + (octave + octaveOffset) * 7 * w - blackKeyOffset;
1226
+ } })));
1227
+ };
1228
+ const PianoContext = createContext(null);
1229
+ function PianoProvider(_a) {
1230
+ var { children } = _a, props = __rest(_a, ["children"]);
1231
+ const storeRef = useRef(null);
1232
+ if (!storeRef.current) {
1233
+ storeRef.current = createPianoStore(props);
1191
1234
  }
1192
- return (jsx("div", Object.assign({ className: clsx(`tremolo-piano-black-key`, className), "aria-disabled": __disabled, "data-active": played, style: Object.assign(Object.assign(Object.assign({}, colors), { left: __position, width: __fill ? __width : (width !== null && width !== void 0 ? width : __width), height: height }), style), onPointerDown: () => {
1193
- if (__disabled)
1194
- return;
1195
- setPlayed(true);
1196
- if (__onPlayNote)
1197
- __onPlayNote(__note);
1198
- }, onPointerEnter: () => {
1199
- if (__disabled)
1200
- return;
1201
- if (__glissando) {
1202
- setPlayed(true);
1203
- if (__onPlayNote)
1204
- __onPlayNote(__note);
1205
- }
1206
- }, onPointerUp: () => {
1207
- if (__disabled)
1208
- return;
1209
- setPlayed(false);
1210
- if (__onStopNote)
1211
- __onStopNote(__note);
1212
- }, onPointerLeave: () => {
1213
- if (__disabled)
1214
- return;
1215
- if (__glissando) {
1216
- setPlayed(false);
1217
- if (__onStopNote)
1218
- __onStopNote(__note);
1219
- }
1220
- }, onContextMenu: onContextMenu }, props, { children: jsx(KeyLabel, Object.assign({ __note: __note, __label: __label, __index: __index }, keyLabelProps)) })));
1221
- });
1235
+ useEffect(() => {
1236
+ if (storeRef.current) {
1237
+ storeRef.current.setState(props);
1238
+ }
1239
+ else {
1240
+ storeRef.current = createPianoStore(props);
1241
+ }
1242
+ }, [props]);
1243
+ return (jsx(PianoContext.Provider, { value: storeRef.current, children: children }));
1244
+ }
1245
+ /** @category Piano */
1246
+ function usePianoContext(selector) {
1247
+ const store = useContext(PianoContext);
1248
+ if (!store)
1249
+ throw new Error('Missing PianoContext.Provider in the tree');
1250
+ return useStore(store, selector);
1251
+ }
1222
1252
 
1223
1253
  /** @category Piano */
1224
1254
  const SHORTCUTS = {
@@ -1245,66 +1275,121 @@ const SHORTCUTS = {
1245
1275
  },
1246
1276
  };
1247
1277
 
1278
+ /**
1279
+ * Piano component
1280
+ *
1281
+ * TODO:
1282
+ * - add scale highlight
1283
+ */
1284
+ /** @category Piano */
1285
+ function getNoteRangeArray(noteRange) {
1286
+ return Array.from({ length: noteRange.last - noteRange.first + 1 }, (_, i) => i + noteRange.first);
1287
+ }
1248
1288
  /**
1249
1289
  * Customizable piano component.
1250
1290
  * @category Piano
1251
1291
  */
1252
1292
  function Piano(_a) {
1253
- var { noteRange, glissando = true, midiMax = 127, keyboardShortcuts, fill = false, height = fill ? '100%' : 160, style, className, onPlayNote, onStopNote, label, children, onPointerUp, onPointerDown } = _a, props = __rest(_a, ["noteRange", "glissando", "midiMax", "keyboardShortcuts", "fill", "height", "style", "className", "onPlayNote", "onStopNote", "label", "children", "onPointerUp", "onPointerDown"]);
1293
+ var { noteRange, glissando = true, midiMax = 127, keyboardShortcuts, fill = false, height = fill ? '100%' : 160, whiteNoteWidth: _whiteNoteWidth = defaultWhiteKeyWidth, style, className, onPlayNote, onStopNote, label, children, onPointerDown } = _a, props = __rest(_a, ["noteRange", "glissando", "midiMax", "keyboardShortcuts", "fill", "height", "whiteNoteWidth", "style", "className", "onPlayNote", "onStopNote", "label", "children", "onPointerDown"]);
1254
1294
  // -- state and ref ---
1255
- const [pressed, setPressed] = useState(false);
1256
- const [whiteNoteWidth, setWhiteNoteWidth] = useState(-1);
1295
+ const [whiteNoteWidth, setWhiteNoteWidth] = useState(_whiteNoteWidth);
1257
1296
  const keyRefs = useRef([]);
1258
1297
  for (let i = 0; i < noteRange.last - noteRange.first + 1; i++) {
1259
1298
  keyRefs.current[i] = createRef();
1260
1299
  }
1261
1300
  const pianoRef = useRef(null);
1301
+ const hitKeyIndex = useRef(-1);
1262
1302
  // --- interpret props ---
1263
- const noteRangeArray = Array.from({ length: noteRange.last - noteRange.first + 1 }, (_, i) => i + noteRange.first);
1264
- const whiteNoteCount = noteRangeArray.filter((v) => isWhiteKey(v)).length;
1265
- let whiteKeyProps = {}, blackKeyProps = {};
1266
- if (children != undefined) {
1267
- React.Children.forEach(children, (child) => {
1268
- if (React.isValidElement(child)) {
1269
- if (child.type == WhiteKey) {
1270
- whiteKeyProps = child.props;
1271
- }
1272
- else if (child.type == BlackKey) {
1273
- blackKeyProps = child.props;
1274
- }
1275
- else {
1276
- throw new Error('only <WhiteKey> or <BlackKey>');
1303
+ const noteRangeArray = getNoteRangeArray(noteRange);
1304
+ const whiteNotes = noteRangeArray.filter((v) => isWhiteKey(v));
1305
+ const blackNotes = noteRangeArray.filter((v) => isBlackKey(v));
1306
+ const whiteNoteCount = whiteNotes.length;
1307
+ const padding = 1;
1308
+ const staticWidth = (whiteNoteWidth + padding) * whiteNoteCount;
1309
+ // const blackNoteShiftPercent = 0.3
1310
+ // TODO: if fill setWhiteNoteWidth
1311
+ // useMemo
1312
+ // __width
1313
+ const childrenWithProps = useMemo(() => {
1314
+ return (children &&
1315
+ React.Children.map(children, (child, index) => {
1316
+ if (React.isValidElement(child)) {
1317
+ const props = { ref: keyRefs.current[index] };
1318
+ return React.cloneElement(child, props);
1277
1319
  }
1278
- }
1279
- else {
1280
- throw new Error('children is an invalid element.');
1281
- }
1282
- });
1283
- }
1320
+ return child;
1321
+ }));
1322
+ }, [children]);
1284
1323
  // --- internal functions ---
1285
- function notePosition(note) {
1324
+ const notePosition = useCallback((note) => {
1286
1325
  const pitchPositions = {
1287
1326
  C: 0,
1288
- 'C#': 0.55,
1327
+ 'C#': 1,
1289
1328
  D: 1,
1290
- 'D#': 1.8,
1329
+ 'D#': 2,
1291
1330
  E: 2,
1292
1331
  F: 3,
1293
- 'F#': 3.5,
1332
+ 'F#': 4,
1294
1333
  G: 4,
1295
- 'G#': 4.7,
1334
+ 'G#': 5,
1296
1335
  A: 5,
1297
- 'A#': 5.85,
1336
+ 'A#': 6,
1298
1337
  B: 6,
1299
1338
  };
1339
+ const whiteNoteWidth = defaultWhiteKeyWidth;
1340
+ const blackNoteWidth = defaultBlackKeyWidth;
1341
+ const padding = 1;
1300
1342
  const targetNoteKey = noteKey(note);
1301
1343
  const firstNoteKey = noteKey(noteRange.first);
1302
- const pos = pitchPositions[targetNoteKey] - pitchPositions[firstNoteKey];
1303
1344
  const octave = Math.floor((note - noteRange.first) / 12);
1304
1345
  const octaveOffset = noteKeys.indexOf(firstNoteKey) > noteKeys.indexOf(targetNoteKey) ? 1 : 0;
1305
- const length = whiteNoteWidth + 3;
1306
- return pos * length + (octave + octaveOffset) * 7 * length;
1307
- }
1346
+ const w = whiteNoteWidth + padding;
1347
+ const pos = pitchPositions[targetNoteKey] - pitchPositions[firstNoteKey];
1348
+ const blackKeyOffset = isBlackKey(note) ? blackNoteWidth / 2 : 0;
1349
+ return pos * w + (octave + octaveOffset) * 7 * w - blackKeyOffset;
1350
+ }, [noteRange.first]);
1351
+ const getHitKeyIndex = useCallback((x, y) => {
1352
+ if (!pianoRef.current)
1353
+ return -1;
1354
+ const containerHeight = pianoRef.current.clientHeight;
1355
+ const notes = [...blackNotes, ...whiteNotes];
1356
+ for (let i = 0; i < notes.length; i++) {
1357
+ const note = notes[i];
1358
+ const pos = notePosition(note);
1359
+ const w = isWhiteKey(note) ? defaultWhiteKeyWidth : defaultBlackKeyWidth;
1360
+ const h = isWhiteKey(note) ? containerHeight : containerHeight * 0.6;
1361
+ if (pos <= x && x < pos + w && 0 <= y && y < h) {
1362
+ return note;
1363
+ }
1364
+ }
1365
+ return -1;
1366
+ }, [blackNotes, notePosition, whiteNotes]);
1367
+ // TODO: 単一のポインターに対しては、useDragで対応可能だが、
1368
+ // マルチタッチに対しては、TouchEventを使う必要がありそう
1369
+ // 取り敢えず、シングルタッチだけ対応
1370
+ const onDrag = useCallback((perX, perY) => {
1371
+ var _a, _b, _c, _d;
1372
+ if (!pianoRef.current)
1373
+ return;
1374
+ const x = perX * staticWidth;
1375
+ const y = perY * pianoRef.current.clientHeight;
1376
+ const note = getHitKeyIndex(x, y);
1377
+ const index = noteRangeArray.indexOf(note);
1378
+ if (index == -1)
1379
+ return;
1380
+ if (hitKeyIndex.current != index) {
1381
+ (_b = (_a = keyRefs.current[hitKeyIndex.current]) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.stop();
1382
+ (_d = (_c = keyRefs.current[index]) === null || _c === void 0 ? void 0 : _c.current) === null || _d === void 0 ? void 0 : _d.play();
1383
+ hitKeyIndex.current = index;
1384
+ }
1385
+ }, [getHitKeyIndex, noteRangeArray, staticWidth]);
1386
+ const onDragEnd = useCallback(() => {
1387
+ var _a, _b, _c, _d;
1388
+ if ((_b = (_a = keyRefs.current[hitKeyIndex.current]) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.played()) {
1389
+ (_d = (_c = keyRefs.current[hitKeyIndex.current]) === null || _c === void 0 ? void 0 : _c.current) === null || _d === void 0 ? void 0 : _d.stop();
1390
+ }
1391
+ hitKeyIndex.current = -1;
1392
+ }, [keyRefs]);
1308
1393
  // --- hooks ---
1309
1394
  useEffect(() => {
1310
1395
  if (fill && pianoRef.current) {
@@ -1313,7 +1398,7 @@ function Piano(_a) {
1313
1398
  throw new Error("doesn't have a parent element.");
1314
1399
  const resizeObserver = new ResizeObserver(() => {
1315
1400
  const w = pianoRef.current.clientWidth;
1316
- setWhiteNoteWidth(w / whiteNoteCount - 3);
1401
+ setWhiteNoteWidth(w / whiteNoteCount - padding);
1317
1402
  });
1318
1403
  resizeObserver.observe(parent);
1319
1404
  return () => {
@@ -1321,9 +1406,14 @@ function Piano(_a) {
1321
1406
  };
1322
1407
  }
1323
1408
  else {
1324
- setWhiteNoteWidth((whiteKeyProps === null || whiteKeyProps === void 0 ? void 0 : whiteKeyProps.width) || defaultWhiteKeyWidth);
1409
+ setWhiteNoteWidth(_whiteNoteWidth);
1325
1410
  }
1326
- }, [fill, whiteKeyProps === null || whiteKeyProps === void 0 ? void 0 : whiteKeyProps.width, whiteNoteCount]);
1411
+ }, [fill, _whiteNoteWidth, whiteNoteCount]);
1412
+ const [touchMoveRefCallback, pointerDownHandler] = useDragWithElement({
1413
+ baseElementRef: pianoRef,
1414
+ onDrag: onDrag,
1415
+ onDragEnd: onDragEnd,
1416
+ });
1327
1417
  useEventListener(globalThis.window, 'keydown', (e) => {
1328
1418
  var _a, _b, _c, _d;
1329
1419
  if (e.repeat)
@@ -1348,15 +1438,14 @@ function Piano(_a) {
1348
1438
  (_b = (_a = keyRefs.current[index]) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.stop();
1349
1439
  }
1350
1440
  });
1351
- return (jsx("div", Object.assign({ ref: pianoRef, className: clsx('tremolo-piano', className), style: Object.assign({ width: fill ? '100%' : (whiteNoteWidth + 3) * whiteNoteCount, height: height }, style), onPointerDown: (e) => {
1352
- setPressed(true);
1353
- onPointerDown === null || onPointerDown === void 0 ? void 0 : onPointerDown(e);
1354
- }, onPointerUp: (e) => {
1355
- setPressed(false);
1356
- onPointerUp === null || onPointerUp === void 0 ? void 0 : onPointerUp(e);
1357
- } }, props, { children: noteRangeArray.map((note, index) => {
1358
- return isWhiteKey(note) ? (jsx(WhiteKey, Object.assign({ ref: keyRefs.current[index], __glissando: glissando && pressed, __position: notePosition(note), __note: note, __disabled: note > midiMax, __index: index, __fill: fill, __width: whiteNoteWidth, __onPlayNote: onPlayNote, __onStopNote: onStopNote, __label: label }, whiteKeyProps), note)) : (jsx(BlackKey, Object.assign({ ref: keyRefs.current[index], __glissando: glissando && pressed, __position: notePosition(note), __note: note, __disabled: note > midiMax, __index: index, __fill: fill, __width: whiteNoteWidth * 0.65, __onPlayNote: onPlayNote, __onStopNote: onStopNote, __label: label }, blackKeyProps), note));
1359
- }) })));
1441
+ return (jsx(PianoProvider, { noteRange: noteRange, glissando: glissando, midiMax: midiMax, fill: fill, onPlayNote: onPlayNote, onStopNote: onStopNote, label: label, children: jsx("div", Object.assign({ ref: (div) => {
1442
+ pianoRef.current = div;
1443
+ touchMoveRefCallback(div);
1444
+ }, className: clsx('tremolo-piano', className), style: Object.assign({ width: fill ? '100%' : staticWidth, height: height }, style), onPointerDown: (event) => {
1445
+ pointerDownHandler(event);
1446
+ onPointerDown === null || onPointerDown === void 0 ? void 0 : onPointerDown(event);
1447
+ } }, props, { children: childrenWithProps ||
1448
+ getNoteRangeArray(noteRange).map((note, index) => isWhiteKey(note) ? (jsx(WhiteKey, { ref: keyRefs.current[index], noteNumber: note }, note)) : (jsx(BlackKey, { ref: keyRefs.current[index], noteNumber: note }, note))) })) }));
1360
1449
  }
1361
1450
 
1362
1451
  const createSliderStore = (initProps) => {
@@ -1527,7 +1616,6 @@ const Slider = forwardRef((_a, ref) => {
1527
1616
  // -- state and ref ---
1528
1617
  const trackElementRef = useRef(null);
1529
1618
  const thumbRef = useRef(null);
1530
- const thumbDragged = useRef(false);
1531
1619
  // --- interpret props ---
1532
1620
  const p = toFixed$1(normalizeValue(value, min, max, skew) * 100);
1533
1621
  const rev = toFixed$1(100 - p);
@@ -1563,29 +1651,14 @@ const Slider = forwardRef((_a, ref) => {
1563
1651
  });
1564
1652
  }
1565
1653
  // --- internal functions ---
1566
- const handleValue = (event) => {
1567
- if (!trackElementRef.current ||
1568
- !thumbDragged.current ||
1569
- !onChange ||
1570
- readonly) {
1654
+ const onDrag = useCallback((nx, ny) => {
1655
+ if (!onChange || readonly)
1571
1656
  return;
1572
- }
1573
- const isTouch = event instanceof TouchEvent;
1574
- if (isTouch && event.cancelable)
1575
- event.preventDefault();
1576
- if (bodyNoSelect)
1577
- addNoSelect();
1578
- const { left: x1, top: y1, right: x2, bottom: y2, } = trackElementRef.current.getBoundingClientRect();
1579
- const mouseX = isTouch ? event.touches[0].clientX : event.clientX;
1580
- const mouseY = isTouch ? event.touches[0].clientY : event.clientY;
1581
- const n = vertical
1582
- ? normalizeValue(mouseY, y1, y2)
1583
- : normalizeValue(mouseX, x1, x2);
1657
+ const n = vertical ? ny : nx;
1584
1658
  const v = rawValue(displayReversed ? 1 - n : n, min, max, skew);
1585
1659
  const v2 = clamp(stepValue(v, step), min, max);
1586
1660
  onChange(v2);
1587
- return v2;
1588
- };
1661
+ }, [displayReversed, max, min, onChange, readonly, skew, step, vertical]);
1589
1662
  const updateValueByEvent = useCallback((eventType, x) => {
1590
1663
  let newValue;
1591
1664
  if (eventType == 'normalized') {
@@ -1610,7 +1683,26 @@ const Slider = forwardRef((_a, ref) => {
1610
1683
  }
1611
1684
  }, [keyboard, onChange, readonly, reverse, updateValueByEvent]);
1612
1685
  // --- hooks ---
1613
- const touchMoveRefCallback = useRefCallbackEvent('touchmove', handleValue, { passive: false }, [min, max, skew, step, vertical, reverse, onChange, readonly]);
1686
+ const [touchMoveRefCallback, pointerDownHandler] = useDragWithElement({
1687
+ baseElementRef: trackElementRef,
1688
+ onDrag: onDrag,
1689
+ onDragStart: (nx, ny) => {
1690
+ var _a;
1691
+ if (readonly)
1692
+ return;
1693
+ if (bodyNoSelect)
1694
+ addNoSelect();
1695
+ (_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.focus();
1696
+ onDragStart === null || onDragStart === void 0 ? void 0 : onDragStart(clamp(stepValue(rawValue(vertical ? ny : nx, min, max, skew), step), min, max));
1697
+ },
1698
+ onDragEnd: (nx, ny) => {
1699
+ if (readonly)
1700
+ return;
1701
+ if (bodyNoSelect)
1702
+ removeNoSelect();
1703
+ onDragEnd === null || onDragEnd === void 0 ? void 0 : onDragEnd(clamp(stepValue(rawValue(vertical ? ny : nx, min, max, skew), step), min, max));
1704
+ },
1705
+ });
1614
1706
  const wheelRefCallback = useRefCallbackEvent('wheel', (event) => {
1615
1707
  if (!wheel || !onChange || readonly)
1616
1708
  return;
@@ -1630,17 +1722,6 @@ const Slider = forwardRef((_a, ref) => {
1630
1722
  }, {
1631
1723
  passive: false,
1632
1724
  }, [wheel, vertical, readonly, onChange, updateValueByEvent]);
1633
- useEventListener(globalThis.window, 'mousemove', (event) => {
1634
- handleValue(event);
1635
- });
1636
- useEventListener(globalThis.window, 'mouseup', () => {
1637
- if (!thumbDragged.current || readonly)
1638
- return;
1639
- thumbDragged.current = false;
1640
- onDragEnd === null || onDragEnd === void 0 ? void 0 : onDragEnd(value);
1641
- if (bodyNoSelect)
1642
- removeNoSelect();
1643
- });
1644
1725
  useImperativeHandle(ref, () => {
1645
1726
  return {
1646
1727
  focus() {
@@ -1657,10 +1738,7 @@ const Slider = forwardRef((_a, ref) => {
1657
1738
  wheelRefCallback(div);
1658
1739
  touchMoveRefCallback(div);
1659
1740
  }, tabIndex: -1, role: "slider", "aria-valuenow": value, "aria-valuemin": min, "aria-valuemax": max, "aria-orientation": vertical ? 'vertical' : 'horizontal', "aria-disabled": disabled, "aria-readonly": readonly, style: Object.assign({ margin: `calc(${styleHelper((_b = thumbProps === null || thumbProps === void 0 ? void 0 : thumbProps.size) !== null && _b !== void 0 ? _b : 22)} / 2)` }, style), onPointerDown: (event) => {
1660
- thumbDragged.current = true;
1661
- const v = handleValue(event);
1662
- if (!readonly)
1663
- onDragStart === null || onDragStart === void 0 ? void 0 : onDragStart(v !== null && v !== void 0 ? v : value);
1741
+ pointerDownHandler(event);
1664
1742
  onPointerDown === null || onPointerDown === void 0 ? void 0 : onPointerDown(event);
1665
1743
  }, onKeyDown: (event) => {
1666
1744
  handleKeyDown(event);
@@ -1743,13 +1821,13 @@ const XYPad = forwardRef((_a, ref) => {
1743
1821
  // -- state and ref ---
1744
1822
  const areaElementRef = useRef(null);
1745
1823
  const thumbRef = useRef(null);
1746
- const thumbDragged = useRef(false);
1747
1824
  // --- interpret props ---
1748
1825
  const nx = normalizeValue(x.value, x.min, x.max, x.skew);
1749
1826
  const ny = normalizeValue(y.value, y.min, y.max, y.skew);
1750
1827
  const percentX = toFixed$1((x.reverse ? 1 - nx : nx) * 100);
1751
1828
  const percentY = toFixed$1((y.reverse ? 1 - ny : ny) * 100);
1752
- let areaProps = {}, thumbProps = {};
1829
+ let areaProps = {};
1830
+ let thumbProps = {};
1753
1831
  if (children != undefined) {
1754
1832
  React.Children.forEach(children, (child) => {
1755
1833
  if (React.isValidElement(child)) {
@@ -1769,29 +1847,28 @@ const XYPad = forwardRef((_a, ref) => {
1769
1847
  });
1770
1848
  }
1771
1849
  // --- internal functions ---
1772
- const handleValue = (event) => {
1773
- if (!areaElementRef.current ||
1774
- !thumbDragged.current ||
1775
- !onChange ||
1776
- readonly)
1850
+ const onDrag = useCallback((nx, ny) => {
1851
+ if (!onChange || readonly)
1777
1852
  return;
1778
- const isTouch = event instanceof TouchEvent;
1779
- if (isTouch && event.cancelable)
1780
- event.preventDefault();
1781
- if (bodyNoSelect)
1782
- addNoSelect();
1783
- const { left: x1, top: y1, right: x2, bottom: y2, } = areaElementRef.current.getBoundingClientRect();
1784
- const mouseX = isTouch ? event.touches[0].clientX : event.clientX;
1785
- const mouseY = isTouch ? event.touches[0].clientY : event.clientY;
1786
- const nx = normalizeValue(mouseX, x1, x2);
1787
- const ny = normalizeValue(mouseY, y1, y2);
1788
1853
  const vx = rawValue(x.reverse ? 1 - nx : nx, x.min, x.max, x.skew);
1789
1854
  const vy = rawValue(y.reverse ? 1 - ny : ny, y.min, y.max, y.skew);
1790
1855
  const newX = clamp(stepValue(vx, x.step), x.min, x.max);
1791
1856
  const newY = clamp(stepValue(vy, y.step), y.min, y.max);
1792
1857
  onChange(newX, newY);
1793
- return [newX, newY];
1794
- };
1858
+ }, [
1859
+ onChange,
1860
+ readonly,
1861
+ x.max,
1862
+ x.min,
1863
+ x.reverse,
1864
+ x.skew,
1865
+ x.step,
1866
+ y.max,
1867
+ y.min,
1868
+ y.reverse,
1869
+ y.skew,
1870
+ y.step,
1871
+ ]);
1795
1872
  const updateValueByEvent = useCallback((eventType, target, x) => {
1796
1873
  let v;
1797
1874
  if (eventType == 'normalized') {
@@ -1828,7 +1905,30 @@ const XYPad = forwardRef((_a, ref) => {
1828
1905
  }
1829
1906
  }, [onChange, readonly, x, y, updateValueByEvent]);
1830
1907
  // --- hooks ---
1831
- const touchMoveRefCallback = useRefCallbackEvent('touchmove', handleValue, { passive: false }, [x, onChange, readonly]);
1908
+ const [touchMoveRefCallback, pointerDownHandler] = useDragWithElement({
1909
+ baseElementRef: areaElementRef,
1910
+ onDrag: onDrag,
1911
+ onDragStart: (nx, ny) => {
1912
+ var _a;
1913
+ if (readonly)
1914
+ return;
1915
+ if (bodyNoSelect)
1916
+ addNoSelect();
1917
+ (_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.focus();
1918
+ const valueX = clamp(stepValue(rawValue(nx, x.min, x.max, x.skew), x.step), x.min, x.max);
1919
+ const valueY = clamp(stepValue(rawValue(ny, y.min, y.max, y.skew), y.step), y.min, y.max);
1920
+ onDragStart === null || onDragStart === void 0 ? void 0 : onDragStart(valueX, valueY);
1921
+ },
1922
+ onDragEnd: (nx, ny) => {
1923
+ if (readonly)
1924
+ return;
1925
+ if (bodyNoSelect)
1926
+ removeNoSelect();
1927
+ const valueX = clamp(stepValue(rawValue(nx, x.min, x.max, x.skew), x.step), x.min, x.max);
1928
+ const valueY = clamp(stepValue(rawValue(ny, y.min, y.max, y.skew), y.step), y.min, y.max);
1929
+ onDragEnd === null || onDragEnd === void 0 ? void 0 : onDragEnd(valueX, valueY);
1930
+ },
1931
+ });
1832
1932
  const wheelRefCallback = useRefCallbackEvent('wheel', (event) => {
1833
1933
  if (!onChange || readonly)
1834
1934
  return;
@@ -1851,17 +1951,6 @@ const XYPad = forwardRef((_a, ref) => {
1851
1951
  }, {
1852
1952
  passive: false,
1853
1953
  }, [x, y, onChange, readonly, updateValueByEvent]);
1854
- useEventListener(globalThis.window, 'mousemove', (event) => {
1855
- handleValue(event);
1856
- });
1857
- useEventListener(globalThis.window, 'mouseup', () => {
1858
- if (!thumbDragged.current || readonly)
1859
- return;
1860
- thumbDragged.current = false;
1861
- onDragEnd === null || onDragEnd === void 0 ? void 0 : onDragEnd(x.value, y.value);
1862
- if (bodyNoSelect)
1863
- removeNoSelect();
1864
- });
1865
1954
  useImperativeHandle(ref, () => {
1866
1955
  return {
1867
1956
  focus() {
@@ -1880,11 +1969,7 @@ const XYPad = forwardRef((_a, ref) => {
1880
1969
  wheelRefCallback(div);
1881
1970
  touchMoveRefCallback(div);
1882
1971
  }, tabIndex: -1, "aria-disabled": disabled, "aria-readonly": readonly, style: Object.assign({ margin: `calc(${styleHelper((_b = thumbProps === null || thumbProps === void 0 ? void 0 : thumbProps.size) !== null && _b !== void 0 ? _b : 22)} / 2)`, cursor: readonly ? 'not-allowed' : 'pointer', WebkitTapHighlightColor: 'transparent' }, style), onPointerDown: (event) => {
1883
- var _a, _b;
1884
- thumbDragged.current = true;
1885
- const v = handleValue(event);
1886
- if (!readonly)
1887
- onDragStart === null || onDragStart === void 0 ? void 0 : onDragStart((_a = v === null || v === void 0 ? void 0 : v[0]) !== null && _a !== void 0 ? _a : x.value, (_b = v === null || v === void 0 ? void 0 : v[1]) !== null && _b !== void 0 ? _b : y.value);
1972
+ pointerDownHandler(event);
1888
1973
  onPointerDown === null || onPointerDown === void 0 ? void 0 : onPointerDown(event);
1889
1974
  }, onKeyDown: (event) => {
1890
1975
  handleKeyDown(event);
@@ -1916,5 +2001,5 @@ const useAnimationFrame = (callback = () => { }, deps = []) => {
1916
2001
  }, [loop]);
1917
2002
  };
1918
2003
 
1919
- export { AnimationCanvas, AnimationKnob, BlackKey, DecrementStepper, DragObserver, IncrementStepper, KeyLabel, Knob, NumberInput, Piano, SHORTCUTS, Scale, ScaleOption, Slider, SliderThumb, SliderTrack, Stepper, WheelObserver, WhiteKey, XYPad, XYPadArea, XYPadThumb, useAnimationFrame, useDrag, useEventListener, useInterval, useLongPress, useSliderContext };
2004
+ export { AnimationCanvas, AnimationKnob, BlackKey, DecrementStepper, DragObserver, IncrementStepper, KeyLabel, Knob, NumberInput, Piano, SHORTCUTS, Scale, ScaleOption, Slider, SliderThumb, SliderTrack, Stepper, WheelObserver, WhiteKey, XYPad, XYPadArea, XYPadThumb, getNoteRangeArray, useAnimationFrame, useDrag, useDragWithElement, useEventListener, useInterval, useLongPress, useSliderContext };
1920
2005
  //# sourceMappingURL=index.js.map