solid-pianoroll 0.0.13 → 0.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/index.js CHANGED
@@ -29,9 +29,9 @@ function styleInject(css, ref) {
29
29
  }
30
30
  }
31
31
 
32
- var css_248z$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";
33
- var styles$3 = {"PianoRoll":"PianoRoll-module_PianoRoll__rkRYx","PianoRollContainer":"PianoRoll-module_PianoRollContainer__Oyxr7"};
34
- styleInject(css_248z$3);
32
+ 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";
33
+ var styles$4 = {"PianoRoll":"PianoRoll-module_PianoRoll__rkRYx","PianoRollContainer":"PianoRoll-module_PianoRollContainer__Oyxr7"};
34
+ styleInject(css_248z$4);
35
35
 
36
36
  const PianoRollContext = solidJs.createContext();
37
37
  const PianoRollContextProvider = PianoRollContext.Provider;
@@ -40,34 +40,16 @@ const usePianoRollContext = () => {
40
40
  if (!context) throw new Error("No PianoRollContext found");
41
41
  return context;
42
42
  };
43
- const splitContextProps = allProps => solidJs.splitProps(allProps, ["condensed", "duration", "gridDivision", "notes", "position", "ppq", "snapToGrid", "verticalPosition", "verticalZoom", "zoom", "onInsertNote", "onNoteChange", "onPositionChange", "onRemoveNote", "onVerticalPositionChange", "onVerticalZoomChange", "onZoomChange"]);
43
+ const splitContextProps = allProps => solidJs.splitProps(allProps, ["mode", "duration", "gridDivision", "tracks", "position", "ppq", "snapToGrid", "verticalPosition", "verticalZoom", "zoom", "onInsertNote", "onNoteChange", "onPositionChange", "onRemoveNote", "onVerticalPositionChange", "onVerticalZoomChange", "onZoomChange", "showAllTracks", "selectedTrackIndex", "onSelectedTrackIndexChange"]);
44
44
 
45
- const _tmpl$$7 = /*#__PURE__*/web.template(`<input max="500" min="1" step="0.01">`, 1);
46
- const HorizontalZoomControl = props => {
47
- const context = usePianoRollContext();
48
- return (() => {
49
- const _el$ = _tmpl$$7.cloneNode(true);
50
- web.spread(_el$, web.mergeProps(props, {
51
- get value() {
52
- return context.zoom;
53
- },
54
- "onInput": event => context.onZoomChange?.(event.currentTarget.valueAsNumber),
55
- "type": "range",
56
- get style() {
57
- return {
58
- "margin-left": "50px",
59
- "margin-right": "16px",
60
- ...(typeof props.style === "object" && props.style)
61
- };
62
- }
63
- }), false, false);
64
- return _el$;
65
- })();
66
- };
45
+ var css_248z$3 = ".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";
46
+ var styles$3 = {"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"};
47
+ styleInject(css_248z$3);
67
48
 
