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