solid-pianoroll 0.0.14 → 0.0.16

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 { spread, mergeProps, template, createComponent, delegateEvents, use, insert, memo, effect, className, style, classList, setAttribute } from 'solid-js/web';
2
- import { createContext, splitProps, useContext, createEffect, createMemo, createSignal, Index, Show, mergeProps as mergeProps$1 } from 'solid-js';
1
+ import { createComponent, delegateEvents, insert, effect, className, classList, setAttribute, template, use, memo, spread, mergeProps } from 'solid-js/web';
2
+ import { createContext, splitProps, useContext, createEffect, createMemo, Index, createSignal, Show, batch, For, mergeProps as mergeProps$1 } from 'solid-js';
3
3
  import { createStore } from 'solid-js/store';
4
4
 
5
5
  function styleInject(css, ref) {
@@ -27,9 +27,9 @@ function styleInject(css, ref) {
27
27
  }
28
28
  }
29
29
 
30
- var css_248z$3 = ".PianoRoll-module_PianoRoll__rkRYx {\n box-sizing: border-box;\n display: flex;\n overflow: hidden;\n flex-direction: column;\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$3 = {"PianoRoll":"PianoRoll-module_PianoRoll__rkRYx","PianoRollContainer":"PianoRoll-module_PianoRollContainer__Oyxr7"};
32
- styleInject(css_248z$3);
30
+ var css_248z$4 = ".PianoRoll-module_PianoRoll__rkRYx {\n box-sizing: border-box;\n display: flex;\n overflow: hidden;\n flex-direction: column;\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$4 = {"PianoRoll":"PianoRoll-module_PianoRoll__rkRYx","PianoRollContainer":"PianoRoll-module_PianoRollContainer__Oyxr7"};
32
+ styleInject(css_248z$4);
33
33
 
34
34
  const PianoRollContext = createContext();
35
35
  const PianoRollContextProvider = PianoRollContext.Provider;
@@ -38,30 +38,11 @@ const usePianoRollContext = () => {
38
38
  if (!context) throw new Error("No PianoRollContext found");
39
39
  return context;
40
40
  };
41
- const splitContextProps = allProps => splitProps(allProps, ["condensed", "duration", "gridDivision", "notes", "position", "ppq", "snapToGrid", "verticalPosition", "verticalZoom", "zoom", "onInsertNote", "onNoteChange", "onPositionChange", "onRemoveNote", "onVerticalPositionChange", "onVerticalZoomChange", "onZoomChange"]);
41
+ const splitContextProps = allProps => splitProps(allProps, ["mode", "duration", "gridDivision", "tracks", "position", "ppq", "snapToGrid", "verticalPosition", "verticalZoom", "zoom", "onInsertNote", "onNoteChange", "onPositionChange", "onRemoveNote", "onVerticalPositionChange", "onVerticalZoomChange", "onZoomChange", "showAllTracks", "selectedTrackIndex", "onSelectedTrackIndexChange"]);
42
42
 
