solid-pianoroll 0.0.20 → 0.0.22

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,10 +29,88 @@ 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";
33
- var styles$5 = {"PianoRoll":"PianoRoll-module_PianoRoll__rkRYx","PianoRollContainer":"PianoRoll-module_PianoRollContainer__Oyxr7"};
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
34
  styleInject(css_248z$5);
35
35
 
36
+ const defaultState = {
37
+ ppq: 0,
38
+ mode: "keys",
39
+ position: 0,
40
+ zoom: 10,
41
+ verticalZoom: 5,
42
+ verticalPosition: 44,
43
+ verticalTrackZoom: 0.5,
44
+ verticalTrackPosition: 0,
45
+ gridDivision: 4,
46
+ snapToGrid: true,
47
+ duration: 0,
48
+ tracks: [],
49
+ selectedTrackIndex: 0,
50
+ pressedKeys: {}
51
+ };
52
+ 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"];
54
+ const createPianoRollstate = initialState => {
55
+ const [state, setState] = store.createStore({
56
+ ...defaultState,
57
+ ...initialState
58
+ });
59
+ const handlers = Object.fromEntries(Object.entries(state).map(entry => {
60
+ return [propNameToHandlerName(entry[0]), value => setState(entry[0], value)];
61
+ }));
62
+ const updateNotes = async (trackIndex, getNotes) => {
63
+ const track = state.tracks[trackIndex];
64
+ if (!track) return;
65
+ const notes = track.notes;
66
+ handlers.onTracksChange([...state.tracks.slice(0, trackIndex), {
67
+ ...track,
68
+ notes: getNotes(notes)
69
+ }, ...state.tracks.slice(trackIndex + 1)]);
70
+ };
71
+ const onNoteChange = (trackIndex, noteIndex, note) => {
72
+ updateNotes(trackIndex, notes => [...notes.slice(0, noteIndex), note, ...notes.slice(noteIndex + 1)]);
73
+ };
74
+ const onInsertNote = (trackIndex, note) => {
75
+ const track = state.tracks[trackIndex];
76
+ if (!track) return -1;
77
+ const notes = track.notes;
78
+ const newNoteIndex = Math.max(notes.findIndex(({
79
+ ticks
80
+ }) => ticks > note.ticks), 0);
81
+ updateNotes(trackIndex, notes => [...notes.slice(0, newNoteIndex), note, ...notes.slice(newNoteIndex)]);
82
+ return newNoteIndex;
83
+ };
84
+ const onRemoveNote = (trackIndex, noteIndex) => {
85
+ updateNotes(trackIndex, notes => [...notes.slice(0, noteIndex), ...notes.slice(noteIndex + 1)]);
86
+ };
87
+ const updateKeyPressedState = (trackIndex, keyNumber, value) => {
88
+ handlers.onPressedKeysChange?.({
89
+ ...state.pressedKeys,
90
+ [trackIndex]: {
91
+ ...state.pressedKeys[trackIndex],
92
+ [keyNumber]: value
93
+ }
94
+ });
95
+ };
96
+ const onNoteDown = (trackIndex, keyNumber) => {
97
+ updateKeyPressedState(trackIndex, keyNumber, true);
98
+ };
99
+ const onNoteUp = (trackIndex, keyNumber) => {
100
+ updateKeyPressedState(trackIndex, keyNumber, false);
101
+ };
102
+ const isKeyDown = (trackIndex, keyNumber) => !!state.pressedKeys[trackIndex]?.[keyNumber];
103
+ return solidJs.mergeProps(state, {
104
+ ...handlers,
105
+ onNoteChange,
106
+ onInsertNote,
107
+ onRemoveNote,
108
+ onNoteDown,
109
+ onNoteUp,
110
+ isKeyDown
111
+ });
112
+ };
113
+
36
114
  const PianoRollContext = solidJs.createContext();
37
115
  const PianoRollContextProvider = PianoRollContext.Provider;