68
49
  function createViewPortDimension(getState) {
69
50
  const getStateWithFunctions = () => ({
70
51
  ...getState(),
52
+ calculatePixelOffset,
71
53
  calculatePixelValue,
72
54
  calculatePosition,
73
55
  calculatePixelDimensions,
@@ -136,11 +118,70 @@ const useViewPortDimension = name => {
136
118
  return viewPort;
137
119
  };
138
120
 
121
+ const _tmpl$$8 = /*#__PURE__*/web.template(`<div></div>`, 2);
122
+ const PianoRollKeys = () => {
123
+ const viewPort = solidJs.createMemo(() => useViewPortDimension("vertical"));
124
+ return (() => {
125
+ const _el$ = _tmpl$$8.cloneNode(true);
126
+ web.insert(_el$, web.createComponent(solidJs.Index, {
127
+ each: keys,
128
+ children: key => {
129
+ const virtualDimensions = solidJs.createMemo(() => viewPort().calculatePixelDimensions(127 - key().number, 1));
130
+ return web.createComponent(solidJs.Show, {
131
+ get when() {
132
+ return virtualDimensions().size > 0;
133
+ },
134
+ get children() {
135
+ const _el$2 = _tmpl$$8.cloneNode(true);
136
+ web.effect(_p$ => {
137
+ const _v$ = {
138
+ [styles$3["Key"]]: true,
139
+ [styles$3["black"]]: key().isBlack,
140
+ [styles$3["white"]]: !key().isBlack,
141
+ [styles$3["whiteAndNextIsWhite"]]: !key().isBlack && !blackKeys.includes((key().number + 1) % 12),
142
+ [styles$3["whiteAndPreviousIsWhite"]]: !key().isBlack && !blackKeys.includes((key().number - 1) % 12)
143
+ },
144
+ _v$2 = key().name,
145
+ _v$3 = key().number % 12,
146
+ _v$4 = `${virtualDimensions().offset}px`,
147
+ _v$5 = `${virtualDimensions().size}px`;
148
+ _p$._v$ = web.classList(_el$2, _v$, _p$._v$);
149
+ _v$2 !== _p$._v$2 && web.setAttribute(_el$2, "title", _p$._v$2 = _v$2);
150
+ _v$3 !== _p$._v$3 && web.setAttribute(_el$2, "data-index", _p$._v$3 = _v$3);
151
+ _v$4 !== _p$._v$4 && _el$2.style.setProperty("top", _p$._v$4 = _v$4);
152
+ _v$5 !== _p$._v$5 && _el$2.style.setProperty("height", _p$._v$5 = _v$5);
153
+ return _p$;
154
+ }, {
155
+ _v$: undefined,
156
+ _v$2: undefined,
157
+ _v$3: undefined,
158
+ _v$4: undefined,
159
+ _v$5: undefined
160
+ });
161
+ return _el$2;
162
+ }
163
+ });
164
+ }
165
+ }));
166
+ web.effect(() => web.className(_el$, styles$3.PianoRollKeys));
167
+ return _el$;
168
+ })();
169
+ };
170
+ const blackKeys = [1, 3, 6, 8, 10];
171
+ const keyNames = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
172
+ const keys = Array.from({
173
+ length: 128
174
+ }).map((_, index) => ({
175
+ number: index,
176
+ name: `${keyNames[index % 12]} ${Math.floor(index / 12) - 2}`,
177
+ isBlack: blackKeys.includes(index % 12)
178
+ }));
179
+
139
180
  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}";
140
181
  var styles$2 = {"PianoRollNotes":"PianoRollNotes-module_PianoRollNotes__6pF-y","Note":"PianoRollNotes-module_Note__-jxLb","trimStart":"PianoRollNotes-module_trimStart__vuBlj","trimEnd":"PianoRollNotes-module_trimEnd__zPdjr"};
141
182
  styleInject(css_248z$2);
142
183
 
143
- const _tmpl$$6 = /*#__PURE__*/web.template(`<div></div>`, 2);
184
+ const _tmpl$$7 = /*#__PURE__*/web.template(`<div></div>`, 2);
144
185
  const PianoRollNotes = props => {
145
186
  const context = usePianoRollContext();
146
187
  const verticalViewPort = solidJs.createMemo(() => useViewPortDimension("vertical"));
@@ -149,10 +190,11 @@ const PianoRollNotes = props => {
149
190
  const snapValueToGridIfEnabled = (value, altKey) => context.snapToGrid && !altKey ? Math.round(value / gridDivisionTicks()) * gridDivisionTicks() : value;
150
191
  const insertOrUpdateNote = event => {
151
192
  const position = horizontalViewPort().calculatePosition(event.clientX);
152
- const midi = context.condensed ? 60 : 127 - Math.floor(verticalViewPort().calculatePosition(event.clientY));
193
+ const existingNote = currentNote();
194
+ const midi = context.mode === "keys" ? 127 - Math.floor(verticalViewPort().calculatePosition(event.clientY)) : existingNote?.midi ?? 60;
195
+ const targetTrackIndex = context.mode === "tracks" ? Math.floor(verticalViewPort().calculatePosition(event.clientY)) : context.selectedTrackIndex ?? 0;
153
196
  const eventPositionTicks = Math.floor(position / gridDivisionTicks()) * gridDivisionTicks();
154
197
  const velocity = 127;
155
- const existingNote = newNote();
156
198
  const ticks = existingNote?.ticks ?? eventPositionTicks;
157
199
  const durationTicks = existingNote?.ticks ? eventPositionTicks - existingNote?.ticks : gridDivisionTicks();
158
200
  const note = {
@@ -162,25 +204,26 @@ const PianoRollNotes = props => {
162
204
  velocity
163
205
  };
164
206
  if (existingNote) {
165
- context.onNoteChange?.(newNoteIndex(), note);
166
- return newNoteIndex();
207
+ context.onNoteChange?.(currentNoteTrackIndex(), currentNoteIndex(), note);
208
+ return currentNoteIndex();
167
209
  } else {
168
- return context.onInsertNote?.(note) ?? -1;
210
+ return context.onInsertNote?.(targetTrackIndex, note) ?? -1;
169
211
  }
170
212
  };
171
213
  const [isDragging, setIsDragging] = solidJs.createSignal(false);
172
214
  const [noteDragMode, setNoteDragMode] = solidJs.createSignal();
173
- const [newNoteIndex, setNewNoteIndex] = solidJs.createSignal(-1);
215
+ const [currentNoteIndex, setCurrentNoteIndex] = solidJs.createSignal(-1);
216
+ const [currentNoteTrackIndex, setCurrentNoteTrackIndex] = solidJs.createSignal(-1);
174
217
  const [isMouseDown, setIsMouseDown] = solidJs.createSignal(false);
175
- const newNote = solidJs.createMemo(() => context.notes[newNoteIndex()]);
218
+ const currentNote = solidJs.createMemo(() => context.tracks[currentNoteTrackIndex()]?.notes[currentNoteIndex()]);
176
219
  const getClasses = noteDragMode => {
177
220
  return noteDragMode ? [styles$2.Note, styles$2[noteDragMode]] : [styles$2.Note];
178
221
  };
179
222
  return (() => {
180
- const _el$ = _tmpl$$6.cloneNode(true);
223
+ const _el$ = _tmpl$$7.cloneNode(true);
181
224
  _el$.$$click = event => {
182
- if (newNote()) {
183
- setNewNoteIndex(-1);
225
+ if (currentNote()) {
226
+ setCurrentNoteIndex(-1);
184
227
  return;
185
228
  }
186
229
  if (!event.altKey) return;
@@ -201,94 +244,133 @@ const PianoRollNotes = props => {
201
244
  return;
202
245
  }
203
246
  const index = insertOrUpdateNote(event);
204
- setNewNoteIndex(index);
247
+ setCurrentNoteIndex(index);
205
248
  };
206
249
  _el$.$$mousedown = event => {
207
- console.log("down");
208
- setIsMouseDown(true);
209
- const index = insertOrUpdateNote(event);
210
- setNewNoteIndex(index);
250
+ solidJs.batch(() => {
251
+ setIsMouseDown(true);
252
+ const index = insertOrUpdateNote(event);
253
+ setCurrentNoteTrackIndex(context.selectedTrackIndex ?? 0);
254
+ setCurrentNoteIndex(index);
255
+ });
211
256
  };
212
257
  const _ref$ = props.ref;
213
258
  typeof _ref$ === "function" ? web.use(_ref$, _el$) : props.ref = _el$;
214
- web.insert(_el$, web.createComponent(solidJs.Index, {
259
+ web.insert(_el$, web.createComponent(solidJs.For, {
215
260
  get each() {
216
- return context.notes;
261
+ return context.tracks;
217
262
  },
218
- children: (note, index) => {
219
- const verticalVirtualDimensions = solidJs.createMemo(() => verticalViewPort().calculatePixelDimensions(127 - note().midi, 1));
220
- const horizontalDimensions = solidJs.createMemo(() => horizontalViewPort().calculatePixelDimensions(note().ticks, note().durationTicks));
263
+ children: (track, trackIndex) => {
221
264
  return web.createComponent(solidJs.Show, {
222
265
  get when() {
223
- return web.memo(() => !!(verticalViewPort().isVisible(verticalVirtualDimensions()) || context.condensed))() && horizontalViewPort().isVisible(horizontalDimensions());
266
+ return trackIndex() === context.selectedTrackIndex || context.showAllTracks || context.mode === "tracks";
224
267
  },
225
268
  get children() {
226
- const _el$2 = _tmpl$$6.cloneNode(true);
227
- _el$2.$$mousedown = event => {
228
- event.stopPropagation();
229
- setIsDragging(true);
230
- const initialPosition = horizontalViewPort().calculatePosition(event.clientX);
231
- const diffPosition = initialPosition - note().ticks;
232
- const handleMouseMove = mouseMoveEvent => {
233
- const ticks = snapValueToGridIfEnabled(Math.max(horizontalViewPort().calculatePosition(mouseMoveEvent.clientX) - diffPosition, 0), mouseMoveEvent.altKey);
234
- context.onNoteChange?.(index, {
235
- ...note(),
236
- ...(noteDragMode() === "move" && !context.condensed && {
237
- midi: Math.round(127 - verticalViewPort().calculatePosition(mouseMoveEvent.clientY))
238
- }),
239
- ...((noteDragMode() === "move" || noteDragMode() === "trimStart") && {
240
- ticks
241
- }),
242
- ...(noteDragMode() === "trimStart" && {
243
- durationTicks: note().durationTicks + note().ticks - ticks
244
- }),
245
- ...(noteDragMode() === "trimEnd" && {
246
- durationTicks: snapValueToGridIfEnabled(horizontalViewPort().calculatePosition(mouseMoveEvent.clientX) - note().ticks, mouseMoveEvent.altKey)
247
- })
269
+ return web.createComponent(solidJs.For, {
270
+ get each() {
271
+ return track.notes;
272
+ },
273
+ children: (note, noteIndex) => {
274
+ const verticalVirtualDimensions = solidJs.createMemo(() => verticalViewPort().calculatePixelDimensions(context.mode === "keys" ? 127 - note.midi : trackIndex(), 1));
275
+ const horizontalDimensions = solidJs.createMemo(() => horizontalViewPort().calculatePixelDimensions(note.ticks, note.durationTicks));
276
+ return web.createComponent(solidJs.Show, {
277
+ get when() {
278
+ return web.memo(() => !!verticalViewPort().isVisible(verticalVirtualDimensions()))() && horizontalViewPort().isVisible(horizontalDimensions());
279
+ },
280
+ get children() {
281
+ const _el$2 = _tmpl$$7.cloneNode(true);
282
+ _el$2.$$mousedown = event => {
283
+ event.stopPropagation();
284
+ setIsDragging(true);
285
+ setCurrentNoteIndex(noteIndex());
286
+ setCurrentNoteTrackIndex(trackIndex());
287
+ const initialPosition = horizontalViewPort().calculatePosition(event.clientX);
288
+ const diffPosition = initialPosition - note.ticks;
289
+ const handleMouseMove = mouseMoveEvent => {
290
+ const note = currentNote();
291
+ if (!note) return;
292
+ const ticks = snapValueToGridIfEnabled(Math.max(horizontalViewPort().calculatePosition(mouseMoveEvent.clientX) - diffPosition, 0), mouseMoveEvent.altKey);
293
+ const updatedNote = {
294
+ ...note,
295
+ ...(noteDragMode() === "move" && context.mode === "keys" && {
296
+ midi: Math.round(127 - verticalViewPort().calculatePosition(mouseMoveEvent.clientY))
297
+ }),
298
+ ...((noteDragMode() === "move" || noteDragMode() === "trimStart") && {
299
+ ticks
300
+ }),
301
+ ...(noteDragMode() === "trimStart" && {
302
+ durationTicks: note.durationTicks + note.ticks - ticks
303
+ }),
304
+ ...(noteDragMode() === "trimEnd" && {
305
+ durationTicks: snapValueToGridIfEnabled(horizontalViewPort().calculatePosition(mouseMoveEvent.clientX) - note.ticks, mouseMoveEvent.altKey)
306
+ })
307
+ };
308
+ const previousTrackIndex = currentNoteTrackIndex();
309
+ const previousNoteIndex = currentNoteIndex();
310
+ const targetTrackIndex = context.mode === "tracks" ? Math.floor(verticalViewPort().calculatePosition(mouseMoveEvent.clientY)) : previousTrackIndex;
311
+ if (targetTrackIndex === previousTrackIndex) {
312
+ context.onNoteChange?.(previousTrackIndex, previousNoteIndex, updatedNote);
313
+ } else {
314
+ solidJs.batch(() => {
315
+ if (context.onInsertNote) {
316
+ context.onRemoveNote?.(previousTrackIndex, previousNoteIndex);
317
+ const newNoteIndex = context.onInsertNote(targetTrackIndex, updatedNote);
318
+ setCurrentNoteTrackIndex(targetTrackIndex);
319
+ setCurrentNoteIndex(newNoteIndex);
320
+ }
321
+ });
322
+ }
323
+ };
324
+ const handleMouseUp = () => {
325
+ setIsDragging(false);
326
+ setCurrentNoteIndex(-1);
327
+ setCurrentNoteTrackIndex(-1);
328
+ window.removeEventListener("mousemove", handleMouseMove);
329
+ window.removeEventListener("mouseup", handleMouseUp);
330
+ };
331
+ window.addEventListener("mousemove", handleMouseMove);
332
+ window.addEventListener("mouseup", handleMouseUp);
333
+ };
334
+ _el$2.$$dblclick = event => {
335
+ event.stopPropagation();
336
+ context.onRemoveNote?.(trackIndex(), noteIndex());
337
+ };
338
+ _el$2.$$mousemove = event => {
339
+ if (isDragging()) return;
340
+ event.stopPropagation();
341
+ const relativeX = horizontalViewPort().calculatePixelValue(horizontalViewPort().calculatePosition(event.clientX));
342
+ const noteStartX = horizontalViewPort().calculatePixelValue(note.ticks);
343
+ const noteEndX = horizontalViewPort().calculatePixelValue(note.ticks + note.durationTicks);
344
+ setNoteDragMode(relativeX - noteStartX < 3 ? "trimStart" : noteEndX - relativeX < 3 ? "trimEnd" : "move");
345
+ };
346
+ web.setAttribute(_el$2, "draggable", false);
347
+ web.effect(_p$ => {
348
+ const _v$ = getClasses(noteDragMode()).join(" "),
349
+ _v$2 = `rgba(255,0,0, ${(128 + note.velocity) / 256})`,
350
+ _v$3 = `${verticalVirtualDimensions().offset}px`,
351
+ _v$4 = `${verticalVirtualDimensions().size}px`,
352
+ _v$5 = `${horizontalDimensions().offset}px`,
353
+ _v$6 = `${horizontalDimensions().size}px`;
354
+ _v$ !== _p$._v$ && web.className(_el$2, _p$._v$ = _v$);
355
+ _v$2 !== _p$._v$2 && _el$2.style.setProperty("background-color", _p$._v$2 = _v$2);
356
+ _v$3 !== _p$._v$3 && _el$2.style.setProperty("top", _p$._v$3 = _v$3);
357
+ _v$4 !== _p$._v$4 && _el$2.style.setProperty("height", _p$._v$4 = _v$4);
358
+ _v$5 !== _p$._v$5 && _el$2.style.setProperty("left", _p$._v$5 = _v$5);
359
+ _v$6 !== _p$._v$6 && _el$2.style.setProperty("width", _p$._v$6 = _v$6);
360
+ return _p$;
361
+ }, {
362
+ _v$: undefined,
363
+ _v$2: undefined,
364
+ _v$3: undefined,
365
+ _v$4: undefined,
366
+ _v$5: undefined,
367
+ _v$6: undefined
368
+ });
369
+ return _el$2;
370
+ }
248
371
  });
249
- };
250
- const handleMouseUp = () => {
251
- setIsDragging(false);
252
- window.removeEventListener("mousemove", handleMouseMove);
253
- window.removeEventListener("mouseup", handleMouseUp);
254
- };
255
- window.addEventListener("mousemove", handleMouseMove);
256
- window.addEventListener("mouseup", handleMouseUp);
257
- };
258
- _el$2.$$dblclick = event => {
259
- event.stopPropagation();
260
- context.onRemoveNote?.(index);
261
- };
262
- _el$2.$$mousemove = event => {
263
- if (isDragging()) return;
264
- event.stopPropagation();
265
- const relativeX = horizontalViewPort().calculatePixelValue(horizontalViewPort().calculatePosition(event.clientX));
266
- const noteStartX = horizontalViewPort().calculatePixelValue(note().ticks);
267
- const noteEndX = horizontalViewPort().calculatePixelValue(note().ticks + note().durationTicks);
268
- setNoteDragMode(relativeX - noteStartX < 3 ? "trimStart" : noteEndX - relativeX < 3 ? "trimEnd" : "move");
269
- };
270
- web.effect(_p$ => {
271
- const _v$ = getClasses(noteDragMode()).join(" "),
272
- _v$2 = {
273
- "background-color": `rgba(255,0,0, ${(128 + note().velocity) / 256})`,
274
- ...(context.condensed ? {
275
- top: 0,
276
- height: "100%"
277
- } : {
278
- top: `${verticalVirtualDimensions().offset}px`,
279
- height: `${verticalVirtualDimensions().size}px`
280
- }),
281
- left: `${horizontalDimensions().offset}px`,
282
- width: `${horizontalDimensions().size}px`
283
- };
284
- _v$ !== _p$._v$ && web.className(_el$2, _p$._v$ = _v$);
285
- _p$._v$2 = web.style(_el$2, _v$2, _p$._v$2);
286
- return _p$;
287
- }, {
288
- _v$: undefined,
289
- _v$2: undefined
372
+ }
290
373
  });
291
- return _el$2;
292
374
  }
293
375
  });
294
376
  }
@@ -299,7 +381,7 @@ const PianoRollNotes = props => {
299
381
  };
300
382
  web.delegateEvents(["mousedown", "mousemove", "mouseup", "dblclick", "click"]);
301
383
 
302
- const _tmpl$$5 = /*#__PURE__*/web.template(`<div class="PianoRoll-Scroller"><div><div><div></div></div></div></div>`, 8);
384
+ const _tmpl$$6 = /*#__PURE__*/web.template(`<div class="PianoRoll-Scroller"><div><div><div></div></div></div></div>`, 8);
303
385
  const ScrollContainer = props => {
304
386
  let scrollContentRef;
305
387
  const verticalViewPort = solidJs.createMemo(() => useViewPortDimension("vertical"));
@@ -361,7 +443,7 @@ const ScrollContainer = props => {
361
443
  });
362
444
  });
363
445
  return (() => {
364
- const _el$ = _tmpl$$5.cloneNode(true),
446
+ const _el$ = _tmpl$$6.cloneNode(true),
365
447
  _el$2 = _el$.firstChild,
366
448
  _el$3 = _el$2.firstChild,
367
449
  _el$4 = _el$3.firstChild;
@@ -402,74 +484,11 @@ const ScrollContainer = props => {
402
484
  })();
403
485
  };
404
486
 
405
- 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";
406
- 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"};
487
+ 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";
488
+ var styles$1 = {"PianoRollGrid":"PianoRollGrid-module_PianoRollGrid__tG119","PianoRollGrid-Row":"PianoRollGrid-module_PianoRollGrid-Row__nRMCs","PianoRollGrid-Time":"PianoRollGrid-module_PianoRollGrid-Time__jLz3E"};
407
489
  styleInject(css_248z$1);
408
490
 
409
- const _tmpl$$4 = /*#__PURE__*/web.template(`<div></div>`, 2);
410
- const PianoRollKeys = () => {
411
- const viewPort = solidJs.createMemo(() => useViewPortDimension("vertical"));
412
- return (() => {
413
- const _el$ = _tmpl$$4.cloneNode(true);
414
- web.insert(_el$, web.createComponent(solidJs.Index, {
415
- each: keys,
416
- children: key => {
417
- const virtualDimensions = solidJs.createMemo(() => viewPort().calculatePixelDimensions(127 - key().number, 1));
418
- return web.createComponent(solidJs.Show, {
419
- get when() {
420
- return virtualDimensions().size > 0;
421
- },
422
- get children() {
423
- const _el$2 = _tmpl$$4.cloneNode(true);
424
- web.effect(_p$ => {
425
- const _v$ = {
426
- [styles$1["Key"]]: true,
427
- [styles$1["black"]]: key().isBlack,
428
- [styles$1["white"]]: !key().isBlack,
429
- [styles$1["whiteAndNextIsWhite"]]: !key().isBlack && !blackKeys.includes((key().number + 1) % 12),
430
- [styles$1["whiteAndPreviousIsWhite"]]: !key().isBlack && !blackKeys.includes((key().number - 1) % 12)
431
- },
432
- _v$2 = key().name,
433
- _v$3 = key().number % 12,
434
- _v$4 = `${virtualDimensions().offset}px`,
435
- _v$5 = `${virtualDimensions().size}px`;
436
- _p$._v$ = web.classList(_el$2, _v$, _p$._v$);
437
- _v$2 !== _p$._v$2 && web.setAttribute(_el$2, "title", _p$._v$2 = _v$2);
438
- _v$3 !== _p$._v$3 && web.setAttribute(_el$2, "data-index", _p$._v$3 = _v$3);
439
- _v$4 !== _p$._v$4 && _el$2.style.setProperty("top", _p$._v$4 = _v$4);
440
- _v$5 !== _p$._v$5 && _el$2.style.setProperty("height", _p$._v$5 = _v$5);
441
- return _p$;
442
- }, {
443
- _v$: undefined,
444
- _v$2: undefined,
445
- _v$3: undefined,
446
- _v$4: undefined,
447
- _v$5: undefined
448
- });
449
- return _el$2;
450
- }
451
- });
452
- }
453
- }));
454
- web.effect(() => web.className(_el$, styles$1.PianoRollKeys));
455
- return _el$;
456
- })();
457
- };
458
- const blackKeys = [1, 3, 6, 8, 10];
459
- const keyNames = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
460
- const keys = Array.from({
461
- length: 128
462
- }).map((_, index) => ({
463
- number: index,
464
- name: `${keyNames[index % 12]} ${Math.floor(index / 12) - 2}`,
465
- isBlack: blackKeys.includes(index % 12)
466
- }));
467
-
468
- 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";
469
- var styles = {"PianoRollGrid":"PianoRollGrid-module_PianoRollGrid__tG119","PianoRollGrid-Key":"PianoRollGrid-module_PianoRollGrid-Key__VRbiB","PianoRollGrid-Time":"PianoRollGrid-module_PianoRollGrid-Time__jLz3E"};
470
- styleInject(css_248z);
471
-
472
- const _tmpl$$3 = /*#__PURE__*/web.template(`<div></div>`, 2);
491
+ const _tmpl$$5 = /*#__PURE__*/web.template(`<div></div>`, 2);
473
492
  const PianoRollGrid = () => {
474
493
  const context = usePianoRollContext();
475
494
  const verticalViewPort = solidJs.createMemo(() => useViewPortDimension("vertical"));
@@ -477,10 +496,12 @@ const PianoRollGrid = () => {
477
496
  const measureTicks = solidJs.createMemo(() => context.ppq * 4);
478
497
  const selectedGridDivisorTicks = solidJs.createMemo(() => measureTicks() / context.gridDivision);
479
498
  function calculateVisibleGridDivisorTicks(value) {
480
- if (horizontalViewPort().calculateVisibleRange() / value > 100) {
499
+ const visibleRange = horizontalViewPort().calculateVisibleRange();
500
+ if (visibleRange <= 0) return 0;
501
+ if (visibleRange / value > 100) {
481
502
  return calculateVisibleGridDivisorTicks(value * 2);
482
503
  }
483
- if (horizontalViewPort().calculateVisibleRange() / value < 30) {
504
+ if (visibleRange / value < 30) {
484
505
  return calculateVisibleGridDivisorTicks(value / 2);
485
506
  }
486
507
  return value;
@@ -497,7 +518,7 @@ const PianoRollGrid = () => {
497
518
  }));
498
519
  });
499
520
  return (() => {
500
- const _el$ = _tmpl$$3.cloneNode(true);
521
+ const _el$ = _tmpl$$5.cloneNode(true);
501
522
  web.insert(_el$, web.createComponent(solidJs.Index, {
502
523
  get each() {
503
524
  return gridArray();
@@ -509,9 +530,9 @@ const PianoRollGrid = () => {
509
530
  return virtualDimensions().size > 0;
510
531
  },
511
532
  get children() {
512
- const _el$2 = _tmpl$$3.cloneNode(true);
533
+ const _el$2 = _tmpl$$5.cloneNode(true);
513
534
  web.effect(_p$ => {
514
- const _v$ = styles["PianoRollGrid-Time"],
535
+ const _v$ = styles$1["PianoRollGrid-Time"],
515
536
  _v$2 = Math.ceil((entry().index + 1 * selectedGridDivisorTicks()) / measureTicks()) % 2 === 0 ? "#ddd" : "#ccc",
516
537
  _v$3 = `${virtualDimensions().offset}px`,
517
538
  _v$4 = `${virtualDimensions().size}px`,
@@ -536,7 +557,7 @@ const PianoRollGrid = () => {
536
557
  }), null);
537
558
  web.insert(_el$, web.createComponent(solidJs.Show, {
538
559
  get when() {
539
- return !context.condensed;
560
+ return context.mode === "keys";
540
561
  },
541
562
  get children() {
542
563
  return web.createComponent(solidJs.Index, {
@@ -548,10 +569,10 @@ const PianoRollGrid = () => {
548
569
  return virtualDimensions().size > 0;
549
570
  },
550
571
  get children() {
551
- const _el$3 = _tmpl$$3.cloneNode(true);
572
+ const _el$3 = _tmpl$$5.cloneNode(true);
552
573
  _el$3.style.setProperty("width", "100%");
553
574
  web.effect(_p$ => {
554
- const _v$6 = styles["PianoRollGrid-Key"],
575
+ const _v$6 = styles$1["PianoRollGrid-Row"],
555
576
  _v$7 = `${virtualDimensions().offset}px`,
556
577
  _v$8 = `${virtualDimensions().size}px`,
557
578
  _v$9 = key().isBlack ? "rgba(0,0,0,0.2)" : "none",
@@ -576,35 +597,53 @@ const PianoRollGrid = () => {
576
597
  });
577
598
  }
578
599
  }), null);
579
- web.effect(() => web.className(_el$, styles.PianoRollGrid));
600
+ web.insert(_el$, web.createComponent(solidJs.Show, {
601
+ get when() {
602
+ return context.mode === "tracks";
603
+ },
604
+ get children() {
605
+ return web.createComponent(solidJs.For, {
606
+ get each() {
607
+ return context.tracks;
608
+ },
609
+ children: (track, index) => {
610
+ const virtualDimensions = solidJs.createMemo(() => verticalViewPort().calculatePixelDimensions(index(), 1));
611
+ return web.createComponent(solidJs.Show, {
612
+ get when() {
613
+ return virtualDimensions().size > 0;
614
+ },
615
+ get children() {
616
+ const _el$4 = _tmpl$$5.cloneNode(true);
617
+ _el$4.style.setProperty("width", "100%");
618
+ _el$4.style.setProperty("border-width", "0");
619
+ web.effect(_p$ => {
620
+ const _v$11 = styles$1["PianoRollGrid-Row"],
621
+ _v$12 = `${virtualDimensions().offset}px`,
622
+ _v$13 = `${virtualDimensions().size}px`,
623
+ _v$14 = index() % 2 === 0 ? "rgba(0,0,0,0.2)" : "none";
624
+ _v$11 !== _p$._v$11 && web.className(_el$4, _p$._v$11 = _v$11);
625
+ _v$12 !== _p$._v$12 && _el$4.style.setProperty("top", _p$._v$12 = _v$12);
626
+ _v$13 !== _p$._v$13 && _el$4.style.setProperty("height", _p$._v$13 = _v$13);
627
+ _v$14 !== _p$._v$14 && _el$4.style.setProperty("background-color", _p$._v$14 = _v$14);
628
+ return _p$;
629
+ }, {
630
+ _v$11: undefined,
631
+ _v$12: undefined,
632
+ _v$13: undefined,
633
+ _v$14: undefined
634
+ });
635
+ return _el$4;
636
+ }
637
+ });
638
+ }
639
+ });
640
+ }
641
+ }), null);
642
+ web.effect(() => web.className(_el$, styles$1.PianoRollGrid));
580
643
  return _el$;
581
644
  })();
582
645
  };
583
646
 
584
- const useNotes = () => {
585
- const [notes, onNotesChange] = solidJs.createSignal([]);
586
- const onNoteChange = (index, note) => {
587
- onNotesChange([...notes().slice(0, index), note, ...notes().splice(index + 1)]);
588
- };
589
- const onInsertNote = note => {
590
- const index = Math.max(notes().findIndex(({
591
- ticks
592
- }) => ticks > note.ticks), 0);
593
- onNotesChange([...notes().slice(0, index), note, ...notes().splice(index)]);
594
- return index;
595
- };
596
- const onRemoveNote = index => {
597
- onNotesChange([...notes().slice(0, index), ...notes().splice(index + 1)]);
598
- };
599
- return {
600
- notes,
601
- onNotesChange,
602
- onNoteChange,
603
- onInsertNote,
604
- onRemoveNote
605
- };
606
- };
607
-
608
647
  const defaultRect = {
609
648
  left: 0,
610
649
  width: 0,
@@ -633,11 +672,11 @@ function useBoundingClientRect(containerRef) {
633
672
  return boundingClientRect;
634
673
  }
635
674
 
636
- const _tmpl$$2 = /*#__PURE__*/web.template(`<input min="1" max="11" step="0.01">`, 1);
675
+ const _tmpl$$4 = /*#__PURE__*/web.template(`<input min="1" max="11" step="0.01">`, 1);
637
676
  const VerticalZoomControl = props => {
638
677
  const context = usePianoRollContext();
639
678
  return (() => {
640
- const _el$ = _tmpl$$2.cloneNode(true);
679
+ const _el$ = _tmpl$$4.cloneNode(true);
641
680
  web.spread(_el$, web.mergeProps(props, {
642
681
  get value() {
643
682
  return context.verticalZoom;
@@ -661,196 +700,91 @@ const VerticalZoomControl = props => {
661
700
  })();
662
701
  };
663
702
 
664
- const _tmpl$$1 = /*#__PURE__*/web.template(`<ul></ul>`, 2),
665
- _tmpl$2 = /*#__PURE__*/web.template(`<div><ul></ul><div><div></div></div></div>`, 8),
666
- _tmpl$3 = /*#__PURE__*/web.template(`<li></li>`, 2);
667
- const MultiTrackPianoRoll = allProps => {
668
- const condensed = solidJs.createMemo(() => !allProps.selectedTrack);
669
- const [ownProps, restProps] = solidJs.splitProps(allProps, ["tracks", "selectedTrack"]);
670
- const [contextProps, divProps] = splitContextProps(solidJs.mergeProps(restProps, {
671
- notes: [],
672
- condensed: false
673
- }));
674
- const [scrollerRef, setScrollerRef] = solidJs.createSignal();
675
- const clientRect = useBoundingClientRect(scrollerRef);
676
- const zoomFactor = 500;
677
- const minZoom = solidJs.createMemo(() => 1 / (zoomFactor / clientRect().width));
678
- const maxZoom = solidJs.createMemo(() => 500 * (zoomFactor / clientRect().width));
679
- const minVerticalZoom = solidJs.createMemo(() => 1 / (zoomFactor / clientRect().height));
680
- const maxVerticalZoom = solidJs.createMemo(() => 10 * (zoomFactor / clientRect().height));
681
- return web.createComponent(PianoRollContextProvider, {
682
- get value() {
683
- return {
684
- ...contextProps
685
- };
686
- },
687
- get children() {
688
- return web.createComponent(ScrollZoomViewPort, {
689
- dimensions: {
690
- horizontal: () => ({
691
- pixelOffset: clientRect().left,
692
- pixelSize: clientRect().width,
693
- position: contextProps.position,
694
- range: contextProps.duration,
695
- zoom: contextProps.zoom * (zoomFactor / clientRect().width),
696
- onPositionChange: contextProps.onPositionChange,
697
- onZoomChange: zoom => contextProps.onZoomChange?.(clamp(zoom / (zoomFactor / clientRect().width), minZoom(), maxZoom()))
698
- }),
699
- vertical: () => ({
700
- pixelOffset: clientRect().top,
701
- pixelSize: clientRect().height,
702
- position: contextProps.verticalPosition,
703
- range: 128,
704
- zoom: contextProps.verticalZoom * (zoomFactor / clientRect().height),
705
- onPositionChange: contextProps.onVerticalPositionChange,
706
- onZoomChange: verticalZoom => contextProps.onVerticalZoomChange?.(clamp(verticalZoom / (zoomFactor / clientRect().height), minVerticalZoom(), maxVerticalZoom()))
707
- })
708
- },
709
- get children() {
710
- const _el$ = _tmpl$2.cloneNode(true),
711
- _el$2 = _el$.firstChild,
712
- _el$3 = _el$2.nextSibling,
713
- _el$4 = _el$3.firstChild;
714
- _el$.style.setProperty("display", "flex");
715
- _el$.style.setProperty("flex", "1");
716
- _el$.style.setProperty("height", "100%");
717
- _el$.style.setProperty("flex-direction", "row");
718
- _el$2.style.setProperty("margin-block-start", "0");
719
- _el$2.style.setProperty("padding-inline-start", "0");
720
- _el$2.style.setProperty("width", "150px");
721
- web.insert(_el$2, web.createComponent(solidJs.Index, {
722
- get each() {
723
- return allProps.tracks;
724
- },
725
- children: track => (() => {
726
- const _el$6 = _tmpl$3.cloneNode(true);
727
- _el$6.$$click = () => {
728
- if (track() === allProps.selectedTrack) {
729
- allProps.onSelectedTrackChange(undefined);
730
- } else {
731
- allProps.onSelectedTrackChange(track());
732
- }
733
- };
734
- _el$6.style.setProperty("width", "150px");
735
- _el$6.style.setProperty("height", "30px");
736
- _el$6.style.setProperty("list-style", "none");
737
- _el$6.style.setProperty("display", "flex");
738
- _el$6.style.setProperty("align-items", "center");
739
- _el$6.style.setProperty("border-top", "1px black solid");
740
- _el$6.style.setProperty("cursor", "pointer");
741
- web.insert(_el$6, () => track().name);
742
- web.effect(() => _el$6.style.setProperty("background", track() === allProps.selectedTrack ? "lightgray" : "none"));
743
- return _el$6;
744
- })()
745
- }));
746
- _el$3.style.setProperty("flex", "1");
747
- web.insert(_el$4, web.createComponent(solidJs.Show, {
748
- get when() {
749
- return !condensed();
750
- },
751
- get children() {
752
- return web.createComponent(PianoRollKeys, {});
753
- }
754
- }), null);
755
- web.insert(_el$4, web.createComponent(ScrollContainer, {
756
- ref: setScrollerRef,
757
- get children() {
758
- const _el$5 = _tmpl$$1.cloneNode(true);
759
- _el$5.style.setProperty("margin", "0");
760
- _el$5.style.setProperty("margin-block-start", "0");
761
- _el$5.style.setProperty("padding-inline-start", "0");
762
- web.insert(_el$5, web.createComponent(solidJs.Index, {
763
- get each() {
764
- return allProps.tracks;
765
- },
766
- children: track => {
767
- const notes = useNotes();
768
- solidJs.createEffect(() => {
769
- notes.onNotesChange(track().notes);
770
- });
771
- const [context, setContext] = store.createStore({
772
- ...contextProps
773
- });
774
- solidJs.createEffect(() => {
775
- setContext({
776
- ...contextProps,
777
- condensed: condensed(),
778
- notes: notes.notes(),
779
- onNoteChange: notes.onNoteChange,
780
- onInsertNote: notes.onInsertNote,
781
- onRemoveNote: notes.onRemoveNote
782
- });
783
- });
784
- const verticalViewPort = useViewPortDimension("vertical");
785
- return web.createComponent(solidJs.Show, {
786
- get when() {
787
- return condensed() || allProps.selectedTrack === track();
788
- },
789
- get children() {
790
- return web.createComponent(PianoRollContextProvider, {
791
- value: context,
792
- get children() {
793
- const _el$7 = _tmpl$3.cloneNode(true);
794
- _el$7.style.setProperty("display", "flex");
795
- _el$7.style.setProperty("position", "relative");
796
- _el$7.style.setProperty("list-style", "none");
797
- _el$7.style.setProperty("flex-direction", "row");
798
- _el$7.style.setProperty("border-top", "1px black solid");
799
- web.insert(_el$7, web.createComponent(PianoRollNotes, {}), null);
800
- web.insert(_el$7, web.createComponent(PianoRollGrid, {}), null);
801
- web.effect(() => _el$7.style.setProperty("height", condensed() ? "30px" : `${verticalViewPort.pixelSize}px`));
802
- return _el$7;
803
- }
804
- });
805
- }
806
- });
807
- }
808
- }));
809
- return _el$5;
810
- }
811
- }), null);
812
- web.insert(_el$4, web.createComponent(solidJs.Show, {
813
- get when() {
814
- return !condensed();
815
- },
816
- get children() {
817
- return web.createComponent(VerticalZoomControl, {
818
- get value() {
819
- return contextProps.verticalZoom;
703
+ const _tmpl$$3 = /*#__PURE__*/web.template(`<input max="500" min="1" step="0.01">`, 1);
704
+ const HorizontalZoomControl = props => {
705
+ const context = usePianoRollContext();
706
+ return (() => {
707
+ const _el$ = _tmpl$$3.cloneNode(true);
708
+ web.spread(_el$, web.mergeProps(props, {
709
+ get value() {
710
+ return context.zoom;
711
+ },
712
+ "onInput": event => context.onZoomChange?.(event.currentTarget.valueAsNumber),
713
+ "type": "range",
714
+ get style() {
715
+ return {
716
+ "margin-left": "50px",
717
+ "margin-right": "16px",
718
+ ...(typeof props.style === "object" && props.style)
719
+ };
720
+ }
721
+ }), false, false);
722
+ return _el$;
723
+ })();
724
+ };
725
+
726
+ 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";
727
+ var styles = {"PianoRollTrackList":"PianoRollTrackList-module_PianoRollTrackList__pyuxQ","Track":"PianoRollTrackList-module_Track__VZFIg"};
728
+ styleInject(css_248z);
729
+
730
+ const _tmpl$$2 = /*#__PURE__*/web.template(`<div></div>`, 2),
731
+ _tmpl$2$1 = /*#__PURE__*/web.template(`<div> </div>`, 2);
732
+ const PianoRollTrackList = () => {
733
+ const viewPort = solidJs.createMemo(() => useViewPortDimension("vertical"));
734
+ const context = usePianoRollContext();
735
+ return (() => {
736
+ const _el$ = _tmpl$$2.cloneNode(true);
737
+ web.insert(_el$, web.createComponent(solidJs.Index, {
738
+ get each() {
739
+ return context.tracks;
740
+ },
741
+ children: (track, index) => {
742
+ const virtualDimensions = solidJs.createMemo(() => viewPort().calculatePixelDimensions(context.mode === "tracks" ? index : index * 8, context.mode === "tracks" ? 1 : 8));
743
+ return web.createComponent(solidJs.Show, {
744
+ get when() {
745
+ return virtualDimensions().size > 0;
746
+ },
747
+ get children() {
748
+ const _el$2 = _tmpl$2$1.cloneNode(true),
749
+ _el$3 = _el$2.firstChild;
750
+ _el$2.$$click = () => context.onSelectedTrackIndexChange?.(index);
751
+ _el$2.style.setProperty("cursor", "pointer");
752
+ web.insert(_el$2, index, _el$3);
753
+ web.insert(_el$2, () => track().name || "[unnamed track]", null);
754
+ web.effect(_p$ => {
755
+ const _v$ = {
756
+ [styles["Track"]]: true
820
757
  },
821
- onInput: event => contextProps.onVerticalZoomChange?.(event.currentTarget.valueAsNumber)
822
- });
823
- }
824
- }), null);
825
- web.insert(_el$3, web.createComponent(HorizontalZoomControl, {
826
- style: {
827
- width: "100%",
828
- "margin-left": 0
829
- },
830
- get value() {
831
- return contextProps.zoom;
832
- },
833
- onInput: event => contextProps.onZoomChange?.(event.currentTarget.valueAsNumber)
834
- }), null);
835
- web.effect(_p$ => {
836
- const _v$ = styles$3.PianoRoll,
837
- _v$2 = styles$3.PianoRollContainer;
838
- _v$ !== _p$._v$ && web.className(_el$3, _p$._v$ = _v$);
839
- _v$2 !== _p$._v$2 && web.className(_el$4, _p$._v$2 = _v$2);
840
- return _p$;
841
- }, {
842
- _v$: undefined,
843
- _v$2: undefined
844
- });
845
- return _el$;
846
- }
847
- });
848
- }
849
- });
758
+ _v$2 = track().name,
759
+ _v$3 = `${virtualDimensions().offset}px`,
760
+ _v$4 = `${virtualDimensions().size}px`,
761
+ _v$5 = index === context.selectedTrackIndex ? "gray" : "lightgrey";
762
+ _p$._v$ = web.classList(_el$2, _v$, _p$._v$);
763
+ _v$2 !== _p$._v$2 && web.setAttribute(_el$2, "title", _p$._v$2 = _v$2);
764
+ _v$3 !== _p$._v$3 && _el$2.style.setProperty("top", _p$._v$3 = _v$3);
765
+ _v$4 !== _p$._v$4 && _el$2.style.setProperty("height", _p$._v$4 = _v$4);
766
+ _v$5 !== _p$._v$5 && _el$2.style.setProperty("background", _p$._v$5 = _v$5);
767
+ return _p$;
768
+ }, {
769
+ _v$: undefined,
770
+ _v$2: undefined,
771
+ _v$3: undefined,
772
+ _v$4: undefined,
773
+ _v$5: undefined
774
+ });
775
+ return _el$2;
776
+ }
777
+ });
778
+ }
779
+ }));
780
+ web.effect(() => web.className(_el$, styles.PianoRollTrackList));
781
+ return _el$;
782
+ })();
850
783
  };
851
784
  web.delegateEvents(["click"]);
852
785
 
853
- const _tmpl$ = /*#__PURE__*/web.template(`<div><div></div></div>`, 4);
786
+ const _tmpl$$1 = /*#__PURE__*/web.template(`<div></div>`, 2),
787
+ _tmpl$2 = /*#__PURE__*/web.template(`<div><div></div></div>`, 4);
854
788
  const PianoRoll = allProps => {
855
789
  const [contextProps, divProps] = splitContextProps(allProps);
856
790
  const [scrollerRef, setScrollerRef] = solidJs.createSignal();
@@ -860,14 +794,20 @@ const PianoRoll = allProps => {
860
794
  const maxZoom = solidJs.createMemo(() => 500 * (zoomFactor / clientRect().width));
861
795
  const minVerticalZoom = solidJs.createMemo(() => 1 / (zoomFactor / clientRect().height));
862
796
  const maxVerticalZoom = solidJs.createMemo(() => 10 * (zoomFactor / clientRect().height));
797
+ solidJs.createEffect(isInitial => {
798
+ if (isInitial) return false;
799
+ if (contextProps.mode === "tracks") {
800
+ contextProps.onVerticalPositionChange?.(0);
801
+ }
802
+ }, true);
863
803
  return web.createComponent(PianoRollContextProvider, {
864
804
  value: contextProps,
865
805
  get children() {
866
- const _el$ = _tmpl$.cloneNode(true),
806
+ const _el$ = _tmpl$2.cloneNode(true),
867
807
  _el$2 = _el$.firstChild;
868
808
  web.spread(_el$, web.mergeProps(divProps, {
869
809
  get ["class"]() {
870
- return styles$3.PianoRoll;
810
+ return styles$4.PianoRoll;
871
811
  }
872
812
  }), false, true);
873
813
  web.insert(_el$2, web.createComponent(ScrollZoomViewPort, {
@@ -885,17 +825,38 @@ const PianoRoll = allProps => {
885
825
  pixelOffset: clientRect().top,
886
826
  pixelSize: clientRect().height,
887
827
  position: contextProps.verticalPosition,
888
- range: 128,
828
+ range: contextProps.mode === "keys" ? 128 : Math.max(contextProps.tracks.length, 16),
889
829
  zoom: contextProps.verticalZoom * (zoomFactor / clientRect().height),
890
830
  onPositionChange: contextProps.onVerticalPositionChange,
891
831
  onZoomChange: verticalZoom => contextProps.onVerticalZoomChange?.(clamp(verticalZoom / (zoomFactor / clientRect().height), minVerticalZoom(), maxVerticalZoom()))
892
832
  })
893
833
  },
894
834
  get children() {
895
- return [web.createComponent(PianoRollKeys, {}), web.createComponent(ScrollContainer, {
835
+ return [(() => {
836
+ const _el$3 = _tmpl$$1.cloneNode(true);
837
+ _el$3.style.setProperty("display", "flex");
838
+ _el$3.style.setProperty("width", "20%");
839
+ web.insert(_el$3, web.createComponent(solidJs.Show, {
840
+ get when() {
841
+ return allProps.showTrackList;
842
+ },
843
+ get children() {
844
+ return web.createComponent(PianoRollTrackList, {});
845
+ }
846
+ }), null);
847
+ web.insert(_el$3, web.createComponent(solidJs.Show, {
848
+ get when() {
849
+ return contextProps.mode === "keys";
850
+ },
851
+ get children() {
852
+ return web.createComponent(PianoRollKeys, {});
853
+ }
854
+ }), null);
855
+ return _el$3;
856
+ })(), web.createComponent(ScrollContainer, {
896
857
  ref: setScrollerRef,
897
858
  get children() {
898
- return [web.createComponent(PianoRollGrid, {}), web.createComponent(PianoRollNotes, {})];
859
+ return [web.memo(() => allProps.children), web.createComponent(PianoRollGrid, {}), web.createComponent(PianoRollNotes, {})];
899
860
  }
900
861
  }), web.createComponent(VerticalZoomControl, {
901
862
  get min() {
@@ -915,19 +876,97 @@ const PianoRoll = allProps => {
915
876
  return maxZoom();
916
877
  }
917
878
  }), null);
918
- web.effect(() => web.className(_el$2, styles$3.PianoRollContainer));
879
+ web.effect(() => web.className(_el$2, styles$4.PianoRollContainer));
919
880
  return _el$;
920
881
  }
921
882
  });
922
883
  };
923
884
 
885
+ const _tmpl$ = /*#__PURE__*/web.template(`<div class="PlayHead"></div>`, 2);
886
+ const PlayHead = allProps => {
887
+ const propsWithDefauls = solidJs.mergeProps({
888
+ playHeadPosition: 0,
889
+ sync: false
890
+ }, allProps);
891
+ const [props, divProps] = solidJs.splitProps(propsWithDefauls, ["playHeadPosition", "sync", "onPlayHeadPositionChange", "onPositionChange"]);
892
+ const viewPort = useViewPortDimension("horizontal");
893
+ solidJs.createEffect(() => {
894
+ if (!props.sync) return;
895
+ const maxPosition = viewPort.range;
896
+ const newPosition = clamp(props.playHeadPosition - viewPort.range / viewPort.zoom / 2, 0, maxPosition);
897
+ props.onPositionChange?.(newPosition);
898
+ });
899
+ const leftPosition = solidJs.createMemo(() => viewPort.calculatePixelOffset(props.playHeadPosition));
900
+ return (() => {
901
+ const _el$ = _tmpl$.cloneNode(true);
902
+ web.spread(_el$, web.mergeProps(divProps, {
903
+ get style() {
904
+ return {
905
+ position: "absolute",
906
+ height: "100%",
907
+ width: "2px",
908
+ left: `${leftPosition()}px`,
909
+ "background-color": "green",
910
+ cursor: "pointer",
911
+ ...(typeof divProps.style === "object" && divProps.style)
912
+ };
913
+ },
914
+ "onMouseDown": event => {
915
+ event.preventDefault();
916
+ event.stopPropagation();
917
+ const handleMouseMove = ({
918
+ movementX
919
+ }) => {
920
+ const {
921
+ parentElement
922
+ } = event.currentTarget;
923
+ if (!parentElement) return;
924
+ const newPosition = viewPort.calculatePosition(leftPosition() + movementX);
925
+ props.onPlayHeadPositionChange?.(newPosition);
926
+ };
927
+ const handleMouseUp = () => {
928
+ window.removeEventListener("mousemove", handleMouseMove);
929
+ window.removeEventListener("mouseup", handleMouseUp);
930
+ };
931
+ window.addEventListener("mousemove", handleMouseMove);
932
+ window.addEventListener("mouseup", handleMouseUp);
933
+ }
934
+ }), false, false);
935
+ return _el$;
936
+ })();
937
+ };
938
+
939
+ const useNotes = () => {
940
+ const [notes, onNotesChange] = solidJs.createSignal([]);
941
+ const onNoteChange = (index, note) => {
942
+ onNotesChange([...notes().slice(0, index), note, ...notes().splice(index + 1)]);
943
+ };
944
+ const onInsertNote = note => {
945
+ const index = Math.max(notes().findIndex(({
946
+ ticks
947
+ }) => ticks > note.ticks), 0);
948
+ onNotesChange([...notes().slice(0, index), note, ...notes().splice(index)]);
949
+ return index;
950
+ };
951
+ const onRemoveNote = index => {
952
+ onNotesChange([...notes().slice(0, index), ...notes().splice(index + 1)]);
953
+ };
954
+ return {
955
+ notes,
956
+ onNotesChange,
957
+ onNoteChange,
958
+ onInsertNote,
959
+ onRemoveNote
960
+ };
961
+ };
962
+
924
963
  const usePianoRoll = () => {
925
964
  const [ppq, onPpqChange] = solidJs.createSignal(120);
926
- const [condensed, onCondensedChange] = solidJs.createSignal(false);
965
+ const [mode, onModeChange] = solidJs.createSignal("tracks");
927
966
  const [position, onPositionChange] = solidJs.createSignal(0);
928
967
  const [zoom, onZoomChange] = solidJs.createSignal(10);
929
968
  const [verticalZoom, onVerticalZoomChange] = solidJs.createSignal(5);
930
- const [verticalPosition, onVerticalPositionChange] = solidJs.createSignal(64);
969
+ const [verticalPosition, onVerticalPositionChange] = solidJs.createSignal(44);
931
970
  const [gridDivision, onGridDivisionChange] = solidJs.createSignal(4);
932
971
  const [snapToGrid, onSnapToGridChange] = solidJs.createSignal(true);
933
972
  const [duration, onDurationChange] = solidJs.createSignal(0);
@@ -946,15 +985,15 @@ const usePianoRoll = () => {
946
985
  onGridDivisionChange,
947
986
  snapToGrid,
948
987
  onSnapToGridChange,
949
- condensed,
950
- onCondensedChange,
988
+ mode,
989
+ onModeChange,
951
990
  duration,
952
991
  onDurationChange
953
992
  };
954
993
  };
955
994
 
956
- exports.MultiTrackPianoRoll = MultiTrackPianoRoll;
957
995
  exports.PianoRoll = PianoRoll;
996
+ exports.PlayHead = PlayHead;
958
997
  exports.useNotes = useNotes;
959
998
  exports.usePianoRoll = usePianoRoll;
960
999
  //# sourceMappingURL=index.js.map