solid-pianoroll 0.0.22 → 0.0.24

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/esm/index.js CHANGED
@@ -27,9 +27,9 @@ function styleInject(css, ref) {
27
27
  }
28
28
  }
29
29
 
30
- var css_248z$5 = ".PianoRoll-module_PianoRoll__rkRYx {\n box-sizing: border-box;\n display: flex;\n overflow: hidden;\n flex-direction: column;\n height: 100%;\n}\n\n.PianoRoll-module_PianoRollContainer__Oyxr7 {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n\n.PianoRoll-module_PianoRollLeftColumn__S6SZQ {\n display: flex;\n width: 30%;\n max-width: 250px;\n}\n";
31
- var styles$5 = {"PianoRoll":"PianoRoll-module_PianoRoll__rkRYx","PianoRollContainer":"PianoRoll-module_PianoRollContainer__Oyxr7","PianoRollLeftColumn":"PianoRoll-module_PianoRollLeftColumn__S6SZQ"};
32
- styleInject(css_248z$5);
30
+ var css_248z$6 = ".PianoRoll-module_PianoRoll__rkRYx {\n box-sizing: border-box;\n display: flex;\n overflow: hidden;\n flex-direction: column;\n height: 100%;\n user-select: none;\n}\n\n.PianoRoll-module_PianoRollContainer__Oyxr7 {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n\n.PianoRoll-module_PianoRollLeftColumn__S6SZQ {\n display: flex;\n overflow: hidden;\n flex-direction: column;\n max-width: 250px;\n margin-top: 30px;\n}\n.PianoRoll-module_PianoRollLeftColumn__S6SZQ.PianoRoll-module_showTrackList__mqsrT {\n width: 30%;\n margin-top: 0;\n}\n\n.PianoRoll-module_PianoRollRightColumn__V98HV {\n display: flex;\n position: relative;\n flex-direction: column;\n flex: 1;\n height: 100%;\n}\n";
31
+ var styles$6 = {"PianoRoll":"PianoRoll-module_PianoRoll__rkRYx","PianoRollContainer":"PianoRoll-module_PianoRollContainer__Oyxr7","PianoRollLeftColumn":"PianoRoll-module_PianoRollLeftColumn__S6SZQ","showTrackList":"PianoRoll-module_showTrackList__mqsrT","PianoRollRightColumn":"PianoRoll-module_PianoRollRightColumn__V98HV"};
32
+ styleInject(css_248z$6);
33
33
 
