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