38
116
  const usePianoRollContext = () => {
@@ -40,9 +118,9 @@ const usePianoRollContext = () => {
40
118
  if (!context) throw new Error("No PianoRollContext found");
41
119
  return context;
42
120
  };
43
- const splitContextProps = allProps => solidJs.splitProps(allProps, ["ppq", "mode", "position", "zoom", "verticalZoom", "verticalPosition", "verticalTrackZoom", "verticalTrackPosition", "gridDivision", "snapToGrid", "duration", "tracks", "selectedTrackIndex", "pressedKeys", "onPpqChange", "onModeChange", "onPositionChange", "onZoomChange", "onVerticalZoomChange", "onVerticalPositionChange", "onVerticalTrackZoomChange", "onVerticalTrackPositionChange", "onGridDivisionChange", "onSnapToGridChange", "onDurationChange", "onTracksChange", "onNoteChange", "onInsertNote", "onRemoveNote", "onSelectedTrackIndexChange", "onPressedKeysChange", "showAllTracks", "showTrackList"]);
121
+ const splitContextProps = allProps => solidJs.splitProps(allProps, [...pianoRollStatePropNames, "showAllTracks", "showTrackList"]);
44
122
 
45
- 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}\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}\n\n.PianoRollKeys-module_white__fR2Lm {\n background: white;\n border-top-width: 0.5px;\n border-bottom-width: 0.5px;\n}\n\n.PianoRollKeys-module_down__GcNeZ {\n transition: none;\n background: red;\n}\n";
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";
46
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"};
47
125
  styleInject(css_248z$4);
48
126
 