34
34
  const defaultState = {
35
35
  ppq: 0,
@@ -40,23 +40,46 @@ const defaultState = {
40
40
  verticalPosition: 44,
41
41
  verticalTrackZoom: 0.5,
42
42
  verticalTrackPosition: 0,
43
+ playHeadPosition: 0,
43
44
  gridDivision: 4,
44
45
  snapToGrid: true,
45
46
  duration: 0,
46
47
  tracks: [],
47
48
  selectedTrackIndex: 0,
48
- pressedKeys: {}
49
+ pressedKeys: {},
50
+ notesScrollerClientRect: {
51
+ left: 0,
52
+ width: 0,
53
+ top: 0,
54
+ height: 0
55
+ },
56
+ tracksScrollerClientRect: {
57
+ left: 0,
58
+ width: 0,
59
+ top: 0,
60
+ height: 0
61
+ }
49
62
  };
50
63
  const propNameToHandlerName = name => `on${name[0]?.toUpperCase()}${name.slice(1)}Change`;
51
- const pianoRollStatePropNames = [...Object.keys(defaultState), ...Object.keys(defaultState).map(propNameToHandlerName), "onNoteChange", "onInsertNote", "onRemoveNote", "onNoteDown", "onNoteUp", "isKeyDown"];
64
+ const pianoRollStatePropNames = [...Object.keys(defaultState), ...Object.keys(defaultState).map(propNameToHandlerName), "onNoteChange", "onInsertNote", "onRemoveNote", "onNoteDown", "onNoteUp", "isKeyDown", "snapValueToGridIfEnabled"];
52
65
  const createPianoRollstate = initialState => {
53
66
  const [state, setState] = createStore({
54
67
  ...defaultState,
55
68
  ...initialState
56
69
  });
57
70
  const handlers = Object.fromEntries(Object.entries(state).map(entry => {
58
- return [propNameToHandlerName(entry[0]), value => setState(entry[0], value)];
71
+ const handlerName = propNameToHandlerName(entry[0]);
72
+ return [handlerName, (value, originalEvent) => {
73
+ const handler = initialState?.[handlerName];
74
+ setState(entry[0], value);
75
+ if (handler) {
76
+ handler(value, originalEvent);
77
+ }
78
+ }];
59
79
  }));
80
+ const onPlayheadPositionChange = (playheadPosition, originalEvent) => {
81
+ handlers.onPlayHeadPositionChange(snapValueToGridIfEnabled(playheadPosition, !!originalEvent?.altKey));
82
+ };
60
83
  const updateNotes = async (trackIndex, getNotes) => {
61
84
  const track = state.tracks[trackIndex];
62
85
  if (!track) return;
@@ -98,6 +121,10 @@ const createPianoRollstate = initialState => {
98
121
  updateKeyPressedState(trackIndex, keyNumber, false);
99
122
  };
100
123
  const isKeyDown = (trackIndex, keyNumber) => !!state.pressedKeys[trackIndex]?.[keyNumber];
124
+ const snapValueToGridIfEnabled = (value, altKey) => {
125
+ const gridDivisionTicks = state.ppq * 4 / state.gridDivision;
126
+ return state.snapToGrid && !altKey ? Math.round(value / gridDivisionTicks) * gridDivisionTicks : value;
127
+ };
101
128
  return mergeProps(state, {
102
129
  ...handlers,
103
130
  onNoteChange,
@@ -105,7 +132,9 @@ const createPianoRollstate = initialState => {
105
132
  onRemoveNote,
106
133
  onNoteDown,
107
134
  onNoteUp,
108
- isKeyDown
135
+ isKeyDown,
136
+ snapValueToGridIfEnabled,
137
+ onPlayheadPositionChange
109
138
  });
110
139
  };
111
140
 
@@ -118,9 +147,9 @@ const usePianoRollContext = () => {
118
147
  };
119
148
  const splitContextProps = allProps => splitProps(allProps, [...pianoRollStatePropNames, "showAllTracks", "showTrackList"]);
120
149
 
121
- var css_248z$4 = ".PianoRollKeys-module_PianoRollKeys__5vnQ0 {\n position: relative;\n height: 100%;\n border-width: 0;\n border-color: #000;\n border-style: solid;\n border-right-width: 1px;\n border-left-width: 1px;\n background: white;\n cursor: pointer;\n}\n\n.PianoRollKeys-module_Key__jybNC {\n transition-property: background-color, box-shadow;\n transition-duration: 200ms;\n transition-timing-function: ease-out;\n border-width: 0px;\n position: absolute;\n box-sizing: border-box;\n width: 120%;\n left: -20%;\n border-color: #000;\n border-style: solid;\n transform: rotateX(0deg);\n perspective: 100px;\n}\n\n.PianoRollKeys-module_black__7rncB {\n background: black;\n border-top-right-radius: 12%;\n border-bottom-right-radius: 12%;\n margin-left: -20%;\n z-index: 1;\n border: 1px black outset;\n}\n\n.PianoRollKeys-module_white__fR2Lm {\n background: white;\n border-top-width: 0.5px;\n border-bottom-width: 0.5px;\n border-top-right-radius: 4%;\n border-bottom-right-radius: 4%;\n}\n\n.PianoRollKeys-module_down__GcNeZ {\n transition: none;\n background: red;\n transform: rotateX(0deg) rotateY(20deg);\n}\n";
122
- var styles$4 = {"PianoRollKeys":"PianoRollKeys-module_PianoRollKeys__5vnQ0","Key":"PianoRollKeys-module_Key__jybNC","black":"PianoRollKeys-module_black__7rncB","white":"PianoRollKeys-module_white__fR2Lm","down":"PianoRollKeys-module_down__GcNeZ"};
123
- styleInject(css_248z$4);
150
+ var css_248z$5 = ".PianoRollKeys-module_PianoRollKeys__5vnQ0 {\n position: relative;\n height: 100%;\n border-width: 0;\n border-color: #000;\n border-style: solid;\n border-right-width: 1px;\n border-left-width: 1px;\n background: white;\n cursor: pointer;\n background: black;\n}\n\n.PianoRollKeys-module_Key__jybNC {\n transition-property: background-color, box-shadow;\n transition-duration: 200ms;\n transition-timing-function: ease-out;\n border-width: 0px;\n position: absolute;\n box-sizing: border-box;\n width: 120%;\n left: -20%;\n border-color: #000;\n border-style: solid;\n transform: rotateX(0deg);\n perspective: 100px;\n}\n\n.PianoRollKeys-module_black__7rncB {\n background: black;\n border-top-right-radius: 12%;\n border-bottom-right-radius: 12%;\n margin-left: -20%;\n z-index: 1;\n border: 1px black outset;\n}\n\n.PianoRollKeys-module_white__fR2Lm {\n background: white;\n border-top-width: 0.5px;\n border-bottom-width: 0.5px;\n border-top-right-radius: 4%;\n border-bottom-right-radius: 4%;\n}\n\n.PianoRollKeys-module_down__GcNeZ {\n transition: none;\n background: red;\n transform: rotateX(0deg) rotateY(20deg);\n}\n";
151
+ var styles$5 = {"PianoRollKeys":"PianoRollKeys-module_PianoRollKeys__5vnQ0","Key":"PianoRollKeys-module_Key__jybNC","black":"PianoRollKeys-module_black__7rncB","white":"PianoRollKeys-module_white__fR2Lm","down":"PianoRollKeys-module_down__GcNeZ"};
152
+ styleInject(css_248z$5);
124
153
 
125
154
  function createViewPortDimension(getState) {
126
155
  const getStateWithFunctions = () => {
@@ -210,11 +239,11 @@ const useViewPortDimension = name => {
210
239
  return viewPort;
211
240
  };
212
241
 
213
- var css_248z$3 = ".ScrollZoomContainer-module_ScrollZoomContainer__o2bRt {\n height: 100%;\n width: 100%;\n z-index: 4;\n}\n";
214
- var styles$3 = {"ScrollZoomContainer":"ScrollZoomContainer-module_ScrollZoomContainer__o2bRt"};
215
- styleInject(css_248z$3);
242
+ var css_248z$4 = ".ScrollZoomContainer-module_ScrollZoomContainer__o2bRt {\n height: 100%;\n width: 100%;\n z-index: 4;\n}\n";
243
+ var styles$4 = {"ScrollZoomContainer":"ScrollZoomContainer-module_ScrollZoomContainer__o2bRt"};
244
+ styleInject(css_248z$4);
216
245
 
217
- const _tmpl$$7 = /*#__PURE__*/template(`<div><div><div><div></div></div></div></div>`, 8);
246
+ const _tmpl$$8 = /*#__PURE__*/template(`<div><div><div><div></div></div></div></div>`, 8);
218
247
  const ScrollZoomContainer = props => {
219
248
  let scrollContentRef;
220
249
  const [ownProps, divProps] = splitProps(props, ["ref", "verticalDimensionName", "horizontalDimensionName", "showScrollbar"]);
@@ -250,11 +279,11 @@ const ScrollZoomContainer = props => {
250
279
  if (event.altKey) {
251
280
  event.preventDefault();
252
281
  if (Math.abs(event.deltaX) > Math.abs(event.deltaY)) {
253
- horizontalViewPort()?.onZoomChange?.(horizontalViewPort().zoom * (1 + event.deltaX / horizontalViewPort().pixelSize));
282
+ horizontalViewPort()?.onZoomChange?.(horizontalViewPort().zoom / (1 + event.deltaX / horizontalViewPort().pixelSize));
254
283
  const maxPosition = horizontalViewPort().calculateMaxPosition();
255
284
  horizontalViewPort()?.onPositionChange?.(Math.min(maxPosition, horizontalViewPort()?.position));
256
285
  } else {
257
- verticalViewPort()?.onZoomChange?.(verticalViewPort().zoom * (1 + event.deltaY / verticalViewPort().pixelSize));
286
+ verticalViewPort()?.onZoomChange?.(verticalViewPort().zoom / (1 + event.deltaY / verticalViewPort().pixelSize));
258
287
  const maxVerticalPosition = verticalViewPort().calculateMaxPosition();
259
288
  verticalViewPort()?.onPositionChange?.(Math.min(maxVerticalPosition, verticalViewPort()?.position));
260
289
  }
@@ -286,7 +315,7 @@ const ScrollZoomContainer = props => {
286
315
  });
287
316
  });
288
317
  return (() => {
289
- const _el$ = _tmpl$$7.cloneNode(true),
318
+ const _el$ = _tmpl$$8.cloneNode(true),
290
319
  _el$2 = _el$.firstChild,
291
320
  _el$3 = _el$2.firstChild,
292
321
  _el$4 = _el$3.firstChild;
@@ -303,7 +332,7 @@ const ScrollZoomContainer = props => {
303
332
  },
304
333
  get classList() {
305
334
  return {
306
- [styles$3.ScrollZoomContainer]: true,
335
+ [styles$4.ScrollZoomContainer]: true,
307
336
  ...divProps.classList
308
337
  };
309
338
  }
@@ -336,7 +365,7 @@ const ScrollZoomContainer = props => {
336
365
  })();
337
366
  };
338
367
 
339
- const _tmpl$$6 = /*#__PURE__*/template(`<div></div>`, 2);
368
+ const _tmpl$$7 = /*#__PURE__*/template(`<div></div>`, 2);
340
369
  const PianoRollKeys = () => {
341
370
  const verticalViewPort = createMemo(() => useViewPortDimension("vertical"));
342
371
  const horizontalViewPort = createMemo(() => useViewPortDimension("horizontalKeys"));
@@ -353,7 +382,7 @@ const PianoRollKeys = () => {
353
382
  }
354
383
  });
355
384
  return (() => {
356
- const _el$ = _tmpl$$6.cloneNode(true);
385
+ const _el$ = _tmpl$$7.cloneNode(true);
357
386
  insert(_el$, createComponent(ScrollZoomContainer, {
358
387
  horizontalDimensionName: "horizontalKeys",
359
388
  showScrollbar: false,
@@ -367,10 +396,10 @@ const PianoRollKeys = () => {
367
396
  const nextIsBlack = blackKeys.includes((key().number + 1) % 12);
368
397
  return createComponent(Show, {
369
398
  get when() {
370
- return virtualDimensions().size > 0;
399
+ return verticalViewPort().isVisible(virtualDimensions());
371
400
  },
372
401
  get children() {
373
- const _el$2 = _tmpl$$6.cloneNode(true);
402
+ const _el$2 = _tmpl$$7.cloneNode(true);
374
403
  _el$2.addEventListener("mouseleave", () => {
375
404
  if (isMouseDown()) {
376
405
  context.onNoteUp(context.selectedTrackIndex, key().number);
@@ -390,16 +419,16 @@ const PianoRollKeys = () => {
390
419
  };
391
420
  effect(_p$ => {
392
421
  const _v$3 = {
393
- [styles$4["Key"]]: true,
394
- [styles$4["black"]]: key().isBlack,
395
- [styles$4["white"]]: !key().isBlack,
396
- [styles$4["down"]]: isDown()
422
+ [styles$5["Key"]]: true,
423
+ [styles$5["black"]]: key().isBlack,
424
+ [styles$5["white"]]: !key().isBlack,
425
+ [styles$5["down"]]: isDown()
397
426
  },
398
427
  _v$4 = key().name,
399
428
  _v$5 = key().number % 12,
400
429
  _v$6 = `${virtualDimensions().offset - virtualDimensions().size * (!key().isBlack ? nextIsBlack ? 1 / 2 : 0 : 0)}px`,
401
430
  _v$7 = `${virtualDimensions().size + (!key().isBlack && nextIsBlack ? virtualDimensions().size / 2 : 0) + (!key().isBlack && previousIsBlack ? virtualDimensions().size / 2 : 0)}px`,
402
- _v$8 = [`0px 0px ${Math.min(virtualDimensions().size / 20, 1)}px ${Math.min(virtualDimensions().size / 50, 1)}px rgba(0, 0, 0, 0.5) ${key().isBlack ? "" : "inset"}`, isDown() && `0px 0px ${Math.min(virtualDimensions().size / 8, 2)}px ${Math.min(virtualDimensions().size / 20, 2)}px rgba(0, 0, 0, 0.5) inset`].filter(item => !!item).join(", ");
431
+ _v$8 = `0px 0px ${Math.min(virtualDimensions().size / 20, 1)}px ${Math.min(virtualDimensions().size / 50, 1)}px rgba(0, 0, 0, 0.5) ${key().isBlack ? "" : "inset"}` + isDown() ? `0px 0px ${Math.min(virtualDimensions().size / 8, 2)}px ${Math.min(virtualDimensions().size / 20, 2)}px rgba(0, 0, 0, 0.5) inset` : "";
403
432
  _p$._v$3 = classList(_el$2, _v$3, _p$._v$3);
404
433
  _v$4 !== _p$._v$4 && setAttribute(_el$2, "title", _p$._v$4 = _v$4);
405
434
  _v$5 !== _p$._v$5 && setAttribute(_el$2, "data-index", _p$._v$5 = _v$5);
@@ -423,7 +452,7 @@ const PianoRollKeys = () => {
423
452
  }
424
453
  }));
425
454
  effect(_p$ => {
426
- const _v$ = styles$4.PianoRollKeys,
455
+ const _v$ = styles$5.PianoRollKeys,
427
456
  _v$2 = `${horizontalViewPort().pixelSize}px`;
428
457
  _v$ !== _p$._v$ && className(_el$, _p$._v$ = _v$);
429
458
  _v$2 !== _p$._v$2 && _el$.style.setProperty("width", _p$._v$2 = _v$2);
@@ -446,11 +475,11 @@ const keys = Array.from({
446
475
  }));
447
476
  delegateEvents(["mousedown", "mouseup"]);
448
477
 
449
- var css_248z$2 = ".PianoRollNotes-module_PianoRollNotes__6pF-y {\n position: absolute;\n flex: 1;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 1;\n cursor: grab;\n}\n.PianoRollNotes-module_PianoRollNotes__6pF-y .PianoRollNotes-module_Note__-jxLb {\n position: absolute;\n box-sizing: border-box;\n border-width: 0.5px;\n border-style: solid;\n border-color: #a00;\n cursor: move;\n}\n.PianoRollNotes-module_PianoRollNotes__6pF-y .PianoRollNotes-module_Note__-jxLb.PianoRollNotes-module_trimStart__vuBlj {\n cursor: w-resize;\n}\n.PianoRollNotes-module_PianoRollNotes__6pF-y .PianoRollNotes-module_Note__-jxLb.PianoRollNotes-module_trimEnd__zPdjr {\n cursor: e-resize;\n}";
450
- var styles$2 = {"PianoRollNotes":"PianoRollNotes-module_PianoRollNotes__6pF-y","Note":"PianoRollNotes-module_Note__-jxLb","trimStart":"PianoRollNotes-module_trimStart__vuBlj","trimEnd":"PianoRollNotes-module_trimEnd__zPdjr"};
451
- styleInject(css_248z$2);
478
+ var css_248z$3 = ".PianoRollNotes-module_PianoRollNotes__6pF-y {\n position: absolute;\n flex: 1;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 1;\n cursor: grab;\n}\n.PianoRollNotes-module_PianoRollNotes__6pF-y .PianoRollNotes-module_Note__-jxLb {\n position: absolute;\n box-sizing: border-box;\n border-width: 0.5px;\n border-style: solid;\n border-color: #a00;\n cursor: move;\n}\n.PianoRollNotes-module_PianoRollNotes__6pF-y .PianoRollNotes-module_Note__-jxLb.PianoRollNotes-module_trimStart__vuBlj {\n cursor: w-resize;\n}\n.PianoRollNotes-module_PianoRollNotes__6pF-y .PianoRollNotes-module_Note__-jxLb.PianoRollNotes-module_trimEnd__zPdjr {\n cursor: e-resize;\n}";
479
+ var styles$3 = {"PianoRollNotes":"PianoRollNotes-module_PianoRollNotes__6pF-y","Note":"PianoRollNotes-module_Note__-jxLb","trimStart":"PianoRollNotes-module_trimStart__vuBlj","trimEnd":"PianoRollNotes-module_trimEnd__zPdjr"};
480
+ styleInject(css_248z$3);
452
481
 
453
- const _tmpl$$5 = /*#__PURE__*/template(`<div></div>`, 2);
482
+ const _tmpl$$6 = /*#__PURE__*/template(`<div></div>`, 2);
454
483
  const PianoRollNotes = props => {
455
484
  const context = usePianoRollContext();
456
485
  const verticalViewPort = createMemo(() => useViewPortDimension(context.mode === "keys" ? "vertical" : "verticalTracks"));
@@ -462,7 +491,6 @@ const PianoRollNotes = props => {
462
491
  const [currentNoteTrackIndex, setCurrentNoteTrackIndex] = createSignal(-1);
463
492
  const [diffPosition, setDiffPosition] = createSignal(0);
464
493
  const [getInitialNote, setInitialNote] = createSignal();
465
- const snapValueToGridIfEnabled = (value, altKey) => context.snapToGrid && !altKey ? Math.round(value / gridDivisionTicks()) * gridDivisionTicks() : value;
466
494
  const calculateNoteDragValues = event => {
467
495
  const targetTrackIndex = context.mode === "tracks" ? clamp(Math.floor(verticalViewPort().calculatePosition(event.clientY)), 0, context.tracks.length - 1) : currentNoteTrackIndex() > -1 ? currentNoteTrackIndex() : context.selectedTrackIndex;
468
496
  const midi = context.mode === "keys" ? Math.floor(128 - verticalViewPort().calculatePosition(event.clientY)) : getInitialNote()?.midi ?? 60;
@@ -474,6 +502,8 @@ const PianoRollNotes = props => {
474
502
  };
475
503
  };
476
504
  const handleMouseMove = mouseMoveEvent => {
505
+ mouseMoveEvent.preventDefault();
506
+ mouseMoveEvent.stopPropagation();
477
507
  const note = getInitialNote();
478
508
  if (!note) return;
479
509
  const {
@@ -481,7 +511,7 @@ const PianoRollNotes = props => {
481
511
  midi,
482
512
  horiontalPosition
483
513
  } = calculateNoteDragValues(mouseMoveEvent);
484
- const ticks = snapValueToGridIfEnabled(horiontalPosition - diffPosition(), mouseMoveEvent.altKey);
514
+ const ticks = context.snapValueToGridIfEnabled(horiontalPosition - diffPosition(), mouseMoveEvent.altKey);
485
515
  const updatedNote = {
486
516
  ...note,
487
517
  midi,
@@ -490,11 +520,11 @@ const PianoRollNotes = props => {
490
520
  }),
491
521
  ...(noteDragMode() === "trimStart" && {
492
522
  ticks: ticks < note.ticks + note.durationTicks ? ticks : note.ticks + note.durationTicks,
493
- durationTicks: ticks < note.ticks + note.durationTicks ? note.ticks + note.durationTicks - ticks : snapValueToGridIfEnabled(horiontalPosition - note.ticks, mouseMoveEvent.altKey)
523
+ durationTicks: ticks < note.ticks + note.durationTicks ? note.ticks + note.durationTicks - ticks : context.snapValueToGridIfEnabled(horiontalPosition - note.ticks, mouseMoveEvent.altKey)
494
524
  }),
495
525
  ...(noteDragMode() === "trimEnd" && {
496
526
  ticks: ticks < note.ticks ? ticks : note.ticks,
497
- durationTicks: ticks < note.ticks ? note.ticks - ticks : snapValueToGridIfEnabled(horiontalPosition - note.ticks, mouseMoveEvent.altKey)
527
+ durationTicks: ticks < note.ticks ? note.ticks - ticks : context.snapValueToGridIfEnabled(horiontalPosition - note.ticks, mouseMoveEvent.altKey)
498
528
  })
499
529
  };
500
530
  const previousTrackIndex = currentNoteTrackIndex();
@@ -514,23 +544,27 @@ const PianoRollNotes = props => {
514
544
  window.removeEventListener("mousemove", handleMouseMove);
515
545
  window.removeEventListener("mouseup", stopDragging);
516
546
  setIsDragging(false);
547
+ setCurrentNoteIndex(-1);
548
+ setCurrentNoteTrackIndex(-1);
517
549
  };
518
550
  const startDragging = () => {
519
551
  window.addEventListener("mousemove", handleMouseMove);
520
552
  window.addEventListener("mouseup", stopDragging);
521
553
  };
522
554
  const getClasses = noteDragMode => {
523
- return noteDragMode ? [styles$2.Note, styles$2[noteDragMode]] : [styles$2.Note];
555
+ return noteDragMode ? [styles$3.Note, styles$3[noteDragMode]] : [styles$3.Note];
524
556
  };
525
557
  return (() => {
526
- const _el$ = _tmpl$$5.cloneNode(true);
558
+ const _el$ = _tmpl$$6.cloneNode(true);
527
559
  _el$.$$mousedown = mouseDownEvent => {
560
+ mouseDownEvent.preventDefault();
561
+ mouseDownEvent.stopPropagation();
528
562
  const {
529
563
  targetTrackIndex,
530
564
  midi,
531
565
  horiontalPosition
532
566
  } = calculateNoteDragValues(mouseDownEvent);
533
- const ticks = snapValueToGridIfEnabled(horiontalPosition, mouseDownEvent.altKey);
567
+ const ticks = context.snapValueToGridIfEnabled(horiontalPosition, mouseDownEvent.altKey, context);
534
568
  const durationTicks = gridDivisionTicks();
535
569
  const newNote = {
536
570
  midi,
@@ -571,7 +605,7 @@ const PianoRollNotes = props => {
571
605
  return memo(() => !!verticalViewPort().isVisible(verticalVirtualDimensions()))() && horizontalViewPort().isVisible(horizontalDimensions());
572
606
  },
573
607
  get children() {
574
- const _el$2 = _tmpl$$5.cloneNode(true);
608
+ const _el$2 = _tmpl$$6.cloneNode(true);
575
609
  _el$2.$$mousedown = event => {
576
610
  event.stopPropagation();
577
611
  const initialPosition = horizontalViewPort().calculatePosition(event.clientX);
@@ -597,17 +631,19 @@ const PianoRollNotes = props => {
597
631
  setAttribute(_el$2, "draggable", false);
598
632
  effect(_p$ => {
599
633
  const _v$ = getClasses(noteDragMode()).join(" "),
600
- _v$2 = `rgba(255,0,0, ${(128 + note.velocity) / 256})`,
601
- _v$3 = `${verticalVirtualDimensions().offset}px`,
602
- _v$4 = `${verticalVirtualDimensions().size}px`,
603
- _v$5 = `${horizontalDimensions().offset}px`,
604
- _v$6 = `${horizontalDimensions().size}px`;
634
+ _v$2 = `${track.color}`,
635
+ _v$3 = (note.velocity / 128 + 2) / 3,
636
+ _v$4 = `${verticalVirtualDimensions().offset}px`,
637
+ _v$5 = `${verticalVirtualDimensions().size}px`,
638
+ _v$6 = `${horizontalDimensions().offset}px`,
639
+ _v$7 = `${horizontalDimensions().size}px`;
605
640
  _v$ !== _p$._v$ && className(_el$2, _p$._v$ = _v$);
606
641
  _v$2 !== _p$._v$2 && _el$2.style.setProperty("background-color", _p$._v$2 = _v$2);
607
- _v$3 !== _p$._v$3 && _el$2.style.setProperty("top", _p$._v$3 = _v$3);
608
- _v$4 !== _p$._v$4 && _el$2.style.setProperty("height", _p$._v$4 = _v$4);
609
- _v$5 !== _p$._v$5 && _el$2.style.setProperty("left", _p$._v$5 = _v$5);
610
- _v$6 !== _p$._v$6 && _el$2.style.setProperty("width", _p$._v$6 = _v$6);
642
+ _v$3 !== _p$._v$3 && _el$2.style.setProperty("opacity", _p$._v$3 = _v$3);
643
+ _v$4 !== _p$._v$4 && _el$2.style.setProperty("top", _p$._v$4 = _v$4);
644
+ _v$5 !== _p$._v$5 && _el$2.style.setProperty("height", _p$._v$5 = _v$5);
645
+ _v$6 !== _p$._v$6 && _el$2.style.setProperty("left", _p$._v$6 = _v$6);
646
+ _v$7 !== _p$._v$7 && _el$2.style.setProperty("width", _p$._v$7 = _v$7);
611
647
  return _p$;
612
648
  }, {
613
649
  _v$: undefined,
@@ -615,7 +651,8 @@ const PianoRollNotes = props => {
615
651
  _v$3: undefined,
616
652
  _v$4: undefined,
617
653
  _v$5: undefined,
618
- _v$6: undefined
654
+ _v$6: undefined,
655
+ _v$7: undefined
619
656
  });
620
657
  return _el$2;
621
658
  }
@@ -627,21 +664,19 @@ const PianoRollNotes = props => {
627
664
  }
628
665
  }));
629
666
  effect(_$p => classList(_el$, {
630
- [styles$2.PianoRollNotes]: true
667
+ [styles$3.PianoRollNotes]: true
631
668
  }, _$p));
632
669
  return _el$;
633
670
  })();
634
671
  };
635
672
  delegateEvents(["mousedown", "mousemove", "dblclick"]);
636
673
 
637
- var css_248z$1 = ".PianoRollGrid-module_PianoRollGrid__tG119 {\n position: relative;\n width: 100%;\n height: 100%;\n}\n\n.PianoRollGrid-module_PianoRollGrid-Row__nRMCs {\n position: absolute;\n border-style: solid;\n border-color: gray;\n}\n\n.PianoRollGrid-module_PianoRollGrid-Time__jLz3E {\n position: absolute;\n box-sizing: border-box;\n top: 0px;\n height: 100%;\n border-left-style: solid;\n border-left-width: 0.5px;\n}\n";
638
- var styles$1 = {"PianoRollGrid":"PianoRollGrid-module_PianoRollGrid__tG119","PianoRollGrid-Row":"PianoRollGrid-module_PianoRollGrid-Row__nRMCs","PianoRollGrid-Time":"PianoRollGrid-module_PianoRollGrid-Time__jLz3E"};
639
- styleInject(css_248z$1);
674
+ var css_248z$2 = ".PianoRollGrid-module_PianoRollGrid__tG119 {\n position: relative;\n width: 100%;\n height: 100%;\n}\n\n.PianoRollGrid-module_PianoRollGrid-Row__nRMCs {\n position: absolute;\n border-style: solid;\n border-color: gray;\n width: 100%;\n border-width: 0;\n}\n.PianoRollGrid-module_PianoRollGrid-Row__nRMCs.PianoRollGrid-module_IsDark__jRyyy {\n background-color: rgba(0, 0, 0, 0.2);\n}\n\n.PianoRollGrid-module_PianoRollGrid-Row__nRMCs.PianoRollGrid-module_Key__8ZwPN {\n border-width: 0px 1px 0px 0;\n}\n.PianoRollGrid-module_PianoRollGrid-Row__nRMCs.PianoRollGrid-module_IsBlackAndNextIsWhite__UF8O2 {\n background-color: rgba(0, 0, 0, 0.2);\n border-width: 0.1px 1px 0.1px 0;\n}\n\n.PianoRollGrid-module_PianoRollGrid-Time__jLz3E {\n position: absolute;\n box-sizing: border-box;\n top: 0px;\n height: 100%;\n border-left-style: solid;\n border-left-width: 0.5px;\n border-left-color: #bbb;\n background: #ccc;\n}\n\n.PianoRollGrid-module_PianoRollGrid-Time__jLz3E.PianoRollGrid-module_Highlighted__N1J6q {\n background: #ddd;\n}\n\n.PianoRollGrid-module_PianoRollGrid-Time__jLz3E.PianoRollGrid-module_HighlightedBorder__Vnrff {\n border-left-color: gray;\n}\n";
675
+ var styles$2 = {"PianoRollGrid":"PianoRollGrid-module_PianoRollGrid__tG119","PianoRollGrid-Row":"PianoRollGrid-module_PianoRollGrid-Row__nRMCs","IsDark":"PianoRollGrid-module_IsDark__jRyyy","Key":"PianoRollGrid-module_Key__8ZwPN","IsBlackAndNextIsWhite":"PianoRollGrid-module_IsBlackAndNextIsWhite__UF8O2","PianoRollGrid-Time":"PianoRollGrid-module_PianoRollGrid-Time__jLz3E","Highlighted":"PianoRollGrid-module_Highlighted__N1J6q","HighlightedBorder":"PianoRollGrid-module_HighlightedBorder__Vnrff"};
676
+ styleInject(css_248z$2);
640
677
 
641
- const _tmpl$$4 = /*#__PURE__*/template(`<div></div>`, 2);
642
- const PianoRollGrid = () => {
678
+ const usePianoRollGrid = () => {
643
679
  const context = usePianoRollContext();
644
- const verticalViewPort = createMemo(() => useViewPortDimension(context.mode === "keys" ? "vertical" : "verticalTracks"));
645
680
  const horizontalViewPort = createMemo(() => useViewPortDimension("horizontal"));
646
681
  const measureTicks = createMemo(() => context.ppq * 4);
647
682
  const selectedGridDivisorTicks = createMemo(() => measureTicks() / context.gridDivision);
@@ -662,43 +697,62 @@ const PianoRollGrid = () => {
662
697
  const startIndex = Math.floor(context.position / gridDivisorTicks());
663
698
  return Array.from({
664
699
  length: numberOfLines
665
- }).map((_, index) => ({
666
- index: index + startIndex,
667
- ticks: (index + startIndex) * gridDivisorTicks()
668
- }));
700
+ }).map((_, i) => {
701
+ const index = i + startIndex;
702
+ const ticks = index * gridDivisorTicks();
703
+ const measurePosition = ticks / context.ppq / 4 + 1;
704
+ const bars = Math.floor(measurePosition);
705
+ const beats = Math.floor((measurePosition - bars) * 4);
706
+ const label = `${bars}${beats ? `.${beats}` : ""}`;
707
+ return {
708
+ index,
709
+ ticks,
710
+ isHighlighted: Math.ceil((index + 1) * selectedGridDivisorTicks() / measureTicks()) % 2 === 0,
711
+ hasHighlightedBorder: index * gridDivisorTicks() % selectedGridDivisorTicks() === 0,
712
+ showLabel: index * selectedGridDivisorTicks() / measureTicks() % 2 === 0,
713
+ virtualDimensions: horizontalViewPort().calculatePixelDimensions(ticks, gridDivisorTicks()),
714
+ label
715
+ };
716
+ });
669
717
  });
718
+ return gridArray;
719
+ };
720
+
721
+ const _tmpl$$5 = /*#__PURE__*/template(`<div></div>`, 2);
722
+ const PianoRollGrid = () => {
723
+ const context = usePianoRollContext();
724
+ const verticalViewPort = createMemo(() => useViewPortDimension(context.mode === "keys" ? "vertical" : "verticalTracks"));
725
+ const horizontalViewPort = createMemo(() => useViewPortDimension("horizontal"));
726
+ const gridArray = usePianoRollGrid();
670
727
  return (() => {
671
- const _el$ = _tmpl$$4.cloneNode(true);
728
+ const _el$ = _tmpl$$5.cloneNode(true);
672
729
  insert(_el$, createComponent(Index, {
673
730
  get each() {
674
731
  return gridArray();
675
732
  },
676
733
  children: entry => {
677
- const virtualDimensions = createMemo(() => horizontalViewPort().calculatePixelDimensions(entry().ticks, gridDivisorTicks()));
678
734
  return createComponent(Show, {
679
735
  get when() {
680
- return virtualDimensions().size > 0;
736
+ return horizontalViewPort().isVisible(entry().virtualDimensions);
681
737
  },
682
738
  get children() {
683
- const _el$2 = _tmpl$$4.cloneNode(true);
739
+ const _el$2 = _tmpl$$5.cloneNode(true);
684
740
  effect(_p$ => {
685
- const _v$ = styles$1["PianoRollGrid-Time"],
686
- _v$2 = Math.ceil((entry().index + 1 * selectedGridDivisorTicks()) / measureTicks()) % 2 === 0 ? "#ddd" : "#ccc",
687
- _v$3 = `${virtualDimensions().offset}px`,
688
- _v$4 = `${virtualDimensions().size}px`,
689
- _v$5 = entry().index * gridDivisorTicks() % selectedGridDivisorTicks() === 0 ? "gray" : "#bbb";
690
- _v$ !== _p$._v$ && className(_el$2, _p$._v$ = _v$);
691
- _v$2 !== _p$._v$2 && _el$2.style.setProperty("background", _p$._v$2 = _v$2);
692
- _v$3 !== _p$._v$3 && _el$2.style.setProperty("left", _p$._v$3 = _v$3);
693
- _v$4 !== _p$._v$4 && _el$2.style.setProperty("width", _p$._v$4 = _v$4);
694
- _v$5 !== _p$._v$5 && _el$2.style.setProperty("border-left-color", _p$._v$5 = _v$5);
741
+ const _v$ = {
742
+ [styles$2["PianoRollGrid-Time"]]: true,
743
+ [styles$2["Highlighted"]]: entry().isHighlighted,
744
+ [styles$2["HighlightedBorder"]]: entry().hasHighlightedBorder
745
+ },
746
+ _v$2 = `${entry().virtualDimensions.offset}px`,
747
+ _v$3 = `${entry().virtualDimensions.size}px`;
748
+ _p$._v$ = classList(_el$2, _v$, _p$._v$);
749
+ _v$2 !== _p$._v$2 && _el$2.style.setProperty("left", _p$._v$2 = _v$2);
750
+ _v$3 !== _p$._v$3 && _el$2.style.setProperty("width", _p$._v$3 = _v$3);
695
751
  return _p$;
696
752
  }, {
697
753
  _v$: undefined,
698
754
  _v$2: undefined,
699
- _v$3: undefined,
700
- _v$4: undefined,
701
- _v$5: undefined
755
+ _v$3: undefined
702
756
  });
703
757
  return _el$2;
704
758
  }
@@ -716,29 +770,27 @@ const PianoRollGrid = () => {
716
770
  const virtualDimensions = createMemo(() => verticalViewPort().calculatePixelDimensions(127 - key().number, 1));
717
771
  return createComponent(Show, {
718
772
  get when() {
719
- return virtualDimensions().size > 0;
773
+ return verticalViewPort().isVisible(virtualDimensions());
720
774
  },
721
775
  get children() {
722
- const _el$3 = _tmpl$$4.cloneNode(true);
723
- _el$3.style.setProperty("width", "100%");
776
+ const _el$3 = _tmpl$$5.cloneNode(true);
724
777
  effect(_p$ => {
725
- const _v$6 = styles$1["PianoRollGrid-Row"],
726
- _v$7 = `${virtualDimensions().offset}px`,
727
- _v$8 = `${virtualDimensions().size}px`,
728
- _v$9 = key().isBlack ? "rgba(0,0,0,0.2)" : "none",
729
- _v$10 = `${!key().isBlack && !blackKeys.includes((key().number + 1) % 12) ? "0.1px" : 0} 1px ${!key().isBlack && !blackKeys.includes((key().number - 1) % 12) ? "0.1px" : 0} 0`;
730
- _v$6 !== _p$._v$6 && className(_el$3, _p$._v$6 = _v$6);
731
- _v$7 !== _p$._v$7 && _el$3.style.setProperty("top", _p$._v$7 = _v$7);
732
- _v$8 !== _p$._v$8 && _el$3.style.setProperty("height", _p$._v$8 = _v$8);
733
- _v$9 !== _p$._v$9 && _el$3.style.setProperty("background-color", _p$._v$9 = _v$9);
734
- _v$10 !== _p$._v$10 && _el$3.style.setProperty("border-width", _p$._v$10 = _v$10);
778
+ const _v$4 = {
779
+ [styles$2["PianoRollGrid-Row"]]: true,
780
+ [styles$2["Key"]]: true,
781
+ [styles$2["IsDark"]]: key().isBlack,
782
+ [styles$2["IsBlackAndNextIsWhite"]]: !key().isBlack && !blackKeys.includes((key().number + 1) % 12)
783
+ },
784
+ _v$5 = `${virtualDimensions().offset}px`,
785
+ _v$6 = `${virtualDimensions().size}px`;
786
+ _p$._v$4 = classList(_el$3, _v$4, _p$._v$4);
787
+ _v$5 !== _p$._v$5 && _el$3.style.setProperty("top", _p$._v$5 = _v$5);
788
+ _v$6 !== _p$._v$6 && _el$3.style.setProperty("height", _p$._v$6 = _v$6);
735
789
  return _p$;
736
790
  }, {
737
- _v$6: undefined,
738
- _v$7: undefined,
739
- _v$8: undefined,
740
- _v$9: undefined,
741
- _v$10: undefined
791
+ _v$4: undefined,
792
+ _v$5: undefined,
793
+ _v$6: undefined
742
794
  });
743
795
  return _el$3;
744
796
  }
@@ -760,27 +812,25 @@ const PianoRollGrid = () => {
760
812
  const virtualDimensions = createMemo(() => verticalViewPort().calculatePixelDimensions(index(), 1));
761
813
  return createComponent(Show, {
762
814
  get when() {
763
- return virtualDimensions().size > 0;
815
+ return verticalViewPort().isVisible(virtualDimensions());
764
816
  },
765
817
  get children() {
766
- const _el$4 = _tmpl$$4.cloneNode(true);
767
- _el$4.style.setProperty("width", "100%");
768
- _el$4.style.setProperty("border-width", "0");
818
+ const _el$4 = _tmpl$$5.cloneNode(true);
769
819
  effect(_p$ => {
770
- const _v$11 = styles$1["PianoRollGrid-Row"],
771
- _v$12 = `${virtualDimensions().offset}px`,
772
- _v$13 = `${virtualDimensions().size}px`,
773
- _v$14 = index() % 2 === 0 ? "rgba(0,0,0,0.2)" : "none";
774
- _v$11 !== _p$._v$11 && className(_el$4, _p$._v$11 = _v$11);
775
- _v$12 !== _p$._v$12 && _el$4.style.setProperty("top", _p$._v$12 = _v$12);
776
- _v$13 !== _p$._v$13 && _el$4.style.setProperty("height", _p$._v$13 = _v$13);
777
- _v$14 !== _p$._v$14 && _el$4.style.setProperty("background-color", _p$._v$14 = _v$14);
820
+ const _v$7 = {
821
+ [styles$2["PianoRollGrid-Row"]]: true,
822
+ [styles$2["IsDark"]]: index() % 2 === 0
823
+ },
824
+ _v$8 = `${virtualDimensions().offset}px`,
825
+ _v$9 = `${virtualDimensions().size}px`;
826
+ _p$._v$7 = classList(_el$4, _v$7, _p$._v$7);
827
+ _v$8 !== _p$._v$8 && _el$4.style.setProperty("top", _p$._v$8 = _v$8);
828
+ _v$9 !== _p$._v$9 && _el$4.style.setProperty("height", _p$._v$9 = _v$9);
778
829
  return _p$;
779
830
  }, {
780
- _v$11: undefined,
781
- _v$12: undefined,
782
- _v$13: undefined,
783
- _v$14: undefined
831
+ _v$7: undefined,
832
+ _v$8: undefined,
833
+ _v$9: undefined
784
834
  });
785
835
  return _el$4;
786
836
  }
@@ -789,47 +839,19 @@ const PianoRollGrid = () => {
789
839
  });
790
840
  }
791
841
  }), null);
792
- effect(() => className(_el$, styles$1.PianoRollGrid));
842
+ effect(() => className(_el$, styles$2.PianoRollGrid));
793
843
  return _el$;
794
844
  })();
795
845
  };
796
846
 
797
- const defaultRect = {
798
- left: 0,
799
- width: 0,
800
- top: 0,
801
- height: 0,
802
- bottom: 0,
803
- right: 0,
804
- x: 0,
805
- y: 0
806
- };
807
- function useBoundingClientRect(containerRef) {
808
- const [boundingClientRect, setBoundingClientRect] = createSignal(defaultRect);
809
- createEffect(() => {
810
- const container = containerRef();
811
- if (!container) return;
812
- const updateBoundingClientRect = () => {
813
- setBoundingClientRect(container.getBoundingClientRect() ?? defaultRect);
814
- };
815
- const resizeObserver = new ResizeObserver(updateBoundingClientRect);
816
- resizeObserver.observe(container);
817
- updateBoundingClientRect();
818
- return () => {
819
- resizeObserver.disconnect();
820
- };
821
- });
822
- return boundingClientRect;
823
- }
824
-
825
- const _tmpl$$3 = /*#__PURE__*/template(`<input step="0.01">`, 1);
847
+ const _tmpl$$4 = /*#__PURE__*/template(`<input step="0.01">`, 1);
826
848
  const ZoomSliderControl = props => {
827
849
  const propsWithDefaults = mergeProps({
828
850
  orientation: "horizontal"
829
851
  }, props);
830
852
  const dimension = createMemo(() => useViewPortDimension(props.dimensionName ?? propsWithDefaults.orientation));
831
853
  return (() => {
832
- const _el$ = _tmpl$$3.cloneNode(true);
854
+ const _el$ = _tmpl$$4.cloneNode(true);
833
855
  spread(_el$, mergeProps$1({
834
856
  get min() {
835
857
  return dimension().minZoom;
@@ -841,7 +863,10 @@ const ZoomSliderControl = props => {
841
863
  get value() {
842
864
  return dimension().zoom;
843
865
  },
844
- "onInput": event => dimension().onZoomChange?.(event.currentTarget.valueAsNumber),
866
+ "onInput": event => {
867
+ const value = event.currentTarget.valueAsNumber;
868
+ dimension().onZoomChange?.(value);
869
+ },
845
870
  "type": "range"
846
871
  }, () => ({
847
872
  orient: propsWithDefaults.orientation
@@ -871,17 +896,17 @@ const ZoomSliderControl = props => {
871
896
  })();
872
897
  };
873
898
 
874
- var css_248z = ".PianoRollTrackList-module_PianoRollTrackList__-sDf0 {\n position: relative;\n height: 100%;\n flex: 1;\n}\n\n.PianoRollTrackList-module_Track__mZP1K {\n border-width: 0px;\n position: absolute;\n box-sizing: border-box;\n width: 100%;\n border-color: #000;\n border-style: solid;\n border-right-width: 1px;\n border-bottom-width: 1px;\n cursor: \"pointer\";\n background: \"lightgrey\";\n}\n.PianoRollTrackList-module_Track__mZP1K.PianoRollTrackList-module_selected__0b-mf {\n background: \"gray\";\n}";
875
- var styles = {"PianoRollTrackList":"PianoRollTrackList-module_PianoRollTrackList__-sDf0","Track":"PianoRollTrackList-module_Track__mZP1K","selected":"PianoRollTrackList-module_selected__0b-mf"};
876
- styleInject(css_248z);
899
+ var css_248z$1 = ".PianoRollTrackList-module_PianoRollTrackList__-sDf0 {\n position: relative;\n height: 100%;\n flex: 1;\n}\n\n.PianoRollTrackList-module_Track__mZP1K {\n border-width: 0px;\n position: absolute;\n box-sizing: border-box;\n width: 100%;\n border-color: #000;\n border-style: solid;\n border-right-width: 1px;\n border-bottom-width: 1px;\n padding: 2px;\n cursor: pointer;\n background: lightgrey;\n}\n.PianoRollTrackList-module_Track__mZP1K.PianoRollTrackList-module_selected__0b-mf {\n background: gray;\n}";
900
+ var styles$1 = {"PianoRollTrackList":"PianoRollTrackList-module_PianoRollTrackList__-sDf0","Track":"PianoRollTrackList-module_Track__mZP1K","selected":"PianoRollTrackList-module_selected__0b-mf"};
901
+ styleInject(css_248z$1);
877
902
 
878
- const _tmpl$$2 = /*#__PURE__*/template(`<div></div>`, 2),
903
+ const _tmpl$$3 = /*#__PURE__*/template(`<div></div>`, 2),
879
904
  _tmpl$2$1 = /*#__PURE__*/template(`<div> </div>`, 2);
880
905
  const PianoRollTrackList = () => {
881
906
  const viewPort = createMemo(() => useViewPortDimension("verticalTracks"));
882
907
  const context = usePianoRollContext();
883
908
  return (() => {
884
- const _el$ = _tmpl$$2.cloneNode(true);
909
+ const _el$ = _tmpl$$3.cloneNode(true);
885
910
  insert(_el$, createComponent(Index, {
886
911
  get each() {
887
912
  return context.tracks;
@@ -900,8 +925,8 @@ const PianoRollTrackList = () => {
900
925
  insert(_el$2, () => track().name || "[unnamed track]", null);
901
926
  effect(_p$ => {
902
927
  const _v$ = {
903
- [styles["Track"]]: true,
904
- [styles["selected"]]: index === context.selectedTrackIndex
928
+ [styles$1["Track"]]: true,
929
+ [styles$1["selected"]]: index === context.selectedTrackIndex
905
930
  },
906
931
  _v$2 = track().name,
907
932
  _v$3 = `${virtualDimensions().offset}px`,
@@ -922,80 +947,228 @@ const PianoRollTrackList = () => {
922
947
  });
923
948
  }
924
949
  }));
925
- effect(() => className(_el$, styles.PianoRollTrackList));
950
+ effect(() => className(_el$, styles$1.PianoRollTrackList));
926
951
  return _el$;
927
952
  })();
928
953
  };
929
954
  delegateEvents(["click"]);
930
955
 
931
- const _tmpl$$1 = /*#__PURE__*/template(`<div><div></div></div>`, 4),
932
- _tmpl$2 = /*#__PURE__*/template(`<div></div>`, 2);
956
+ const PianoRollScrollZoomViewPort = props => {
957
+ const zoomFactor = 500;
958
+ const context = usePianoRollContext();
959
+ return createComponent(ScrollZoomViewPort, {
960
+ dimensions: {
961
+ horizontal: () => ({
962
+ pixelOffset: context.notesScrollerClientRect.left,
963
+ pixelSize: context.notesScrollerClientRect.width,
964
+ position: context.position,
965
+ range: context.duration,
966
+ zoom: context.zoom * (zoomFactor / context.notesScrollerClientRect.width),
967
+ onPositionChange: context.onPositionChange,
968
+ onZoomChange: zoom => context.onZoomChange?.(zoom / (zoomFactor / context.notesScrollerClientRect.width)),
969
+ minZoom: 1,
970
+ maxZoom: 500
971
+ }),
972
+ vertical: () => ({
973
+ pixelOffset: context.notesScrollerClientRect.top,
974
+ pixelSize: context.notesScrollerClientRect.height,
975
+ position: context.verticalPosition,
976
+ range: 128,
977
+ zoom: context.verticalZoom * (zoomFactor / context.notesScrollerClientRect.height),
978
+ onPositionChange: context.onVerticalPositionChange,
979
+ onZoomChange: verticalZoom => context.onVerticalZoomChange?.(verticalZoom / (zoomFactor / context.notesScrollerClientRect.height)),
980
+ minZoom: 1,
981
+ maxZoom: 10
982
+ }),
983
+ horizontalTracks: () => ({
984
+ pixelOffset: 0,
985
+ pixelSize: context.tracksScrollerClientRect.width
986
+ }),
987
+ verticalTracks: () => ({
988
+ pixelOffset: context.tracksScrollerClientRect.top,
989
+ pixelSize: context.tracksScrollerClientRect.height,
990
+ position: context.verticalTrackPosition,
991
+ range: context.tracks.length,
992
+ zoom: context.verticalTrackZoom * (zoomFactor / context.tracksScrollerClientRect.height),
993
+ onPositionChange: context.onVerticalTrackPositionChange,
994
+ onZoomChange: verticalTrackZoom => context.onVerticalTrackZoomChange?.(verticalTrackZoom / (zoomFactor / context.tracksScrollerClientRect.height)),
995
+ minZoom: 0.8,
996
+ maxZoom: 3
997
+ }),
998
+ horizontalKeys: () => ({
999
+ pixelSize: 60
1000
+ })
1001
+ },
1002
+ get children() {
1003
+ return props.children;
1004
+ }
1005
+ });
1006
+ };
1007
+
1008
+ const defaultRect = {
1009
+ left: 0,
1010
+ width: 0,
1011
+ top: 0,
1012
+ height: 0,
1013
+ bottom: 0,
1014
+ right: 0,
1015
+ x: 0,
1016
+ y: 0
1017
+ };
1018
+ function useBoundingClientRect(containerRef) {
1019
+ const [boundingClientRect, setBoundingClientRect] = createSignal(defaultRect);
1020
+ createEffect(() => {
1021
+ const container = containerRef();
1022
+ if (!container) return;
1023
+ const updateBoundingClientRect = () => {
1024
+ setBoundingClientRect(container.getBoundingClientRect() ?? defaultRect);
1025
+ };
1026
+ const resizeObserver = new ResizeObserver(updateBoundingClientRect);
1027
+ resizeObserver.observe(container);
1028
+ updateBoundingClientRect();
1029
+ return () => {
1030
+ resizeObserver.disconnect();
1031
+ };
1032
+ });
1033
+ return boundingClientRect;
1034
+ }
1035
+
1036
+ const PianoRollNotesScroller = props => {
1037
+ const context = usePianoRollContext();
1038
+ const [ref, setRef] = createSignal();
1039
+ const clientRect = useBoundingClientRect(ref);
1040
+ createEffect(() => {
1041
+ context.onNotesScrollerClientRectChange(clientRect());
1042
+ });
1043
+ return createComponent(ScrollZoomContainer, {
1044
+ ref: setRef,
1045
+ get verticalDimensionName() {
1046
+ return context.mode === "keys" ? "vertical" : "verticalTracks";
1047
+ },
1048
+ get children() {
1049
+ return props.children;
1050
+ }
1051
+ });
1052
+ };
1053
+
1054
+ const PianoRollTrackListScroller = props => {
1055
+ const context = usePianoRollContext();
1056
+ const [ref, setRef] = createSignal();
1057
+ const clientRect = useBoundingClientRect(ref);
1058
+ createEffect(() => {
1059
+ context.onTracksScrollerClientRectChange(clientRect());
1060
+ });
1061
+ return createComponent(ScrollZoomContainer, {
1062
+ ref: setRef,
1063
+ horizontalDimensionName: "horizontalTracks",
1064
+ verticalDimensionName: "verticalTracks",
1065
+ get showScrollbar() {
1066
+ return context.mode === "keys";
1067
+ },
1068
+ get children() {
1069
+ return props.children;
1070
+ }
1071
+ });
1072
+ };
1073
+
1074
+ var css_248z = ".PianoRollScale-module_PianoRollScale__5wGRm {\n position: relative;\n width: 100%;\n height: 100%;\n}\n\n.PianoRollScale-module_PianoRollScale-Time__03hGQ {\n position: absolute;\n box-sizing: border-box;\n top: 0px;\n height: 100%;\n border-left-style: solid;\n border-left-width: 0.5px;\n border-left-color: x#666;\n border-bottom-color: #333;\n border-bottom-style: solid;\n border-bottom-width: 0.5px;\n background: #777;\n display: flex;\n align-items: center;\n}\n\n.PianoRollScale-module_PianoRollScale-Label__aoRi6 {\n position: absolute;\n z-index: 1;\n color: #ccc;\n margin-left: 3px;\n font-size: 0.8em;\n}\n\n.PianoRollScale-module_PianoRollScale-Time__03hGQ.PianoRollScale-module_Highlighted__ziWn5 {\n background: #666;\n}\n\n.PianoRollScale-module_PianoRollScale-Time__03hGQ.PianoRollScale-module_HighlightedBorder__-OoZE {\n border-left-color: #111;\n}\n";
1075
+ var styles = {"PianoRollScale":"PianoRollScale-module_PianoRollScale__5wGRm","PianoRollScale-Time":"PianoRollScale-module_PianoRollScale-Time__03hGQ","PianoRollScale-Label":"PianoRollScale-module_PianoRollScale-Label__aoRi6","Highlighted":"PianoRollScale-module_Highlighted__ziWn5","HighlightedBorder":"PianoRollScale-module_HighlightedBorder__-OoZE"};
1076
+ styleInject(css_248z);
1077
+
1078
+ const _tmpl$$2 = /*#__PURE__*/template(`<div></div>`, 2);
1079
+ const PianoRollScale = () => {
1080
+ const horizontalViewPort = createMemo(() => useViewPortDimension("horizontal"));
1081
+ const grid = usePianoRollGrid();
1082
+ const context = usePianoRollContext();
1083
+ const updatePlayheadPosition = event => {
1084
+ const position = horizontalViewPort().calculatePosition(event.clientX);
1085
+ context.onPlayHeadPositionChange(position, event);
1086
+ };
1087
+ return (() => {
1088
+ const _el$ = _tmpl$$2.cloneNode(true);
1089
+ _el$.$$mousedown = event => {
1090
+ updatePlayheadPosition(event);
1091
+ const handleMouseUp = () => {
1092
+ window.removeEventListener("mousemove", updatePlayheadPosition);
1093
+ window.removeEventListener("mouseup", handleMouseUp);
1094
+ };
1095
+ window.addEventListener("mousemove", updatePlayheadPosition);
1096
+ window.addEventListener("mouseup", handleMouseUp);
1097
+ };
1098
+ insert(_el$, createComponent(Index, {
1099
+ get each() {
1100
+ return grid();
1101
+ },
1102
+ children: entry => {
1103
+ return createComponent(Show, {
1104
+ get when() {
1105
+ return horizontalViewPort().isVisible(entry().virtualDimensions);
1106
+ },
1107
+ get children() {
1108
+ const _el$2 = _tmpl$$2.cloneNode(true);
1109
+ insert(_el$2, createComponent(Show, {
1110
+ get when() {
1111
+ return entry().showLabel;
1112
+ },
1113
+ get children() {
1114
+ const _el$3 = _tmpl$$2.cloneNode(true);
1115
+ insert(_el$3, () => entry().label);
1116
+ effect(() => className(_el$3, styles["PianoRollScale-Label"]));
1117
+ return _el$3;
1118
+ }
1119
+ }));
1120
+ effect(_p$ => {
1121
+ const _v$ = {
1122
+ [styles["PianoRollScale-Time"]]: true,
1123
+ [styles["Highlighted"]]: entry().isHighlighted,
1124
+ [styles["HighlightedBorder"]]: entry().hasHighlightedBorder
1125
+ },
1126
+ _v$2 = `${entry().virtualDimensions.offset}px`,
1127
+ _v$3 = `${entry().virtualDimensions.size}px`;
1128
+ _p$._v$ = classList(_el$2, _v$, _p$._v$);
1129
+ _v$2 !== _p$._v$2 && _el$2.style.setProperty("left", _p$._v$2 = _v$2);
1130
+ _v$3 !== _p$._v$3 && _el$2.style.setProperty("width", _p$._v$3 = _v$3);
1131
+ return _p$;
1132
+ }, {
1133
+ _v$: undefined,
1134
+ _v$2: undefined,
1135
+ _v$3: undefined
1136
+ });
1137
+ return _el$2;
1138
+ }
1139
+ });
1140
+ }
1141
+ }));
1142
+ effect(() => className(_el$, styles.PianoRollScale));
1143
+ return _el$;
1144
+ })();
1145
+ };
1146
+ delegateEvents(["mousedown"]);
1147
+
1148
+ const _tmpl$$1 = /*#__PURE__*/template(`<div><button><div></div></button></div>`, 6),
1149
+ _tmpl$2 = /*#__PURE__*/template(`<div><div></div><div></div></div>`, 6),
1150
+ _tmpl$3 = /*#__PURE__*/template(`<div><div><div></div></div></div>`, 6),
1151
+ _tmpl$4 = /*#__PURE__*/template(`<div></div>`, 2);
933
1152
  const PianoRoll = allProps => {
934
1153
  const propsWithDefaults = mergeProps({
935
1154
  showAllTracks: false
936
1155
  }, allProps);
937
1156
  const [context, divProps] = splitContextProps(propsWithDefaults);
938
- const [notesScrollerRef, setNotesScrollerRef] = createSignal();
939
- const [tracksScrollerRef, setTracksScrollerRef] = createSignal();
940
- const notesScrollerClientRect = useBoundingClientRect(notesScrollerRef);
941
- const tracksScrollerClientRect = useBoundingClientRect(tracksScrollerRef);
942
- const zoomFactor = 500;
943
1157
  return createComponent(PianoRollContextProvider, {
944
1158
  value: context,
945
1159
  get children() {
946
- const _el$ = _tmpl$2.cloneNode(true);
1160
+ const _el$ = _tmpl$4.cloneNode(true);
947
1161
  spread(_el$, mergeProps$1(divProps, {
948
1162
  get ["class"]() {
949
- return styles$5.PianoRoll;
1163
+ return styles$6.PianoRoll;
950
1164
  }
951
1165
  }), false, true);
952
- insert(_el$, createComponent(ScrollZoomViewPort, {
953
- dimensions: {
954
- horizontal: () => ({
955
- pixelOffset: notesScrollerClientRect().left,
956
- pixelSize: notesScrollerClientRect().width,
957
- position: context.position,
958
- range: context.duration,
959
- zoom: context.zoom * (zoomFactor / notesScrollerClientRect().width),
960
- onPositionChange: context.onPositionChange,
961
- onZoomChange: zoom => context.onZoomChange?.(zoom / (zoomFactor / notesScrollerClientRect().width)),
962
- minZoom: 1,
963
- maxZoom: 500
964
- }),
965
- vertical: () => ({
966
- pixelOffset: notesScrollerClientRect().top,
967
- pixelSize: notesScrollerClientRect().height,
968
- position: context.verticalPosition,
969
- range: 128,
970
- zoom: context.verticalZoom * (zoomFactor / notesScrollerClientRect().height),
971
- onPositionChange: context.onVerticalPositionChange,
972
- onZoomChange: verticalZoom => context.onVerticalZoomChange?.(verticalZoom / (zoomFactor / notesScrollerClientRect().height)),
973
- minZoom: 1,
974
- maxZoom: 10
975
- }),
976
- horizontalTracks: () => ({
977
- pixelOffset: 0,
978
- pixelSize: tracksScrollerClientRect().width
979
- }),
980
- verticalTracks: () => ({
981
- pixelOffset: tracksScrollerClientRect().top,
982
- pixelSize: tracksScrollerClientRect().height,
983
- position: context.verticalTrackPosition,
984
- range: context.tracks.length,
985
- zoom: context.verticalTrackZoom * (zoomFactor / tracksScrollerClientRect().height),
986
- onPositionChange: context.onVerticalTrackPositionChange,
987
- onZoomChange: verticalTrackZoom => context.onVerticalTrackZoomChange?.(verticalTrackZoom / (zoomFactor / tracksScrollerClientRect().height)),
988
- minZoom: 0.8,
989
- maxZoom: 3
990
- }),
991
- horizontalKeys: () => ({
992
- pixelSize: 60
993
- })
994
- },
1166
+ insert(_el$, createComponent(PianoRollScrollZoomViewPort, {
995
1167
  get children() {
996
1168
  return [(() => {
997
- const _el$2 = _tmpl$$1.cloneNode(true),
998
- _el$3 = _el$2.firstChild;
1169
+ const _el$2 = _tmpl$3.cloneNode(true),
1170
+ _el$3 = _el$2.firstChild,
1171
+ _el$7 = _el$3.firstChild;
999
1172
  insert(_el$2, createComponent(ZoomSliderControl, {
1000
1173
  orientation: "vertical",
1001
1174
  dimensionName: "verticalTracks"
@@ -1005,20 +1178,37 @@ const PianoRoll = allProps => {
1005
1178
  return context.showTrackList;
1006
1179
  },
1007
1180
  get children() {
1008
- return createComponent(ScrollZoomContainer, {
1009
- ref: setTracksScrollerRef,
1010
- horizontalDimensionName: "horizontalTracks",
1011
- verticalDimensionName: "verticalTracks",
1012
- get showScrollbar() {
1013
- return context.mode === "keys";
1014
- },
1181
+ const _el$4 = _tmpl$$1.cloneNode(true),
1182
+ _el$5 = _el$4.firstChild,
1183
+ _el$6 = _el$5.firstChild;
1184
+ _el$4.style.setProperty("height", "30px");
1185
+ _el$4.style.setProperty("border-right", "1px black solid");
1186
+ _el$5.$$click = () => context.onModeChange(context.mode === "keys" ? "tracks" : "keys");
1187
+ _el$5.style.setProperty("font-size", "16px");
1188
+ _el$5.style.setProperty("line-height", "16px");
1189
+ _el$5.style.setProperty("cursor", "pointer");
1190
+ _el$5.style.setProperty("overflow", "hidden");
1191
+ insert(_el$6, () => context.mode === "keys" ? "≡" : "🎹");
1192
+ effect(() => setAttribute(_el$5, "title", context.mode === "keys" ? "Tracks Mode" : "Keys Mode"));
1193
+ return _el$4;
1194
+ }
1195
+ }), _el$7);
1196
+ _el$7.style.setProperty("flex", "1");
1197
+ _el$7.style.setProperty("display", "flex");
1198
+ _el$7.style.setProperty("overflow", "hidden");
1199
+ insert(_el$7, createComponent(Show, {
1200
+ get when() {
1201
+ return context.showTrackList;
1202
+ },
1203
+ get children() {
1204
+ return createComponent(PianoRollTrackListScroller, {
1015
1205
  get children() {
1016
1206
  return createComponent(PianoRollTrackList, {});
1017
1207
  }
1018
1208
  });
1019
1209
  }
1020
1210
  }), null);
1021
- insert(_el$3, createComponent(Show, {
1211
+ insert(_el$7, createComponent(Show, {
1022
1212
  get when() {
1023
1213
  return context.mode === "keys";
1024
1214
  },
@@ -1026,13 +1216,26 @@ const PianoRoll = allProps => {
1026
1216
  return createComponent(PianoRollKeys, {});
1027
1217
  }
1028
1218
  }), null);
1029
- insert(_el$2, createComponent(ScrollZoomContainer, {
1030
- ref: setNotesScrollerRef,
1031
- get verticalDimensionName() {
1032
- return context.mode === "keys" ? "vertical" : "verticalTracks";
1033
- },
1219
+ insert(_el$2, createComponent(PianoRollNotesScroller, {
1034
1220
  get children() {
1035
- return [memo(() => allProps.children), createComponent(PianoRollGrid, {}), createComponent(PianoRollNotes, {})];
1221
+ return [memo(() => allProps.children), (() => {
1222
+ const _el$8 = _tmpl$2.cloneNode(true),
1223
+ _el$9 = _el$8.firstChild,
1224
+ _el$10 = _el$9.nextSibling;
1225
+ _el$8.style.setProperty("width", "100%");
1226
+ _el$8.style.setProperty("display", "flex");
1227
+ _el$8.style.setProperty("height", "100%");
1228
+ _el$8.style.setProperty("flex-direction", "column");
1229
+ _el$8.style.setProperty("overflow", "hidden");
1230
+ _el$9.style.setProperty("height", "30px");
1231
+ insert(_el$9, createComponent(PianoRollScale, {}));
1232
+ _el$10.style.setProperty("flex", "1");
1233
+ _el$10.style.setProperty("position", "relative");
1234
+ _el$10.style.setProperty("overflow", "hidden");
1235
+ insert(_el$10, createComponent(PianoRollGrid, {}), null);
1236
+ insert(_el$10, createComponent(PianoRollNotes, {}), null);
1237
+ return _el$8;
1238
+ })()];
1036
1239
  }
1037
1240
  }), null);
1038
1241
  insert(_el$2, createComponent(ZoomSliderControl, {
@@ -1042,10 +1245,13 @@ const PianoRoll = allProps => {
1042
1245
  }
1043
1246
  }), null);
1044
1247
  effect(_p$ => {
1045
- const _v$ = styles$5.PianoRollContainer,
1046
- _v$2 = styles$5.PianoRollLeftColumn;
1248
+ const _v$ = styles$6.PianoRollContainer,
1249
+ _v$2 = {
1250
+ [styles$6.PianoRollLeftColumn]: true,
1251
+ [styles$6.showTrackList]: context.showTrackList
1252
+ };
1047
1253
  _v$ !== _p$._v$ && className(_el$2, _p$._v$ = _v$);
1048
- _v$2 !== _p$._v$2 && className(_el$3, _p$._v$2 = _v$2);
1254
+ _p$._v$2 = classList(_el$3, _v$2, _p$._v$2);
1049
1255
  return _p$;
1050
1256
  }, {
1051
1257
  _v$: undefined,
@@ -1056,7 +1262,7 @@ const PianoRoll = allProps => {
1056
1262
  orientation: "horizontal",
1057
1263
  get style() {
1058
1264
  return {
1059
- "margin-left": `${tracksScrollerClientRect().width + 24}px`,
1265
+ "margin-left": `${context.tracksScrollerClientRect.width + 24}px`,
1060
1266
  "margin-right": "24px"
1061
1267
  };
1062
1268
  }
@@ -1067,6 +1273,7 @@ const PianoRoll = allProps => {
1067
1273
  }
1068
1274
  });
1069
1275
  };
1276
+ delegateEvents(["click"]);
1070
1277
 
1071
1278
  const _tmpl$ = /*#__PURE__*/template(`<div class="PlayHead"></div>`, 2);
1072
1279
  const PlayHead = allProps => {
@@ -1096,11 +1303,9 @@ const PlayHead = allProps => {
1096
1303
  "onMouseDown": event => {
1097
1304
  event.preventDefault();
1098
1305
  event.stopPropagation();
1099
- const handleMouseMove = ({
1100
- clientX
1101
- }) => {
1102
- const newPosition = viewPort.calculatePosition(clientX);
1103
- props.onPositionChange?.(newPosition);
1306
+ const handleMouseMove = event => {
1307
+ const newPosition = viewPort.calculatePosition(event.clientX);
1308
+ props.onPositionChange?.(newPosition, event);
1104
1309
  };
1105
1310
  const handleMouseUp = () => {
1106
1311
  window.removeEventListener("mousemove", handleMouseMove);