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