@@ -285,13 +363,7 @@ const PianoRollKeys = () => {
285
363
  return web.createComponent(solidJs.Index, {
286
364
  each: keys,
287
365
  children: key => {
288
- const isDown = solidJs.createMemo(() => context.pressedKeys.includes(key().number));
289
- const handleKeyDown = () => {
290
- context.onPressedKeysChange?.([...context.pressedKeys, key().number]);
291
- };
292
- const handleKeyUp = () => {
293
- context.onPressedKeysChange?.([...context.pressedKeys].filter(number => number !== key().number));
294
- };
366
+ const isDown = solidJs.createMemo(() => context.isKeyDown(context.selectedTrackIndex, key().number));
295
367
  const virtualDimensions = solidJs.createMemo(() => verticalViewPort().calculatePixelDimensions(127 - key().number, 1));
296
368
  const previousIsBlack = blackKeys.includes((key().number - 1) % 12);
297
369
  const nextIsBlack = blackKeys.includes((key().number + 1) % 12);
@@ -303,69 +375,47 @@ const PianoRollKeys = () => {
303
375
  const _el$2 = _tmpl$$6.cloneNode(true);
304
376
  _el$2.addEventListener("mouseleave", () => {
305
377
  if (isMouseDown()) {
306
- handleKeyUp();
378
+ context.onNoteUp(context.selectedTrackIndex, key().number);
307
379
  }
308
380
  });
309
381
  _el$2.addEventListener("mouseenter", () => {
310
382
  if (isMouseDown()) {
311
- handleKeyDown();
383
+ context.onNoteDown(context.selectedTrackIndex, key().number);
312
384
  }
313
385
  });
314
386
  _el$2.$$mouseup = () => {
315
- handleKeyUp();
387
+ context.onNoteUp(context.selectedTrackIndex, key().number);
316
388
  };
317
389
  _el$2.$$mousedown = () => {
318
390
  setIsMouseDown(true);
319
- handleKeyDown();
391
+ context.onNoteDown(context.selectedTrackIndex, key().number);
320
392
  };
321
- web.insert(_el$2, web.createComponent(solidJs.Show, {
322
- get when() {
323
- return key().isBlack && false;
324
- },
325
- get children() {
326
- const _el$3 = _tmpl$$6.cloneNode(true);
327
- web.effect(_p$ => {
328
- const _v$3 = styles$4["black-separator"],
329
- _v$4 = `${virtualDimensions().offset}px`,
330
- _v$5 = `${virtualDimensions().size / 2 - 0.25}px`;
331
- _v$3 !== _p$._v$3 && web.className(_el$3, _p$._v$3 = _v$3);
332
- _v$4 !== _p$._v$4 && _el$3.style.setProperty("top", _p$._v$4 = _v$4);
333
- _v$5 !== _p$._v$5 && _el$3.style.setProperty("height", _p$._v$5 = _v$5);
334
- return _p$;
335
- }, {
336
- _v$3: undefined,
337
- _v$4: undefined,
338
- _v$5: undefined
339
- });
340
- return _el$3;
341
- }
342
- }));
343
393
  web.effect(_p$ => {
344
- const _v$6 = {
394
+ const _v$3 = {
345
395
  [styles$4["Key"]]: true,
346
396
  [styles$4["black"]]: key().isBlack,
347
397
  [styles$4["white"]]: !key().isBlack,
348
398
  [styles$4["down"]]: isDown()
349
399
  },
350
- _v$7 = key().name,
351
- _v$8 = key().number % 12,
352
- _v$9 = `${virtualDimensions().offset - (!key().isBlack && nextIsBlack ? virtualDimensions().size / 2 : 0)}px`,
353
- _v$10 = `${virtualDimensions().size + (!key().isBlack && nextIsBlack ? virtualDimensions().size / 2 : 0) + (!key().isBlack && previousIsBlack ? virtualDimensions().size / 2 : 0)}px`,
354
- _v$11 = [`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(", ");
355
- _p$._v$6 = web.classList(_el$2, _v$6, _p$._v$6);
356
- _v$7 !== _p$._v$7 && web.setAttribute(_el$2, "title", _p$._v$7 = _v$7);
357
- _v$8 !== _p$._v$8 && web.setAttribute(_el$2, "data-index", _p$._v$8 = _v$8);
358
- _v$9 !== _p$._v$9 && _el$2.style.setProperty("top", _p$._v$9 = _v$9);
359
- _v$10 !== _p$._v$10 && _el$2.style.setProperty("height", _p$._v$10 = _v$10);
360
- _v$11 !== _p$._v$11 && _el$2.style.setProperty("box-shadow", _p$._v$11 = _v$11);
400
+ _v$4 = key().name,
401
+ _v$5 = key().number % 12,
402
+ _v$6 = `${virtualDimensions().offset - virtualDimensions().size * (!key().isBlack ? nextIsBlack ? 1 / 2 : 0 : 0)}px`,
403
+ _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(", ");
405
+ _p$._v$3 = web.classList(_el$2, _v$3, _p$._v$3);
406
+ _v$4 !== _p$._v$4 && web.setAttribute(_el$2, "title", _p$._v$4 = _v$4);
407
+ _v$5 !== _p$._v$5 && web.setAttribute(_el$2, "data-index", _p$._v$5 = _v$5);
408
+ _v$6 !== _p$._v$6 && _el$2.style.setProperty("top", _p$._v$6 = _v$6);
409
+ _v$7 !== _p$._v$7 && _el$2.style.setProperty("height", _p$._v$7 = _v$7);
410
+ _v$8 !== _p$._v$8 && _el$2.style.setProperty("box-shadow", _p$._v$8 = _v$8);
361
411
  return _p$;
362
412
  }, {
413
+ _v$3: undefined,
414
+ _v$4: undefined,
415
+ _v$5: undefined,
363
416
  _v$6: undefined,
364
417
  _v$7: undefined,
365
- _v$8: undefined,
366
- _v$9: undefined,
367
- _v$10: undefined,
368
- _v$11: undefined
418
+ _v$8: undefined
369
419
  });
370
420
  return _el$2;
371
421
  }
@@ -408,72 +458,96 @@ const PianoRollNotes = props => {
408
458
  const verticalViewPort = solidJs.createMemo(() => useViewPortDimension(context.mode === "keys" ? "vertical" : "verticalTracks"));
409
459
  const horizontalViewPort = solidJs.createMemo(() => useViewPortDimension("horizontal"));
410
460
  const gridDivisionTicks = solidJs.createMemo(() => context.ppq * 4 / context.gridDivision);
461
+ const [isDragging, setIsDragging] = solidJs.createSignal(false);
462
+ const [noteDragMode, setNoteDragMode] = solidJs.createSignal();
463
+ const [currentNoteIndex, setCurrentNoteIndex] = solidJs.createSignal(-1);
464
+ const [currentNoteTrackIndex, setCurrentNoteTrackIndex] = solidJs.createSignal(-1);
465
+ const [diffPosition, setDiffPosition] = solidJs.createSignal(0);
466
+ const [getInitialNote, setInitialNote] = solidJs.createSignal();
411
467
  const snapValueToGridIfEnabled = (value, altKey) => context.snapToGrid && !altKey ? Math.round(value / gridDivisionTicks()) * gridDivisionTicks() : value;
412
- const insertOrUpdateNote = event => {
413
- const position = horizontalViewPort().calculatePosition(event.clientX);
414
- const existingNote = currentNote();
415
- const midi = context.mode === "keys" ? 127 - Math.floor(verticalViewPort().calculatePosition(event.clientY)) : existingNote?.midi ?? 60;
416
- const targetTrackIndex = context.mode === "tracks" ? clamp(Math.floor(verticalViewPort().calculatePosition(event.clientY)), 0, context.tracks.length - 1) : context.selectedTrackIndex ?? 0;
417
- const eventPositionTicks = Math.floor(position / gridDivisionTicks()) * gridDivisionTicks();
418
- const velocity = 127;
419
- const ticks = existingNote?.ticks ?? eventPositionTicks;
420
- const durationTicks = existingNote?.ticks ? eventPositionTicks - existingNote?.ticks : gridDivisionTicks();
421
- const note = {
468
+ const calculateNoteDragValues = event => {
469
+ const targetTrackIndex = context.mode === "tracks" ? clamp(Math.floor(verticalViewPort().calculatePosition(event.clientY)), 0, context.tracks.length - 1) : currentNoteTrackIndex() > -1 ? currentNoteTrackIndex() : context.selectedTrackIndex;
470
+ const midi = context.mode === "keys" ? Math.floor(128 - verticalViewPort().calculatePosition(event.clientY)) : getInitialNote()?.midi ?? 60;
471
+ const horiontalPosition = horizontalViewPort().calculatePosition(event.clientX);
472
+ return {
473
+ targetTrackIndex,
422
474
  midi,
423
- ticks,
424
- durationTicks,
425
- velocity
475
+ horiontalPosition
426
476
  };
427
- if (existingNote) {
428
- context.onNoteChange?.(currentNoteTrackIndex(), currentNoteIndex(), note);
429
- return currentNoteIndex();
477
+ };
478
+ const handleMouseMove = mouseMoveEvent => {
479
+ const note = getInitialNote();
480
+ if (!note) return;
481
+ const {
482
+ targetTrackIndex,
483
+ midi,
484
+ horiontalPosition
485
+ } = calculateNoteDragValues(mouseMoveEvent);
486
+ const ticks = snapValueToGridIfEnabled(horiontalPosition - diffPosition(), mouseMoveEvent.altKey);
487
+ const updatedNote = {
488
+ ...note,
489
+ midi,
490
+ ...(noteDragMode() === "move" && {
491
+ ticks
492
+ }),
493
+ ...(noteDragMode() === "trimStart" && {
494
+ 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)
496
+ }),
497
+ ...(noteDragMode() === "trimEnd" && {
498
+ ticks: ticks < note.ticks ? ticks : note.ticks,
499
+ durationTicks: ticks < note.ticks ? note.ticks - ticks : snapValueToGridIfEnabled(horiontalPosition - note.ticks, mouseMoveEvent.altKey)
500
+ })
501
+ };
502
+ const previousTrackIndex = currentNoteTrackIndex();
503
+ const previousNoteIndex = currentNoteIndex();
504
+ if (targetTrackIndex === previousTrackIndex) {
505
+ context.onNoteChange?.(previousTrackIndex, previousNoteIndex, updatedNote);
430
506
  } else {
431
- return context.onInsertNote?.(targetTrackIndex, note) ?? -1;
507
+ if (context.onInsertNote) {
508
+ context.onRemoveNote?.(previousTrackIndex, previousNoteIndex);
509
+ const newNoteIndex = context.onInsertNote(targetTrackIndex, updatedNote);
510
+ setCurrentNoteTrackIndex(targetTrackIndex);
511
+ setCurrentNoteIndex(newNoteIndex);
512
+ }
432
513
  }
433
514
  };
434
- const [isDragging, setIsDragging] = solidJs.createSignal(false);
435
- const [noteDragMode, setNoteDragMode] = solidJs.createSignal();
436
- const [currentNoteIndex, setCurrentNoteIndex] = solidJs.createSignal(-1);
437
- const [currentNoteTrackIndex, setCurrentNoteTrackIndex] = solidJs.createSignal(-1);
438
- const [isMouseDown, setIsMouseDown] = solidJs.createSignal(false);
439
- const currentNote = solidJs.createMemo(() => context.tracks[currentNoteTrackIndex()]?.notes[currentNoteIndex()]);
515
+ const stopDragging = () => {
516
+ window.removeEventListener("mousemove", handleMouseMove);
517
+ window.removeEventListener("mouseup", stopDragging);
518
+ setIsDragging(false);
519
+ };
520
+ const startDragging = () => {
521
+ window.addEventListener("mousemove", handleMouseMove);
522
+ window.addEventListener("mouseup", stopDragging);
523
+ };
440
524
  const getClasses = noteDragMode => {
441
525
  return noteDragMode ? [styles$2.Note, styles$2[noteDragMode]] : [styles$2.Note];
442
526
  };
443
527
  return (() => {
444
528
  const _el$ = _tmpl$$5.cloneNode(true);
445
- _el$.$$click = event => {
446
- if (currentNote()) {
447
- setCurrentNoteIndex(-1);
448
- return;
449
- }
450
- if (!event.altKey) return;
451
- insertOrUpdateNote(event);
452
- };
453
- _el$.$$dblclick = event => {
454
- insertOrUpdateNote(event);
455
- };
456
- _el$.$$mouseup = event => {
457
- setIsMouseDown(false);
458
- if (!event.altKey) return;
459
- insertOrUpdateNote(event);
460
- };
461
- _el$.$$mousemove = event => {
462
- if (isDragging()) return;
463
- if (!isMouseDown()) {
464
- setNoteDragMode(undefined);
465
- return;
466
- }
467
- const index = insertOrUpdateNote(event);
468
- setCurrentNoteIndex(index);
469
- };
470
- _el$.$$mousedown = event => {
471
- solidJs.batch(() => {
472
- setIsMouseDown(true);
473
- const index = insertOrUpdateNote(event);
474
- setCurrentNoteTrackIndex(context.selectedTrackIndex ?? 0);
475
- setCurrentNoteIndex(index);
476
- });
529
+ _el$.$$mousedown = mouseDownEvent => {
530
+ const {
531
+ targetTrackIndex,
532
+ midi,
533
+ horiontalPosition
534
+ } = calculateNoteDragValues(mouseDownEvent);
535
+ const ticks = snapValueToGridIfEnabled(horiontalPosition, mouseDownEvent.altKey);
536
+ const durationTicks = gridDivisionTicks();
537
+ const newNote = {
538
+ midi,
539
+ ticks,
540
+ durationTicks,
541
+ velocity: 100
542
+ };
543
+ const newNoteIndex = context.onInsertNote(targetTrackIndex, newNote);
544
+ setIsDragging(true);
545
+ setCurrentNoteTrackIndex(targetTrackIndex);
546
+ setCurrentNoteIndex(newNoteIndex);
547
+ setInitialNote(newNote);
548
+ setDiffPosition(0);
549
+ setNoteDragMode("trimEnd");
550
+ startDragging();
477
551
  };
478
552
  const _ref$ = props.ref;
479
553
  typeof _ref$ === "function" ? web.use(_ref$, _el$) : props.ref = _el$;
@@ -502,55 +576,13 @@ const PianoRollNotes = props => {
502
576
  const _el$2 = _tmpl$$5.cloneNode(true);
503
577
  _el$2.$$mousedown = event => {
504
578
  event.stopPropagation();
579
+ const initialPosition = horizontalViewPort().calculatePosition(event.clientX);
580
+ setDiffPosition(noteDragMode() === "trimEnd" ? -(note.ticks + note.durationTicks - initialPosition) : initialPosition - note.ticks);
505
581
  setIsDragging(true);
506
582
  setCurrentNoteIndex(noteIndex());
507
583
  setCurrentNoteTrackIndex(trackIndex());
508
- const initialPosition = horizontalViewPort().calculatePosition(event.clientX);
509
- const diffPosition = initialPosition - note.ticks;
510
- const handleMouseMove = mouseMoveEvent => {
511
- const note = currentNote();
512
- if (!note) return;
513
- const ticks = snapValueToGridIfEnabled(Math.max(horizontalViewPort().calculatePosition(mouseMoveEvent.clientX) - diffPosition, 0), mouseMoveEvent.altKey);
514
- const updatedNote = {
515
- ...note,
516
- ...(noteDragMode() === "move" && context.mode === "keys" && {
517
- midi: Math.round(127 - verticalViewPort().calculatePosition(mouseMoveEvent.clientY))
518
- }),
519
- ...((noteDragMode() === "move" || noteDragMode() === "trimStart") && {
520
- ticks
521
- }),
522
- ...(noteDragMode() === "trimStart" && {
523
- durationTicks: note.durationTicks + note.ticks - ticks
524
- }),
525
- ...(noteDragMode() === "trimEnd" && {
526
- durationTicks: snapValueToGridIfEnabled(horizontalViewPort().calculatePosition(mouseMoveEvent.clientX) - note.ticks, mouseMoveEvent.altKey)
527
- })
528
- };
529
- const previousTrackIndex = currentNoteTrackIndex();
530
- const previousNoteIndex = currentNoteIndex();
531
- const targetTrackIndex = context.mode === "tracks" ? clamp(Math.floor(verticalViewPort().calculatePosition(mouseMoveEvent.clientY)), 0, context.tracks.length - 1) : previousTrackIndex;
532
- if (targetTrackIndex === previousTrackIndex) {
533
- context.onNoteChange?.(previousTrackIndex, previousNoteIndex, updatedNote);
534
- } else {
535
- solidJs.batch(() => {
536
- if (context.onInsertNote) {
537
- context.onRemoveNote?.(previousTrackIndex, previousNoteIndex);
538
- const newNoteIndex = context.onInsertNote(targetTrackIndex, updatedNote);
539
- setCurrentNoteTrackIndex(targetTrackIndex);
540
- setCurrentNoteIndex(newNoteIndex);
541
- }
542
- });
543
- }
544
- };
545
- const handleMouseUp = () => {
546
- setIsDragging(false);
547
- setCurrentNoteIndex(-1);
548
- setCurrentNoteTrackIndex(-1);
549
- window.removeEventListener("mousemove", handleMouseMove);
550
- window.removeEventListener("mouseup", handleMouseUp);
551
- };
552
- window.addEventListener("mousemove", handleMouseMove);
553
- window.addEventListener("mouseup", handleMouseUp);
584
+ setInitialNote(note);
585
+ startDragging();
554
586
  };
555
587
  _el$2.$$dblclick = event => {
556
588
  event.stopPropagation();
@@ -602,7 +634,7 @@ const PianoRollNotes = props => {
602
634
  return _el$;
603
635
  })();
604
636
  };
605
- web.delegateEvents(["mousedown", "mousemove", "mouseup", "dblclick", "click"]);
637
+ web.delegateEvents(["mousedown", "mousemove", "dblclick"]);
606
638
 
607
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";
608
640
  var styles$1 = {"PianoRollGrid":"PianoRollGrid-module_PianoRollGrid__tG119","PianoRollGrid-Row":"PianoRollGrid-module_PianoRollGrid-Row__nRMCs","PianoRollGrid-Time":"PianoRollGrid-module_PianoRollGrid-Time__jLz3E"};
@@ -841,8 +873,8 @@ const ZoomSliderControl = props => {
841
873
  })();
842
874
  };
843
875
 
844
- var css_248z = ".PianoRollTrackList-module_PianoRollTrackList__pyuxQ {\n position: relative;\n height: 100%;\n flex: 1;\n}\n\n.PianoRollTrackList-module_Track__VZFIg {\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}\n";
845
- var styles = {"PianoRollTrackList":"PianoRollTrackList-module_PianoRollTrackList__pyuxQ","Track":"PianoRollTrackList-module_Track__VZFIg"};
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"};
846
878
  styleInject(css_248z);
847
879
 
848
880
  const _tmpl$$2 = /*#__PURE__*/web.template(`<div></div>`, 2),
@@ -866,29 +898,26 @@ const PianoRollTrackList = () => {
866
898
  const _el$2 = _tmpl$2$1.cloneNode(true),
867
899
  _el$3 = _el$2.firstChild;
868
900
  _el$2.$$click = () => context.onSelectedTrackIndexChange?.(index);
869
- _el$2.style.setProperty("cursor", "pointer");
870
901
  web.insert(_el$2, index, _el$3);
871
902
  web.insert(_el$2, () => track().name || "[unnamed track]", null);
872
903
  web.effect(_p$ => {
873
904
  const _v$ = {
874
- [styles["Track"]]: true
905
+ [styles["Track"]]: true,
906
+ [styles["selected"]]: index === context.selectedTrackIndex
875
907
  },
876
908
  _v$2 = track().name,
877
909
  _v$3 = `${virtualDimensions().offset}px`,
878
- _v$4 = `${virtualDimensions().size}px`,
879
- _v$5 = index === context.selectedTrackIndex ? "gray" : "lightgrey";
910
+ _v$4 = `${virtualDimensions().size}px`;
880
911
  _p$._v$ = web.classList(_el$2, _v$, _p$._v$);
881
912
  _v$2 !== _p$._v$2 && web.setAttribute(_el$2, "title", _p$._v$2 = _v$2);
882
913
  _v$3 !== _p$._v$3 && _el$2.style.setProperty("top", _p$._v$3 = _v$3);
883
914
  _v$4 !== _p$._v$4 && _el$2.style.setProperty("height", _p$._v$4 = _v$4);
884
- _v$5 !== _p$._v$5 && _el$2.style.setProperty("background", _p$._v$5 = _v$5);
885
915
  return _p$;
886
916
  }, {
887
917
  _v$: undefined,
888
918
  _v$2: undefined,
889
919
  _v$3: undefined,
890
- _v$4: undefined,
891
- _v$5: undefined
920
+ _v$4: undefined
892
921
  });
893
922
  return _el$2;
894
923
  }
@@ -973,9 +1002,6 @@ const PianoRoll = allProps => {
973
1002
  orientation: "vertical",
974
1003
  dimensionName: "verticalTracks"
975
1004
  }), _el$3);
976
- _el$3.style.setProperty("display", "flex");
977
- _el$3.style.setProperty("width", "30%");
978
- _el$3.style.setProperty("max-width", "250px");
979
1005
  web.insert(_el$3, web.createComponent(solidJs.Show, {
980
1006
  get when() {
981
1007
  return context.showTrackList;
@@ -1017,7 +1043,16 @@ const PianoRoll = allProps => {
1017
1043
  return context.mode !== "keys";
1018
1044
  }
1019
1045
  }), null);
1020
- web.effect(() => web.className(_el$2, styles$5.PianoRollContainer));
1046
+ web.effect(_p$ => {
1047
+ const _v$ = styles$5.PianoRollContainer,
1048
+ _v$2 = styles$5.PianoRollLeftColumn;
1049
+ _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);
1051
+ return _p$;
1052
+ }, {
1053
+ _v$: undefined,
1054
+ _v$2: undefined
1055
+ });
1021
1056
  return _el$2;
1022
1057
  })(), web.createComponent(ZoomSliderControl, {
1023
1058
  orientation: "horizontal",
@@ -1037,19 +1072,15 @@ const PianoRoll = allProps => {
1037
1072
 
1038
1073
  const _tmpl$ = /*#__PURE__*/web.template(`<div class="PlayHead"></div>`, 2);
1039
1074
  const PlayHead = allProps => {
1040
- const propsWithDefauls = solidJs.mergeProps({
1041
- playHeadPosition: 0,
1042
- sync: false
1043
- }, allProps);
1044
- const [props, divProps] = solidJs.splitProps(propsWithDefauls, ["playHeadPosition", "sync", "onPlayHeadPositionChange", "onPositionChange"]);
1045
- const viewPort = useViewPortDimension("horizontal");
1075
+ const [props, divProps] = solidJs.splitProps(allProps, ["position", "sync", "onPositionChange", "dimensionName"]);
1076
+ const viewPort = useViewPortDimension(props.dimensionName ?? "horizontal");
1046
1077
  solidJs.createEffect(() => {
1047
1078
  if (!props.sync) return;
1048
1079
  const maxPosition = viewPort.range;
1049
- const newPosition = clamp(props.playHeadPosition - viewPort.range / viewPort.zoom / 2, 0, maxPosition);
1050
- props.onPositionChange?.(newPosition);
1080
+ const newPosition = clamp(props.position - viewPort.range / viewPort.zoom / 2, 0, maxPosition);
1081
+ viewPort.onPositionChange?.(newPosition);
1051
1082
  });
1052
- const leftPosition = solidJs.createMemo(() => viewPort.calculatePixelOffset(props.playHeadPosition));
1083
+ const leftPosition = solidJs.createMemo(() => viewPort.calculatePixelOffset(props.position));
1053
1084
  return (() => {
1054
1085
  const _el$ = _tmpl$.cloneNode(true);
1055
1086
  web.spread(_el$, web.mergeProps(divProps, {
@@ -1068,14 +1099,10 @@ const PlayHead = allProps => {
1068
1099
  event.preventDefault();
1069
1100
  event.stopPropagation();
1070
1101
  const handleMouseMove = ({
1071
- movementX
1102
+ clientX
1072
1103
  }) => {
1073
- const {
1074
- parentElement
1075
- } = event.currentTarget;
1076
- if (!parentElement) return;
1077
- const newPosition = viewPort.calculatePosition(leftPosition() + movementX);
1078
- props.onPlayHeadPositionChange?.(newPosition);
1104
+ const newPosition = viewPort.calculatePosition(clientX);
1105
+ props.onPositionChange?.(newPosition);
1079
1106
  };
1080
1107
  const handleMouseUp = () => {
1081
1108
  window.removeEventListener("mousemove", handleMouseMove);
@@ -1113,84 +1140,6 @@ const useNotes = () => {
1113
1140
  };
1114
1141
  };
1115
1142
 
1116
- const createPianoRollstate = defaultState => {
1117
- const [state, setState] = store.createStore({
1118
- ppq: 0,
1119
- mode: "keys",
1120
- position: 0,
1121
- zoom: 10,
1122
- verticalZoom: 5,
1123
- verticalPosition: 44,
1124
- verticalTrackZoom: 0.5,
1125
- verticalTrackPosition: 0,
1126
- gridDivision: 4,
1127
- snapToGrid: true,
1128
- duration: 0,
1129
- tracks: [],
1130
- selectedTrackIndex: 0,
1131
- pressedKeys: [],
1132
- ...defaultState
1133
- });
1134
- const updateNotes = async (trackIndex, getNotes) => {
1135
- const track = state.tracks[trackIndex];
1136
- if (!track) return;
1137
- const notes = track.notes;
1138
- onTracksChange([...state.tracks.slice(0, trackIndex), {
1139
- ...track,
1140
- notes: getNotes(notes)
1141
- }, ...state.tracks.slice(trackIndex + 1)]);
1142
- };
1143
- const onNoteChange = (trackIndex, noteIndex, note) => {
1144
- updateNotes(trackIndex, notes => [...notes.slice(0, noteIndex), note, ...notes.slice(noteIndex + 1)]);
1145
- };
1146
- const onInsertNote = (trackIndex, note) => {
1147
- const track = state.tracks[trackIndex];
1148
- if (!track) return -1;
1149
- const notes = track.notes;
1150
- const newNoteIndex = Math.max(notes.findIndex(({
1151
- ticks
1152
- }) => ticks > note.ticks), 0);
1153
- updateNotes(trackIndex, notes => [...notes.slice(0, newNoteIndex), note, ...notes.slice(newNoteIndex)]);
1154
- return newNoteIndex;
1155
- };
1156
- const onRemoveNote = (trackIndex, noteIndex) => {
1157
- updateNotes(trackIndex, notes => [...notes.slice(0, noteIndex), ...notes.slice(noteIndex + 1)]);
1158
- };
1159
- const onPpqChange = ppq => setState("ppq", ppq);
1160
- const onModeChange = mode => setState("mode", mode);
1161
- const onPositionChange = position => setState("position", position);
1162
- const onZoomChange = zoom => setState("zoom", zoom);
1163
- const onVerticalZoomChange = verticalZoom => setState("verticalZoom", verticalZoom);
1164
- const onVerticalPositionChange = verticalPosition => setState("verticalPosition", verticalPosition);
1165
- const onVerticalTrackZoomChange = verticalTrackZoom => setState("verticalTrackZoom", verticalTrackZoom);
1166
- const onVerticalTrackPositionChange = verticalTrackPosition => setState("verticalTrackPosition", verticalTrackPosition);
1167
- const onGridDivisionChange = gridDivision => setState("gridDivision", gridDivision);
1168
- const onSnapToGridChange = snapToGrid => setState("snapToGrid", snapToGrid);
1169
- const onDurationChange = duration => setState("duration", duration);
1170
- const onTracksChange = tracks => setState("tracks", tracks);
1171
- const onSelectedTrackIndexChange = selectedTrackIndex => setState("selectedTrackIndex", selectedTrackIndex);
1172
- const onPressedKeysChange = pressedKeys => setState("pressedKeys", pressedKeys);
1173
- return solidJs.mergeProps(state, {
1174
- onPpqChange,
1175
- onModeChange,
1176
- onPositionChange,
1177
- onZoomChange,
1178
- onVerticalZoomChange,
1179
- onVerticalPositionChange,
1180
- onVerticalTrackZoomChange,
1181
- onVerticalTrackPositionChange,
1182
- onGridDivisionChange,
1183
- onSnapToGridChange,
1184
- onDurationChange,
1185
- onTracksChange,
1186
- onNoteChange,
1187
- onInsertNote,
1188
- onRemoveNote,
1189
- onSelectedTrackIndexChange,
1190
- onPressedKeysChange
1191
- });
1192
- };
1193
-
1194
1143
  exports.PianoRoll = PianoRoll;
1195
1144
  exports.PlayHead = PlayHead;
1196
1145
  exports.createPianoRollstate = createPianoRollstate;