43
- const _tmpl$$8 = /*#__PURE__*/template(`<input max="500" min="1" step="0.01">`, 1);
44
- const HorizontalZoomControl = props => {
45
- const context = usePianoRollContext();
46
- return (() => {
47
- const _el$ = _tmpl$$8.cloneNode(true);
48
- spread(_el$, mergeProps(props, {
49
- get value() {
50
- return context.zoom;
51
- },
52
- "onInput": event => context.onZoomChange?.(event.currentTarget.valueAsNumber),
53
- "type": "range",
54
- get style() {
55
- return {
56
- "margin-left": "50px",
57
- "margin-right": "16px",
58
- ...(typeof props.style === "object" && props.style)
59
- };
60
- }
61
- }), false, false);
62
- return _el$;
63
- })();
64
- };
43
+ var css_248z$3 = ".PianoRollKeys-module_PianoRollKeys__5vnQ0 {\n position: relative;\n height: 100%;\n width: 50px;\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 overflow: hidden;\n}\n\n.PianoRollKeys-module_Key__jybNC {\n transition-property: background-color, box-shadow;\n transition-duration: 400ms;\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";
44
+ var styles$3 = {"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
+ styleInject(css_248z$3);
65
46
 
66
47
  function createViewPortDimension(getState) {
67
48
  const getStateWithFunctions = () => ({
@@ -135,6 +116,95 @@ const useViewPortDimension = name => {
135
116
  return viewPort;
136
117
  };
137
118
 
119
+ const _tmpl$$8 = /*#__PURE__*/template(`<div></div>`, 2);
120
+ const PianoRollKeys = () => {
121
+ const viewPort = createMemo(() => useViewPortDimension("vertical"));
122
+ return (() => {
123
+ const _el$ = _tmpl$$8.cloneNode(true);
124
+ insert(_el$, createComponent(Index, {
125
+ each: keys,
126
+ children: key => {
127
+ const [isDown, setIsDown] = createSignal(false);
128
+ const virtualDimensions = createMemo(() => viewPort().calculatePixelDimensions(127 - key().number, 1));
129
+ const previousIsBlack = blackKeys.includes((key().number - 1) % 12);
130
+ const nextIsBlack = blackKeys.includes((key().number + 1) % 12);
131
+ return createComponent(Show, {
132
+ get when() {
133
+ return virtualDimensions().size > 0;
134
+ },
135
+ get children() {
136
+ const _el$2 = _tmpl$$8.cloneNode(true);
137
+ _el$2.$$mouseup = () => setIsDown(false);
138
+ _el$2.$$mousedown = () => setIsDown(true);
139
+ insert(_el$2, createComponent(Show, {
140
+ get when() {
141
+ return key().isBlack && false;
142
+ },
143
+ get children() {
144
+ const _el$3 = _tmpl$$8.cloneNode(true);
145
+ effect(_p$ => {
146
+ const _v$ = styles$3["black-separator"],
147
+ _v$2 = `${virtualDimensions().offset}px`,
148
+ _v$3 = `${virtualDimensions().size / 2 - 0.25}px`;
149
+ _v$ !== _p$._v$ && className(_el$3, _p$._v$ = _v$);
150
+ _v$2 !== _p$._v$2 && _el$3.style.setProperty("top", _p$._v$2 = _v$2);
151
+ _v$3 !== _p$._v$3 && _el$3.style.setProperty("height", _p$._v$3 = _v$3);
152
+ return _p$;
153
+ }, {
154
+ _v$: undefined,
155
+ _v$2: undefined,
156
+ _v$3: undefined
157
+ });
158
+ return _el$3;
159
+ }
160
+ }));
161
+ effect(_p$ => {
162
+ const _v$4 = {
163
+ [styles$3["Key"]]: true,
164
+ [styles$3["black"]]: key().isBlack,
165
+ [styles$3["white"]]: !key().isBlack,
166
+ [styles$3["down"]]: isDown()
167
+ },
168
+ _v$5 = key().name,
169
+ _v$6 = key().number % 12,
170
+ _v$7 = `${virtualDimensions().offset - (!key().isBlack && nextIsBlack ? virtualDimensions().size / 2 : 0)}px`,
171
+ _v$8 = `${virtualDimensions().size + (!key().isBlack && nextIsBlack ? virtualDimensions().size / 2 : 0) + (!key().isBlack && previousIsBlack ? virtualDimensions().size / 2 : 0)}px`,
172
+ _v$9 = [`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(", ");
173
+ _p$._v$4 = classList(_el$2, _v$4, _p$._v$4);
174
+ _v$5 !== _p$._v$5 && setAttribute(_el$2, "title", _p$._v$5 = _v$5);
175
+ _v$6 !== _p$._v$6 && setAttribute(_el$2, "data-index", _p$._v$6 = _v$6);
176
+ _v$7 !== _p$._v$7 && _el$2.style.setProperty("top", _p$._v$7 = _v$7);
177
+ _v$8 !== _p$._v$8 && _el$2.style.setProperty("height", _p$._v$8 = _v$8);
178
+ _v$9 !== _p$._v$9 && _el$2.style.setProperty("box-shadow", _p$._v$9 = _v$9);
179
+ return _p$;
180
+ }, {
181
+ _v$4: undefined,
182
+ _v$5: undefined,
183
+ _v$6: undefined,
184
+ _v$7: undefined,
185
+ _v$8: undefined,
186
+ _v$9: undefined
187
+ });
188
+ return _el$2;
189
+ }
190
+ });
191
+ }
192
+ }));
193
+ effect(() => className(_el$, styles$3.PianoRollKeys));
194
+ return _el$;
195
+ })();
196
+ };
197
+ const blackKeys = [1, 3, 6, 8, 10];
198
+ const keyNames = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
199
+ const keys = Array.from({
200
+ length: 128
201
+ }).map((_, index) => ({
202
+ number: index,
203
+ name: `${keyNames[index % 12]} ${Math.floor(index / 12) - 2}`,
204
+ isBlack: blackKeys.includes(index % 12)
205
+ }));
206
+ delegateEvents(["mousedown", "mouseup"]);
207
+
138
208
  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}";
139
209
  var styles$2 = {"PianoRollNotes":"PianoRollNotes-module_PianoRollNotes__6pF-y","Note":"PianoRollNotes-module_Note__-jxLb","trimStart":"PianoRollNotes-module_trimStart__vuBlj","trimEnd":"PianoRollNotes-module_trimEnd__zPdjr"};
140
210
  styleInject(css_248z$2);
@@ -148,10 +218,11 @@ const PianoRollNotes = props => {
148
218
  const snapValueToGridIfEnabled = (value, altKey) => context.snapToGrid && !altKey ? Math.round(value / gridDivisionTicks()) * gridDivisionTicks() : value;
149
219
  const insertOrUpdateNote = event => {
150
220
  const position = horizontalViewPort().calculatePosition(event.clientX);
151
- const midi = context.condensed ? 60 : 127 - Math.floor(verticalViewPort().calculatePosition(event.clientY));
221
+ const existingNote = currentNote();
222
+ const midi = context.mode === "keys" ? 127 - Math.floor(verticalViewPort().calculatePosition(event.clientY)) : existingNote?.midi ?? 60;
223
+ const targetTrackIndex = context.mode === "tracks" ? Math.floor(verticalViewPort().calculatePosition(event.clientY)) : context.selectedTrackIndex ?? 0;
152
224
  const eventPositionTicks = Math.floor(position / gridDivisionTicks()) * gridDivisionTicks();
153
225
  const velocity = 127;
154
- const existingNote = newNote();
155
226
  const ticks = existingNote?.ticks ?? eventPositionTicks;
156
227
  const durationTicks = existingNote?.ticks ? eventPositionTicks - existingNote?.ticks : gridDivisionTicks();
157
228
  const note = {
@@ -161,25 +232,26 @@ const PianoRollNotes = props => {
161
232
  velocity
162
233
  };
163
234
  if (existingNote) {
164
- context.onNoteChange?.(newNoteIndex(), note);
165
- return newNoteIndex();
235
+ context.onNoteChange?.(currentNoteTrackIndex(), currentNoteIndex(), note);
236
+ return currentNoteIndex();
166
237
  } else {
167
- return context.onInsertNote?.(note) ?? -1;
238
+ return context.onInsertNote?.(targetTrackIndex, note) ?? -1;
168
239
  }
169
240
  };
170
241
  const [isDragging, setIsDragging] = createSignal(false);
171
242
  const [noteDragMode, setNoteDragMode] = createSignal();
172
- const [newNoteIndex, setNewNoteIndex] = createSignal(-1);
243
+ const [currentNoteIndex, setCurrentNoteIndex] = createSignal(-1);
244
+ const [currentNoteTrackIndex, setCurrentNoteTrackIndex] = createSignal(-1);
173
245
  const [isMouseDown, setIsMouseDown] = createSignal(false);
174
- const newNote = createMemo(() => context.notes[newNoteIndex()]);
246
+ const currentNote = createMemo(() => context.tracks[currentNoteTrackIndex()]?.notes[currentNoteIndex()]);
175
247
  const getClasses = noteDragMode => {
176
248
  return noteDragMode ? [styles$2.Note, styles$2[noteDragMode]] : [styles$2.Note];
177
249
  };
178
250
  return (() => {
179
251
  const _el$ = _tmpl$$7.cloneNode(true);
180
252
  _el$.$$click = event => {
181
- if (newNote()) {
182
- setNewNoteIndex(-1);
253
+ if (currentNote()) {
254
+ setCurrentNoteIndex(-1);
183
255
  return;
184
256
  }
185
257
  if (!event.altKey) return;
@@ -200,94 +272,133 @@ const PianoRollNotes = props => {
200
272
  return;
201
273
  }
202
274
  const index = insertOrUpdateNote(event);
203
- setNewNoteIndex(index);
275
+ setCurrentNoteIndex(index);
204
276
  };
205
277
  _el$.$$mousedown = event => {
206
- console.log("down");
207
- setIsMouseDown(true);
208
- const index = insertOrUpdateNote(event);
209
- setNewNoteIndex(index);
278
+ batch(() => {
279
+ setIsMouseDown(true);
280
+ const index = insertOrUpdateNote(event);
281
+ setCurrentNoteTrackIndex(context.selectedTrackIndex ?? 0);
282
+ setCurrentNoteIndex(index);
283
+ });
210
284
  };
211
285
  const _ref$ = props.ref;
212
286
  typeof _ref$ === "function" ? use(_ref$, _el$) : props.ref = _el$;
213
- insert(_el$, createComponent(Index, {
287
+ insert(_el$, createComponent(For, {
214
288
  get each() {
215
- return context.notes;
289
+ return context.tracks;
216
290
  },
217
- children: (note, index) => {
218
- const verticalVirtualDimensions = createMemo(() => verticalViewPort().calculatePixelDimensions(127 - note().midi, 1));
219
- const horizontalDimensions = createMemo(() => horizontalViewPort().calculatePixelDimensions(note().ticks, note().durationTicks));
291
+ children: (track, trackIndex) => {
220
292
  return createComponent(Show, {
221
293
  get when() {
222
- return memo(() => !!(verticalViewPort().isVisible(verticalVirtualDimensions()) || context.condensed))() && horizontalViewPort().isVisible(horizontalDimensions());
294
+ return trackIndex() === context.selectedTrackIndex || context.showAllTracks || context.mode === "tracks";
223
295
  },
224
296
  get children() {
225
- const _el$2 = _tmpl$$7.cloneNode(true);
226
- _el$2.$$mousedown = event => {
227
- event.stopPropagation();
228
- setIsDragging(true);
229
- const initialPosition = horizontalViewPort().calculatePosition(event.clientX);
230
- const diffPosition = initialPosition - note().ticks;
231
- const handleMouseMove = mouseMoveEvent => {
232
- const ticks = snapValueToGridIfEnabled(Math.max(horizontalViewPort().calculatePosition(mouseMoveEvent.clientX) - diffPosition, 0), mouseMoveEvent.altKey);
233
- context.onNoteChange?.(index, {
234
- ...note(),
235
- ...(noteDragMode() === "move" && !context.condensed && {
236
- midi: Math.round(127 - verticalViewPort().calculatePosition(mouseMoveEvent.clientY))
237
- }),
238
- ...((noteDragMode() === "move" || noteDragMode() === "trimStart") && {
239
- ticks
240
- }),
241
- ...(noteDragMode() === "trimStart" && {
242
- durationTicks: note().durationTicks + note().ticks - ticks
243
- }),
244
- ...(noteDragMode() === "trimEnd" && {
245
- durationTicks: snapValueToGridIfEnabled(horizontalViewPort().calculatePosition(mouseMoveEvent.clientX) - note().ticks, mouseMoveEvent.altKey)
246
- })
297
+ return createComponent(For, {
298
+ get each() {
299
+ return track.notes;
300
+ },
301
+ children: (note, noteIndex) => {
302
+ const verticalVirtualDimensions = createMemo(() => verticalViewPort().calculatePixelDimensions(context.mode === "keys" ? 127 - note.midi : trackIndex(), 1));
303
+ const horizontalDimensions = createMemo(() => horizontalViewPort().calculatePixelDimensions(note.ticks, note.durationTicks));
304
+ return createComponent(Show, {
305
+ get when() {
306
+ return memo(() => !!verticalViewPort().isVisible(verticalVirtualDimensions()))() && horizontalViewPort().isVisible(horizontalDimensions());
307
+ },
308
+ get children() {
309
+ const _el$2 = _tmpl$$7.cloneNode(true);
310
+ _el$2.$$mousedown = event => {
311
+ event.stopPropagation();
312
+ setIsDragging(true);
313
+ setCurrentNoteIndex(noteIndex());
314
+ setCurrentNoteTrackIndex(trackIndex());
315
+ const initialPosition = horizontalViewPort().calculatePosition(event.clientX);
316
+ const diffPosition = initialPosition - note.ticks;
317
+ const handleMouseMove = mouseMoveEvent => {
318
+ const note = currentNote();
319
+ if (!note) return;
320
+ const ticks = snapValueToGridIfEnabled(Math.max(horizontalViewPort().calculatePosition(mouseMoveEvent.clientX) - diffPosition, 0), mouseMoveEvent.altKey);
321
+ const updatedNote = {
322
+ ...note,
323
+ ...(noteDragMode() === "move" && context.mode === "keys" && {
324
+ midi: Math.round(127 - verticalViewPort().calculatePosition(mouseMoveEvent.clientY))
325
+ }),
326
+ ...((noteDragMode() === "move" || noteDragMode() === "trimStart") && {
327
+ ticks
328
+ }),
329
+ ...(noteDragMode() === "trimStart" && {
330
+ durationTicks: note.durationTicks + note.ticks - ticks
331
+ }),
332
+ ...(noteDragMode() === "trimEnd" && {
333
+ durationTicks: snapValueToGridIfEnabled(horizontalViewPort().calculatePosition(mouseMoveEvent.clientX) - note.ticks, mouseMoveEvent.altKey)
334
+ })
335
+ };
336
+ const previousTrackIndex = currentNoteTrackIndex();
337
+ const previousNoteIndex = currentNoteIndex();
338
+ const targetTrackIndex = context.mode === "tracks" ? Math.floor(verticalViewPort().calculatePosition(mouseMoveEvent.clientY)) : previousTrackIndex;
339
+ if (targetTrackIndex === previousTrackIndex) {
340
+ context.onNoteChange?.(previousTrackIndex, previousNoteIndex, updatedNote);
341
+ } else {
342
+ batch(() => {
343
+ if (context.onInsertNote) {
344
+ context.onRemoveNote?.(previousTrackIndex, previousNoteIndex);
345
+ const newNoteIndex = context.onInsertNote(targetTrackIndex, updatedNote);
346
+ setCurrentNoteTrackIndex(targetTrackIndex);
347
+ setCurrentNoteIndex(newNoteIndex);
348
+ }
349
+ });
350
+ }
351
+ };
352
+ const handleMouseUp = () => {
353
+ setIsDragging(false);
354
+ setCurrentNoteIndex(-1);
355
+ setCurrentNoteTrackIndex(-1);
356
+ window.removeEventListener("mousemove", handleMouseMove);
357
+ window.removeEventListener("mouseup", handleMouseUp);
358
+ };
359
+ window.addEventListener("mousemove", handleMouseMove);
360
+ window.addEventListener("mouseup", handleMouseUp);
361
+ };
362
+ _el$2.$$dblclick = event => {
363
+ event.stopPropagation();
364
+ context.onRemoveNote?.(trackIndex(), noteIndex());
365
+ };
366
+ _el$2.$$mousemove = event => {
367
+ if (isDragging()) return;
368
+ event.stopPropagation();
369
+ const relativeX = horizontalViewPort().calculatePixelValue(horizontalViewPort().calculatePosition(event.clientX));
370
+ const noteStartX = horizontalViewPort().calculatePixelValue(note.ticks);
371
+ const noteEndX = horizontalViewPort().calculatePixelValue(note.ticks + note.durationTicks);
372
+ setNoteDragMode(relativeX - noteStartX < 3 ? "trimStart" : noteEndX - relativeX < 3 ? "trimEnd" : "move");
373
+ };
374
+ setAttribute(_el$2, "draggable", false);
375
+ effect(_p$ => {
376
+ const _v$ = getClasses(noteDragMode()).join(" "),
377
+ _v$2 = `rgba(255,0,0, ${(128 + note.velocity) / 256})`,
378
+ _v$3 = `${verticalVirtualDimensions().offset}px`,
379
+ _v$4 = `${verticalVirtualDimensions().size}px`,
380
+ _v$5 = `${horizontalDimensions().offset}px`,
381
+ _v$6 = `${horizontalDimensions().size}px`;
382
+ _v$ !== _p$._v$ && className(_el$2, _p$._v$ = _v$);
383
+ _v$2 !== _p$._v$2 && _el$2.style.setProperty("background-color", _p$._v$2 = _v$2);
384
+ _v$3 !== _p$._v$3 && _el$2.style.setProperty("top", _p$._v$3 = _v$3);
385
+ _v$4 !== _p$._v$4 && _el$2.style.setProperty("height", _p$._v$4 = _v$4);
386
+ _v$5 !== _p$._v$5 && _el$2.style.setProperty("left", _p$._v$5 = _v$5);
387
+ _v$6 !== _p$._v$6 && _el$2.style.setProperty("width", _p$._v$6 = _v$6);
388
+ return _p$;
389
+ }, {
390
+ _v$: undefined,
391
+ _v$2: undefined,
392
+ _v$3: undefined,
393
+ _v$4: undefined,
394
+ _v$5: undefined,
395
+ _v$6: undefined
396
+ });
397
+ return _el$2;
398
+ }
247
399
  });
248
- };
249
- const handleMouseUp = () => {
250
- setIsDragging(false);
251
- window.removeEventListener("mousemove", handleMouseMove);
252
- window.removeEventListener("mouseup", handleMouseUp);
253
- };
254
- window.addEventListener("mousemove", handleMouseMove);
255
- window.addEventListener("mouseup", handleMouseUp);
256
- };
257
- _el$2.$$dblclick = event => {
258
- event.stopPropagation();
259
- context.onRemoveNote?.(index);
260
- };
261
- _el$2.$$mousemove = event => {
262
- if (isDragging()) return;
263
- event.stopPropagation();
264
- const relativeX = horizontalViewPort().calculatePixelValue(horizontalViewPort().calculatePosition(event.clientX));
265
- const noteStartX = horizontalViewPort().calculatePixelValue(note().ticks);
266
- const noteEndX = horizontalViewPort().calculatePixelValue(note().ticks + note().durationTicks);
267
- setNoteDragMode(relativeX - noteStartX < 3 ? "trimStart" : noteEndX - relativeX < 3 ? "trimEnd" : "move");
268
- };
269
- effect(_p$ => {
270
- const _v$ = getClasses(noteDragMode()).join(" "),
271
- _v$2 = {
272
- "background-color": `rgba(255,0,0, ${(128 + note().velocity) / 256})`,
273
- ...(context.condensed ? {
274
- top: 0,
275
- height: "100%"
276
- } : {
277
- top: `${verticalVirtualDimensions().offset}px`,
278
- height: `${verticalVirtualDimensions().size}px`
279
- }),
280
- left: `${horizontalDimensions().offset}px`,
281
- width: `${horizontalDimensions().size}px`
282
- };
283
- _v$ !== _p$._v$ && className(_el$2, _p$._v$ = _v$);
284
- _p$._v$2 = style(_el$2, _v$2, _p$._v$2);
285
- return _p$;
286
- }, {
287
- _v$: undefined,
288
- _v$2: undefined
400
+ }
289
401
  });
290
- return _el$2;
291
402
  }
292
403
  });
293
404
  }
@@ -401,74 +512,11 @@ const ScrollContainer = props => {
401
512
  })();
402
513
  };
403
514
 
404
- var css_248z$1 = ".PianoRollKeys-module_PianoRollKeys__5vnQ0 {\n position: relative;\n height: 100%;\n width: 50px;\n}\n\n.PianoRollKeys-module_Key__jybNC {\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}\n\n.PianoRollKeys-module_black__7rncB {\n background: black;\n}\n\n.PianoRollKeys-module_white__fR2Lm {\n background: white;\n}\n\n.PianoRollKeys-module_whiteAndNextIsWhite__PN1O8 {\n border-top-width: 0.1px;\n}\n\n.PianoRollKeys-module_whiteAndPreviousIsWhite__-fP3q {\n border-bottom-width: 0.1px;\n}\n";
405
- var styles$1 = {"PianoRollKeys":"PianoRollKeys-module_PianoRollKeys__5vnQ0","Key":"PianoRollKeys-module_Key__jybNC","black":"PianoRollKeys-module_black__7rncB","white":"PianoRollKeys-module_white__fR2Lm","whiteAndNextIsWhite":"PianoRollKeys-module_whiteAndNextIsWhite__PN1O8","whiteAndPreviousIsWhite":"PianoRollKeys-module_whiteAndPreviousIsWhite__-fP3q"};
515
+ 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";
516
+ var styles$1 = {"PianoRollGrid":"PianoRollGrid-module_PianoRollGrid__tG119","PianoRollGrid-Row":"PianoRollGrid-module_PianoRollGrid-Row__nRMCs","PianoRollGrid-Time":"PianoRollGrid-module_PianoRollGrid-Time__jLz3E"};
406
517
  styleInject(css_248z$1);
407
518
 
408
519
  const _tmpl$$5 = /*#__PURE__*/template(`<div></div>`, 2);
409
- const PianoRollKeys = () => {
410
- const viewPort = createMemo(() => useViewPortDimension("vertical"));
411
- return (() => {
412
- const _el$ = _tmpl$$5.cloneNode(true);
413
- insert(_el$, createComponent(Index, {
414
- each: keys,
415
- children: key => {
416
- const virtualDimensions = createMemo(() => viewPort().calculatePixelDimensions(127 - key().number, 1));
417
- return createComponent(Show, {
418
- get when() {
419
- return virtualDimensions().size > 0;
420
- },
421
- get children() {
422
- const _el$2 = _tmpl$$5.cloneNode(true);
423
- effect(_p$ => {
424
- const _v$ = {
425
- [styles$1["Key"]]: true,
426
- [styles$1["black"]]: key().isBlack,
427
- [styles$1["white"]]: !key().isBlack,
428
- [styles$1["whiteAndNextIsWhite"]]: !key().isBlack && !blackKeys.includes((key().number + 1) % 12),
429
- [styles$1["whiteAndPreviousIsWhite"]]: !key().isBlack && !blackKeys.includes((key().number - 1) % 12)
430
- },
431
- _v$2 = key().name,
432
- _v$3 = key().number % 12,
433
- _v$4 = `${virtualDimensions().offset}px`,
434
- _v$5 = `${virtualDimensions().size}px`;
435
- _p$._v$ = classList(_el$2, _v$, _p$._v$);
436
- _v$2 !== _p$._v$2 && setAttribute(_el$2, "title", _p$._v$2 = _v$2);
437
- _v$3 !== _p$._v$3 && setAttribute(_el$2, "data-index", _p$._v$3 = _v$3);
438
- _v$4 !== _p$._v$4 && _el$2.style.setProperty("top", _p$._v$4 = _v$4);
439
- _v$5 !== _p$._v$5 && _el$2.style.setProperty("height", _p$._v$5 = _v$5);
440
- return _p$;
441
- }, {
442
- _v$: undefined,
443
- _v$2: undefined,
444
- _v$3: undefined,
445
- _v$4: undefined,
446
- _v$5: undefined
447
- });
448
- return _el$2;
449
- }
450
- });
451
- }
452
- }));
453
- effect(() => className(_el$, styles$1.PianoRollKeys));
454
- return _el$;
455
- })();
456
- };
457
- const blackKeys = [1, 3, 6, 8, 10];
458
- const keyNames = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
459
- const keys = Array.from({
460
- length: 128
461
- }).map((_, index) => ({
462
- number: index,
463
- name: `${keyNames[index % 12]} ${Math.floor(index / 12) - 2}`,
464
- isBlack: blackKeys.includes(index % 12)
465
- }));
466
-
467
- var css_248z = ".PianoRollGrid-module_PianoRollGrid__tG119 {\n position: relative;\n width: 100%;\n height: 100%;\n}\n\n.PianoRollGrid-module_PianoRollGrid-Key__VRbiB {\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";
468
- var styles = {"PianoRollGrid":"PianoRollGrid-module_PianoRollGrid__tG119","PianoRollGrid-Key":"PianoRollGrid-module_PianoRollGrid-Key__VRbiB","PianoRollGrid-Time":"PianoRollGrid-module_PianoRollGrid-Time__jLz3E"};
469
- styleInject(css_248z);
470
-
471
- const _tmpl$$4 = /*#__PURE__*/template(`<div></div>`, 2);
472
520
  const PianoRollGrid = () => {
473
521
  const context = usePianoRollContext();
474
522
  const verticalViewPort = createMemo(() => useViewPortDimension("vertical"));
@@ -498,7 +546,7 @@ const PianoRollGrid = () => {
498
546
  }));
499
547
  });
500
548
  return (() => {
501
- const _el$ = _tmpl$$4.cloneNode(true);
549
+ const _el$ = _tmpl$$5.cloneNode(true);
502
550
  insert(_el$, createComponent(Index, {
503
551
  get each() {
504
552
  return gridArray();
@@ -510,9 +558,9 @@ const PianoRollGrid = () => {
510
558
  return virtualDimensions().size > 0;
511
559
  },
512
560
  get children() {
513
- const _el$2 = _tmpl$$4.cloneNode(true);
561
+ const _el$2 = _tmpl$$5.cloneNode(true);
514
562
  effect(_p$ => {
515
- const _v$ = styles["PianoRollGrid-Time"],
563
+ const _v$ = styles$1["PianoRollGrid-Time"],
516
564
  _v$2 = Math.ceil((entry().index + 1 * selectedGridDivisorTicks()) / measureTicks()) % 2 === 0 ? "#ddd" : "#ccc",
517
565
  _v$3 = `${virtualDimensions().offset}px`,
518
566
  _v$4 = `${virtualDimensions().size}px`,
@@ -537,7 +585,7 @@ const PianoRollGrid = () => {
537
585
  }), null);
538
586
  insert(_el$, createComponent(Show, {
539
587
  get when() {
540
- return !context.condensed;
588
+ return context.mode === "keys";
541
589
  },
542
590
  get children() {
543
591
  return createComponent(Index, {
@@ -549,10 +597,10 @@ const PianoRollGrid = () => {
549
597
  return virtualDimensions().size > 0;
550
598
  },
551
599
  get children() {
552
- const _el$3 = _tmpl$$4.cloneNode(true);
600
+ const _el$3 = _tmpl$$5.cloneNode(true);
553
601
  _el$3.style.setProperty("width", "100%");
554
602
  effect(_p$ => {
555
- const _v$6 = styles["PianoRollGrid-Key"],
603
+ const _v$6 = styles$1["PianoRollGrid-Row"],
556
604
  _v$7 = `${virtualDimensions().offset}px`,
557
605
  _v$8 = `${virtualDimensions().size}px`,
558
606
  _v$9 = key().isBlack ? "rgba(0,0,0,0.2)" : "none",
@@ -577,35 +625,53 @@ const PianoRollGrid = () => {
577
625
  });
578
626
  }
579
627
  }), null);
580
- effect(() => className(_el$, styles.PianoRollGrid));
628
+ insert(_el$, createComponent(Show, {
629
+ get when() {
630
+ return context.mode === "tracks";
631
+ },
632
+ get children() {
633
+ return createComponent(For, {
634
+ get each() {
635
+ return context.tracks;
636
+ },
637
+ children: (track, index) => {
638
+ const virtualDimensions = createMemo(() => verticalViewPort().calculatePixelDimensions(index(), 1));
639
+ return createComponent(Show, {
640
+ get when() {
641
+ return virtualDimensions().size > 0;
642
+ },
643
+ get children() {
644
+ const _el$4 = _tmpl$$5.cloneNode(true);
645
+ _el$4.style.setProperty("width", "100%");
646
+ _el$4.style.setProperty("border-width", "0");
647
+ effect(_p$ => {
648
+ const _v$11 = styles$1["PianoRollGrid-Row"],
649
+ _v$12 = `${virtualDimensions().offset}px`,
650
+ _v$13 = `${virtualDimensions().size}px`,
651
+ _v$14 = index() % 2 === 0 ? "rgba(0,0,0,0.2)" : "none";
652
+ _v$11 !== _p$._v$11 && className(_el$4, _p$._v$11 = _v$11);
653
+ _v$12 !== _p$._v$12 && _el$4.style.setProperty("top", _p$._v$12 = _v$12);
654
+ _v$13 !== _p$._v$13 && _el$4.style.setProperty("height", _p$._v$13 = _v$13);
655
+ _v$14 !== _p$._v$14 && _el$4.style.setProperty("background-color", _p$._v$14 = _v$14);
656
+ return _p$;
657
+ }, {
658
+ _v$11: undefined,
659
+ _v$12: undefined,
660
+ _v$13: undefined,
661
+ _v$14: undefined
662
+ });
663
+ return _el$4;
664
+ }
665
+ });
666
+ }
667
+ });
668
+ }
669
+ }), null);
670
+ effect(() => className(_el$, styles$1.PianoRollGrid));
581
671
  return _el$;
582
672
  })();
583
673
  };
584
674
 
585
- const useNotes = () => {
586
- const [notes, onNotesChange] = createSignal([]);
587
- const onNoteChange = (index, note) => {
588
- onNotesChange([...notes().slice(0, index), note, ...notes().splice(index + 1)]);
589
- };
590
- const onInsertNote = note => {
591
- const index = Math.max(notes().findIndex(({
592
- ticks
593
- }) => ticks > note.ticks), 0);
594
- onNotesChange([...notes().slice(0, index), note, ...notes().splice(index)]);
595
- return index;
596
- };
597
- const onRemoveNote = index => {
598
- onNotesChange([...notes().slice(0, index), ...notes().splice(index + 1)]);
599
- };
600
- return {
601
- notes,
602
- onNotesChange,
603
- onNoteChange,
604
- onInsertNote,
605
- onRemoveNote
606
- };
607
- };
608
-
609
675
  const defaultRect = {
610
676
  left: 0,
611
677
  width: 0,
@@ -634,11 +700,11 @@ function useBoundingClientRect(containerRef) {
634
700
  return boundingClientRect;
635
701
  }
636
702
 
637
- const _tmpl$$3 = /*#__PURE__*/template(`<input min="1" max="11" step="0.01">`, 1);
703
+ const _tmpl$$4 = /*#__PURE__*/template(`<input min="1" max="11" step="0.01">`, 1);
638
704
  const VerticalZoomControl = props => {
639
705
  const context = usePianoRollContext();
640
706
  return (() => {
641
- const _el$ = _tmpl$$3.cloneNode(true);
707
+ const _el$ = _tmpl$$4.cloneNode(true);
642
708
  spread(_el$, mergeProps(props, {
643
709
  get value() {
644
710
  return context.verticalZoom;
@@ -662,224 +728,91 @@ const VerticalZoomControl = props => {
662
728
  })();
663
729
  };
664
730
 
665
- const _tmpl$$2 = /*#__PURE__*/template(`<div><ul></ul></div>`, 4),
666
- _tmpl$2 = /*#__PURE__*/template(`<ul></ul>`, 2),
667
- _tmpl$3 = /*#__PURE__*/template(`<div><div></div></div>`, 4),
668
- _tmpl$4 = /*#__PURE__*/template(`<div></div>`, 2),
669
- _tmpl$5 = /*#__PURE__*/template(`<li></li>`, 2);
670
- const MultiTrackPianoRoll = allProps => {
671
- const selectedTrack = createMemo(() => allProps.tracks[allProps.selectedTrackIndex]);
672
- const condensed = createMemo(() => !selectedTrack());
673
- const [ownProps, restProps] = splitProps(allProps, ["tracks", "selectedTrackIndex"]);
674
- const [contextProps, divProps] = splitContextProps(mergeProps$1(restProps, {
675
- notes: [],
676
- condensed: false,
677
- onNoteChange: () => undefined,
678
- onInsertNote: () => -1,
679
- onRemoveNote: () => undefined
680
- }));
681
- const [scrollerRef, setScrollerRef] = createSignal();
682
- const clientRect = useBoundingClientRect(scrollerRef);
683
- const zoomFactor = 500;
684
- const minZoom = createMemo(() => 1 / (zoomFactor / clientRect().width));
685
- const maxZoom = createMemo(() => 500 * (zoomFactor / clientRect().width));
686
- const minVerticalZoom = createMemo(() => 1 / (zoomFactor / clientRect().height));
687
- const maxVerticalZoom = createMemo(() => 10 * (zoomFactor / clientRect().height));
731
+ const _tmpl$$3 = /*#__PURE__*/template(`<input max="500" min="1" step="0.01">`, 1);
732
+ const HorizontalZoomControl = props => {
733
+ const context = usePianoRollContext();
688
734
  return (() => {
689
- const _el$ = _tmpl$4.cloneNode(true);
690
- spread(_el$, mergeProps(divProps, {
735
+ const _el$ = _tmpl$$3.cloneNode(true);
736
+ spread(_el$, mergeProps(props, {
737
+ get value() {
738
+ return context.zoom;
739
+ },
740
+ "onInput": event => context.onZoomChange?.(event.currentTarget.valueAsNumber),
741
+ "type": "range",
691
742
  get style() {
692
743
  return {
693
- display: "flex",
694
- flex: 1,
695
- height: "100%",
696
- "flex-direction": "row",
697
- overflow: "hidden",
698
- ...(typeof divProps.style === "object" && divProps.style)
744
+ "margin-left": "50px",
745
+ "margin-right": "16px",
746
+ ...(typeof props.style === "object" && props.style)
699
747
  };
700
748
  }
701
- }), false, true);
702
- insert(_el$, createComponent(PianoRollContextProvider, {
703
- get value() {
704
- return {
705
- ...contextProps
706
- };
749
+ }), false, false);
750
+ return _el$;
751
+ })();
752
+ };
753
+
754
+ 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";
755
+ var styles = {"PianoRollTrackList":"PianoRollTrackList-module_PianoRollTrackList__pyuxQ","Track":"PianoRollTrackList-module_Track__VZFIg"};
756
+ styleInject(css_248z);
757
+
758
+ const _tmpl$$2 = /*#__PURE__*/template(`<div></div>`, 2),
759
+ _tmpl$2$1 = /*#__PURE__*/template(`<div> </div>`, 2);
760
+ const PianoRollTrackList = () => {
761
+ const viewPort = createMemo(() => useViewPortDimension("vertical"));
762
+ const context = usePianoRollContext();
763
+ return (() => {
764
+ const _el$ = _tmpl$$2.cloneNode(true);
765
+ insert(_el$, createComponent(Index, {
766
+ get each() {
767
+ return context.tracks;
707
768
  },
708
- get children() {
709
- return createComponent(ScrollZoomViewPort, {
710
- dimensions: {
711
- horizontal: () => ({
712
- pixelOffset: clientRect().left,
713
- pixelSize: clientRect().width,
714
- position: contextProps.position,
715
- range: contextProps.duration,
716
- zoom: contextProps.zoom * (zoomFactor / clientRect().width),
717
- onPositionChange: contextProps.onPositionChange,
718
- onZoomChange: zoom => contextProps.onZoomChange?.(clamp(zoom / (zoomFactor / clientRect().width), minZoom(), maxZoom()))
719
- }),
720
- vertical: () => ({
721
- pixelOffset: clientRect().top,
722
- pixelSize: clientRect().height,
723
- position: contextProps.verticalPosition,
724
- range: 128,
725
- zoom: contextProps.verticalZoom * (zoomFactor / clientRect().height),
726
- onPositionChange: contextProps.onVerticalPositionChange,
727
- onZoomChange: verticalZoom => contextProps.onVerticalZoomChange?.(clamp(verticalZoom / (zoomFactor / clientRect().height), minVerticalZoom(), maxVerticalZoom()))
728
- })
769
+ children: (track, index) => {
770
+ const virtualDimensions = createMemo(() => viewPort().calculatePixelDimensions(context.mode === "tracks" ? index : index * 8, context.mode === "tracks" ? 1 : 8));
771
+ return createComponent(Show, {
772
+ get when() {
773
+ return virtualDimensions().size > 0;
729
774
  },
730
775
  get children() {
731
- return [(() => {
732
- const _el$2 = _tmpl$$2.cloneNode(true),
733
- _el$3 = _el$2.firstChild;
734
- _el$2.style.setProperty("width", "250px");
735
- _el$2.style.setProperty("display", "flex");
736
- _el$2.style.setProperty("flex-direction", "row");
737
- _el$3.style.setProperty("margin-block-start", "0");
738
- _el$3.style.setProperty("padding-inline-start", "0");
739
- _el$3.style.setProperty("flex", "1");
740
- _el$3.style.setProperty("position", "relative");
741
- insert(_el$3, createComponent(Index, {
742
- get each() {
743
- return allProps.tracks;
744
- },
745
- children: (track, noteIndex) => (() => {
746
- const _el$7 = _tmpl$5.cloneNode(true);
747
- _el$7.$$click = () => {
748
- if (noteIndex === allProps.selectedTrackIndex) {
749
- allProps.onSelectedTrackIndexChange(-1);
750
- } else {
751
- allProps.onSelectedTrackIndexChange(noteIndex);
752
- }
753
- };
754
- _el$7.style.setProperty("width", "100%");
755
- _el$7.style.setProperty("height", "30px");
756
- _el$7.style.setProperty("list-style", "none");
757
- _el$7.style.setProperty("display", "flex");
758
- _el$7.style.setProperty("align-items", "center");
759
- _el$7.style.setProperty("border-top", "1px black solid");
760
- _el$7.style.setProperty("cursor", "pointer");
761
- insert(_el$7, () => track().name);
762
- effect(() => _el$7.style.setProperty("background", noteIndex === allProps.selectedTrackIndex ? "lightgray" : "none"));
763
- return _el$7;
764
- })()
765
- }));
766
- insert(_el$2, createComponent(Show, {
767
- get when() {
768
- return !condensed();
769
- },
770
- get children() {
771
- return createComponent(PianoRollKeys, {});
772
- }
773
- }), null);
774
- return _el$2;
775
- })(), (() => {
776
- const _el$4 = _tmpl$3.cloneNode(true),
777
- _el$5 = _el$4.firstChild;
778
- _el$4.style.setProperty("flex", "1");
779
- insert(_el$5, createComponent(ScrollContainer, {
780
- ref: setScrollerRef,
781
- get children() {
782
- return [memo(() => allProps.children), (() => {
783
- const _el$6 = _tmpl$2.cloneNode(true);
784
- _el$6.style.setProperty("margin", "0");
785
- _el$6.style.setProperty("margin-block-start", "0");
786
- _el$6.style.setProperty("padding-inline-start", "0");
787
- insert(_el$6, createComponent(Index, {
788
- get each() {
789
- return allProps.tracks;
790
- },
791
- children: (track, trackIndex) => {
792
- const notes = useNotes();
793
- createEffect(() => {
794
- notes.onNotesChange(track().notes);
795
- });
796
- const [context, setContext] = createStore({
797
- ...contextProps
798
- });
799
- createEffect(() => {
800
- setContext({
801
- ...contextProps,
802
- condensed: condensed(),
803
- notes: track()?.notes,
804
- onNoteChange: (noteIndex, note) => allProps?.onNoteChange?.(trackIndex, noteIndex, note),
805
- onInsertNote: note => allProps.onInsertNote?.(trackIndex, note) ?? -1,
806
- onRemoveNote: noteIndex => allProps.onRemoveNote?.(trackIndex, noteIndex)
807
- });
808
- });
809
- const verticalViewPort = useViewPortDimension("vertical");
810
- return createComponent(Show, {
811
- get when() {
812
- return condensed() || allProps.selectedTrackIndex === trackIndex;
813
- },
814
- get children() {
815
- return createComponent(PianoRollContextProvider, {
816
- value: context,
817
- get children() {
818
- const _el$8 = _tmpl$5.cloneNode(true);
819
- _el$8.style.setProperty("display", "flex");
820
- _el$8.style.setProperty("position", "relative");
821
- _el$8.style.setProperty("list-style", "none");
822
- _el$8.style.setProperty("flex-direction", "row");
823
- _el$8.style.setProperty("border-top", "1px black solid");
824
- insert(_el$8, createComponent(PianoRollNotes, {}), null);
825
- insert(_el$8, createComponent(PianoRollGrid, {}), null);
826
- effect(() => _el$8.style.setProperty("height", condensed() ? "30px" : `${verticalViewPort.pixelSize}px`));
827
- return _el$8;
828
- }
829
- });
830
- }
831
- });
832
- }
833
- }));
834
- return _el$6;
835
- })()];
836
- }
837
- }), null);
838
- insert(_el$5, createComponent(Show, {
839
- get when() {
840
- return !condensed();
841
- },
842
- get children() {
843
- return createComponent(VerticalZoomControl, {
844
- get value() {
845
- return contextProps.verticalZoom;
846
- },
847
- onInput: event => contextProps.onVerticalZoomChange?.(event.currentTarget.valueAsNumber)
848
- });
849
- }
850
- }), null);
851
- insert(_el$4, createComponent(HorizontalZoomControl, {
852
- style: {
853
- width: "100%",
854
- "margin-left": 0
855
- },
856
- get value() {
857
- return contextProps.zoom;
776
+ const _el$2 = _tmpl$2$1.cloneNode(true),
777
+ _el$3 = _el$2.firstChild;
778
+ _el$2.$$click = () => context.onSelectedTrackIndexChange?.(index);
779
+ _el$2.style.setProperty("cursor", "pointer");
780
+ insert(_el$2, index, _el$3);
781
+ insert(_el$2, () => track().name || "[unnamed track]", null);
782
+ effect(_p$ => {
783
+ const _v$ = {
784
+ [styles["Track"]]: true
858
785
  },
859
- onInput: event => contextProps.onZoomChange?.(event.currentTarget.valueAsNumber)
860
- }), null);
861
- effect(_p$ => {
862
- const _v$ = styles$3.PianoRoll,
863
- _v$2 = styles$3.PianoRollContainer;
864
- _v$ !== _p$._v$ && className(_el$4, _p$._v$ = _v$);
865
- _v$2 !== _p$._v$2 && className(_el$5, _p$._v$2 = _v$2);
866
- return _p$;
867
- }, {
868
- _v$: undefined,
869
- _v$2: undefined
870
- });
871
- return _el$4;
872
- })()];
786
+ _v$2 = track().name,
787
+ _v$3 = `${virtualDimensions().offset}px`,
788
+ _v$4 = `${virtualDimensions().size}px`,
789
+ _v$5 = index === context.selectedTrackIndex ? "gray" : "lightgrey";
790
+ _p$._v$ = classList(_el$2, _v$, _p$._v$);
791
+ _v$2 !== _p$._v$2 && setAttribute(_el$2, "title", _p$._v$2 = _v$2);
792
+ _v$3 !== _p$._v$3 && _el$2.style.setProperty("top", _p$._v$3 = _v$3);
793
+ _v$4 !== _p$._v$4 && _el$2.style.setProperty("height", _p$._v$4 = _v$4);
794
+ _v$5 !== _p$._v$5 && _el$2.style.setProperty("background", _p$._v$5 = _v$5);
795
+ return _p$;
796
+ }, {
797
+ _v$: undefined,
798
+ _v$2: undefined,
799
+ _v$3: undefined,
800
+ _v$4: undefined,
801
+ _v$5: undefined
802
+ });
803
+ return _el$2;
873
804
  }
874
805
  });
875
806
  }
876
807
  }));
808
+ effect(() => className(_el$, styles.PianoRollTrackList));
877
809
  return _el$;
878
810
  })();
879
811
  };
880
812
  delegateEvents(["click"]);
881
813
 
882
- const _tmpl$$1 = /*#__PURE__*/template(`<div><div></div></div>`, 4);
814
+ const _tmpl$$1 = /*#__PURE__*/template(`<div></div>`, 2),
815
+ _tmpl$2 = /*#__PURE__*/template(`<div><div></div></div>`, 4);
883
816
  const PianoRoll = allProps => {
884
817
  const [contextProps, divProps] = splitContextProps(allProps);
885
818
  const [scrollerRef, setScrollerRef] = createSignal();
@@ -889,14 +822,20 @@ const PianoRoll = allProps => {
889
822
  const maxZoom = createMemo(() => 500 * (zoomFactor / clientRect().width));
890
823
  const minVerticalZoom = createMemo(() => 1 / (zoomFactor / clientRect().height));
891
824
  const maxVerticalZoom = createMemo(() => 10 * (zoomFactor / clientRect().height));
825
+ createEffect(isInitial => {
826
+ if (isInitial) return false;
827
+ if (contextProps.mode === "tracks") {
828
+ contextProps.onVerticalPositionChange?.(0);
829
+ }
830
+ }, true);
892
831
  return createComponent(PianoRollContextProvider, {
893
832
  value: contextProps,
894
833
  get children() {
895
- const _el$ = _tmpl$$1.cloneNode(true),
834
+ const _el$ = _tmpl$2.cloneNode(true),
896
835
  _el$2 = _el$.firstChild;
897
836
  spread(_el$, mergeProps(divProps, {
898
837
  get ["class"]() {
899
- return styles$3.PianoRoll;
838
+ return styles$4.PianoRoll;
900
839
  }
901
840
  }), false, true);
902
841
  insert(_el$2, createComponent(ScrollZoomViewPort, {
@@ -914,14 +853,35 @@ const PianoRoll = allProps => {
914
853
  pixelOffset: clientRect().top,
915
854
  pixelSize: clientRect().height,
916
855
  position: contextProps.verticalPosition,
917
- range: 128,
856
+ range: contextProps.mode === "keys" ? 128 : Math.max(contextProps.tracks.length, 16),
918
857
  zoom: contextProps.verticalZoom * (zoomFactor / clientRect().height),
919
858
  onPositionChange: contextProps.onVerticalPositionChange,
920
859
  onZoomChange: verticalZoom => contextProps.onVerticalZoomChange?.(clamp(verticalZoom / (zoomFactor / clientRect().height), minVerticalZoom(), maxVerticalZoom()))
921
860
  })
922
861
  },
923
862
  get children() {
924
- return [createComponent(PianoRollKeys, {}), createComponent(ScrollContainer, {
863
+ return [(() => {
864
+ const _el$3 = _tmpl$$1.cloneNode(true);
865
+ _el$3.style.setProperty("display", "flex");
866
+ _el$3.style.setProperty("width", "20%");
867
+ insert(_el$3, createComponent(Show, {
868
+ get when() {
869
+ return allProps.showTrackList;
870
+ },
871
+ get children() {
872
+ return createComponent(PianoRollTrackList, {});
873
+ }
874
+ }), null);
875
+ insert(_el$3, createComponent(Show, {
876
+ get when() {
877
+ return contextProps.mode === "keys";
878
+ },
879
+ get children() {
880
+ return createComponent(PianoRollKeys, {});
881
+ }
882
+ }), null);
883
+ return _el$3;
884
+ })(), createComponent(ScrollContainer, {
925
885
  ref: setScrollerRef,
926
886
  get children() {
927
887
  return [memo(() => allProps.children), createComponent(PianoRollGrid, {}), createComponent(PianoRollNotes, {})];
@@ -944,7 +904,7 @@ const PianoRoll = allProps => {
944
904
  return maxZoom();
945
905
  }
946
906
  }), null);
947
- effect(() => className(_el$2, styles$3.PianoRollContainer));
907
+ effect(() => className(_el$2, styles$4.PianoRollContainer));
948
908
  return _el$;
949
909
  }
950
910
  });
@@ -1004,13 +964,37 @@ const PlayHead = allProps => {
1004
964
  })();
1005
965
  };
1006
966
 
967
+ const useNotes = () => {
968
+ const [notes, onNotesChange] = createSignal([]);
969
+ const onNoteChange = (index, note) => {
970
+ onNotesChange([...notes().slice(0, index), note, ...notes().splice(index + 1)]);
971
+ };
972
+ const onInsertNote = note => {
973
+ const index = Math.max(notes().findIndex(({
974
+ ticks
975
+ }) => ticks > note.ticks), 0);
976
+ onNotesChange([...notes().slice(0, index), note, ...notes().splice(index)]);
977
+ return index;
978
+ };
979
+ const onRemoveNote = index => {
980
+ onNotesChange([...notes().slice(0, index), ...notes().splice(index + 1)]);
981
+ };
982
+ return {
983
+ notes,
984
+ onNotesChange,
985
+ onNoteChange,
986
+ onInsertNote,
987
+ onRemoveNote
988
+ };
989
+ };
990
+
1007
991
  const usePianoRoll = () => {
1008
992
  const [ppq, onPpqChange] = createSignal(120);
1009
- const [condensed, onCondensedChange] = createSignal(false);
993
+ const [mode, onModeChange] = createSignal("tracks");
1010
994
  const [position, onPositionChange] = createSignal(0);
1011
995
  const [zoom, onZoomChange] = createSignal(10);
1012
996
  const [verticalZoom, onVerticalZoomChange] = createSignal(5);
1013
- const [verticalPosition, onVerticalPositionChange] = createSignal(64);
997
+ const [verticalPosition, onVerticalPositionChange] = createSignal(44);
1014
998
  const [gridDivision, onGridDivisionChange] = createSignal(4);
1015
999
  const [snapToGrid, onSnapToGridChange] = createSignal(true);
1016
1000
  const [duration, onDurationChange] = createSignal(0);
@@ -1029,12 +1013,12 @@ const usePianoRoll = () => {
1029
1013
  onGridDivisionChange,
1030
1014
  snapToGrid,
1031
1015
  onSnapToGridChange,
1032
- condensed,
1033
- onCondensedChange,
1016
+ mode,
1017
+ onModeChange,
1034
1018
  duration,
1035
1019
  onDurationChange
1036
1020
  };
1037
1021
  };
1038
1022
 
1039
- export { MultiTrackPianoRoll, PianoRoll, PlayHead, useNotes, usePianoRoll };
1023
+ export { PianoRoll, PlayHead, useNotes, usePianoRoll };
1040
1024
  //# sourceMappingURL=index.js.map