solid-pianoroll 0.0.23 → 0.0.25

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.
Files changed (54) hide show
  1. package/dist/{cjs/index.js → dev.cjs} +446 -426
  2. package/dist/dev.css +212 -0
  3. package/dist/dev.js +1330 -0
  4. package/dist/dev.jsx +1130 -0
  5. package/dist/index.cjs +1330 -0
  6. package/dist/index.css +212 -0
  7. package/dist/index.d.ts +105 -0
  8. package/dist/{esm/index.js → index.js} +450 -425
  9. package/dist/index.jsx +1130 -0
  10. package/package.json +43 -34
  11. package/dist/cjs/index.js.map +0 -1
  12. package/dist/esm/index.js.map +0 -1
  13. package/dist/source/PianoRoll.jsx +0 -78
  14. package/dist/source/PianoRollContext.jsx +0 -11
  15. package/dist/source/PianoRollGrid.jsx +0 -64
  16. package/dist/source/PianoRollKeys.jsx +0 -72
  17. package/dist/source/PianoRollNotes.jsx +0 -162
  18. package/dist/source/PianoRollNotesScroller.jsx +0 -16
  19. package/dist/source/PianoRollScale.jsx +0 -29
  20. package/dist/source/PianoRollScrollZoomViewPort.jsx +0 -49
  21. package/dist/source/PianoRollTrackList.jsx +0 -27
  22. package/dist/source/PianoRollTrackListScroller.jsx +0 -16
  23. package/dist/source/index.jsx +0 -5
  24. package/dist/source/types.js +0 -1
  25. package/dist/source/useBoundingClientRect.js +0 -29
  26. package/dist/source/useNotes.js +0 -23
  27. package/dist/source/usePianoRollGrid.js +0 -45
  28. package/dist/source/usePianoRollState.js +0 -106
  29. package/dist/source/viewport/PlayHead.jsx +0 -43
  30. package/dist/source/viewport/ScrollZoomContainer.jsx +0 -104
  31. package/dist/source/viewport/ScrollZoomViewPort.jsx +0 -31
  32. package/dist/source/viewport/ZoomSliderControl.jsx +0 -29
  33. package/dist/source/viewport/createViewPortDimension.js +0 -51
  34. package/dist/types/PianoRoll.d.ts +0 -9
  35. package/dist/types/PianoRollContext.d.ts +0 -122
  36. package/dist/types/PianoRollGrid.d.ts +0 -2
  37. package/dist/types/PianoRollKeys.d.ts +0 -9
  38. package/dist/types/PianoRollNotes.d.ts +0 -5
  39. package/dist/types/PianoRollNotesScroller.d.ts +0 -3
  40. package/dist/types/PianoRollScale.d.ts +0 -2
  41. package/dist/types/PianoRollScrollZoomViewPort.d.ts +0 -3
  42. package/dist/types/PianoRollTrackList.d.ts +0 -2
  43. package/dist/types/PianoRollTrackListScroller.d.ts +0 -3
  44. package/dist/types/index.d.ts +0 -5
  45. package/dist/types/types.d.ts +0 -6
  46. package/dist/types/useBoundingClientRect.d.ts +0 -3
  47. package/dist/types/useNotes.d.ts +0 -9
  48. package/dist/types/usePianoRollGrid.d.ts +0 -13
  49. package/dist/types/usePianoRollState.d.ts +0 -63
  50. package/dist/types/viewport/PlayHead.d.ts +0 -8
  51. package/dist/types/viewport/ScrollZoomContainer.d.ts +0 -8
  52. package/dist/types/viewport/ScrollZoomViewPort.d.ts +0 -39
  53. package/dist/types/viewport/ZoomSliderControl.d.ts +0 -6
  54. package/dist/types/viewport/createViewPortDimension.d.ts +0 -40
package/dist/dev.js ADDED
@@ -0,0 +1,1330 @@
1
+ import { delegateEvents, createComponent, spread, mergeProps as mergeProps$1, insert, effect, setAttribute, memo, className, classList, template, use } from 'solid-js/web';
2
+ import { createContext, createEffect, createSignal, mergeProps, Show, splitProps, createMemo, useContext, Index, For } from 'solid-js';
3
+ import { createStore } from 'solid-js/store';
4
+
5
+ var __defProp = Object.defineProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+
8
+ // src/PianoRoll.module.css
9
+ var _default = {};
10
+ var defaultState = {
11
+ ppq: 0,
12
+ mode: "keys",
13
+ position: 0,
14
+ zoom: 10,
15
+ verticalZoom: 5,
16
+ verticalPosition: 44,
17
+ verticalTrackZoom: 0.5,
18
+ verticalTrackPosition: 0,
19
+ playHeadPosition: 0,
20
+ gridDivision: 4,
21
+ snapToGrid: true,
22
+ duration: 0,
23
+ tracks: [],
24
+ selectedTrackIndex: 0,
25
+ pressedKeys: {},
26
+ notesScrollerClientRect: { left: 0, width: 0, top: 0, height: 0 },
27
+ tracksScrollerClientRect: { left: 0, width: 0, top: 0, height: 0 }
28
+ };
29
+ var propNameToHandlerName = /* @__PURE__ */ __name((name) => `on${name[0]?.toUpperCase()}${name.slice(1)}Change`, "propNameToHandlerName");
30
+ var pianoRollStatePropNames = [
31
+ ...Object.keys(defaultState),
32
+ ...Object.keys(defaultState).map(propNameToHandlerName),
33
+ "onNoteChange",
34
+ "onInsertNote",
35
+ "onRemoveNote",
36
+ "onNoteDown",
37
+ "onNoteUp",
38
+ "isKeyDown",
39
+ "snapValueToGridIfEnabled"
40
+ ];
41
+ var createPianoRollstate = /* @__PURE__ */ __name((initialState) => {
42
+ const [state, setState] = createStore({
43
+ ...defaultState,
44
+ ...initialState
45
+ });
46
+ const handlers = Object.fromEntries(
47
+ Object.entries(state).map((entry) => {
48
+ const handlerName = propNameToHandlerName(entry[0]);
49
+ return [
50
+ handlerName,
51
+ (value, originalEvent) => {
52
+ const handler = initialState?.[handlerName];
53
+ setState(entry[0], value);
54
+ if (handler) {
55
+ handler(value, originalEvent);
56
+ }
57
+ }
58
+ ];
59
+ })
60
+ );
61
+ const onPlayheadPositionChange = /* @__PURE__ */ __name((playheadPosition, originalEvent) => {
62
+ handlers.onPlayHeadPositionChange(
63
+ snapValueToGridIfEnabled(playheadPosition, !!originalEvent?.altKey)
64
+ );
65
+ }, "onPlayheadPositionChange");
66
+ const updateNotes = /* @__PURE__ */ __name(async (trackIndex, getNotes) => {
67
+ const track = state.tracks[trackIndex];
68
+ if (!track)
69
+ return;
70
+ const notes = track.notes;
71
+ handlers.onTracksChange([
72
+ ...state.tracks.slice(0, trackIndex),
73
+ { ...track, notes: getNotes(notes) },
74
+ ...state.tracks.slice(trackIndex + 1)
75
+ ]);
76
+ }, "updateNotes");
77
+ const onNoteChange = /* @__PURE__ */ __name((trackIndex, noteIndex, note) => {
78
+ updateNotes(trackIndex, (notes) => [
79
+ ...notes.slice(0, noteIndex),
80
+ note,
81
+ ...notes.slice(noteIndex + 1)
82
+ ]);
83
+ }, "onNoteChange");
84
+ const onInsertNote = /* @__PURE__ */ __name((trackIndex, note) => {
85
+ const track = state.tracks[trackIndex];
86
+ if (!track)
87
+ return -1;
88
+ const notes = track.notes;
89
+ const newNoteIndex = Math.max(
90
+ notes.findIndex(({ ticks }) => ticks > note.ticks),
91
+ 0
92
+ );
93
+ updateNotes(trackIndex, (notes2) => [
94
+ ...notes2.slice(0, newNoteIndex),
95
+ note,
96
+ ...notes2.slice(newNoteIndex)
97
+ ]);
98
+ return newNoteIndex;
99
+ }, "onInsertNote");
100
+ const onRemoveNote = /* @__PURE__ */ __name((trackIndex, noteIndex) => {
101
+ updateNotes(trackIndex, (notes) => [
102
+ ...notes.slice(0, noteIndex),
103
+ ...notes.slice(noteIndex + 1)
104
+ ]);
105
+ }, "onRemoveNote");
106
+ const updateKeyPressedState = /* @__PURE__ */ __name((trackIndex, keyNumber, value) => {
107
+ handlers.onPressedKeysChange?.({
108
+ ...state.pressedKeys,
109
+ [trackIndex]: {
110
+ ...state.pressedKeys[trackIndex],
111
+ [keyNumber]: value
112
+ }
113
+ });
114
+ }, "updateKeyPressedState");
115
+ const onNoteDown = /* @__PURE__ */ __name((trackIndex, keyNumber) => {
116
+ updateKeyPressedState(trackIndex, keyNumber, true);
117
+ }, "onNoteDown");
118
+ const onNoteUp = /* @__PURE__ */ __name((trackIndex, keyNumber) => {
119
+ updateKeyPressedState(trackIndex, keyNumber, false);
120
+ }, "onNoteUp");
121
+ const isKeyDown = /* @__PURE__ */ __name((trackIndex, keyNumber) => !!state.pressedKeys[trackIndex]?.[keyNumber], "isKeyDown");
122
+ const snapValueToGridIfEnabled = /* @__PURE__ */ __name((value, altKey) => {
123
+ const gridDivisionTicks = state.ppq * 4 / state.gridDivision;
124
+ return state.snapToGrid && !altKey ? Math.round(value / gridDivisionTicks) * gridDivisionTicks : value;
125
+ }, "snapValueToGridIfEnabled");
126
+ return mergeProps(state, {
127
+ ...handlers,
128
+ onNoteChange,
129
+ onInsertNote,
130
+ onRemoveNote,
131
+ onNoteDown,
132
+ onNoteUp,
133
+ isKeyDown,
134
+ snapValueToGridIfEnabled,
135
+ onPlayheadPositionChange
136
+ });
137
+ }, "createPianoRollstate");
138
+ var usePianoRollState_default = createPianoRollstate;
139
+
140
+ // src/PianoRollContext.tsx
141
+ var PianoRollContext = createContext();
142
+ var PianoRollContextProvider = PianoRollContext.Provider;
143
+ var usePianoRollContext = /* @__PURE__ */ __name(() => {
144
+ const context = useContext(PianoRollContext);
145
+ if (!context)
146
+ throw new Error("No PianoRollContext found");
147
+ return context;
148
+ }, "usePianoRollContext");
149
+ var splitContextProps = /* @__PURE__ */ __name((allProps) => splitProps(allProps, [...pianoRollStatePropNames, "showAllTracks", "showTrackList"]), "splitContextProps");
150
+
151
+ // src/PianoRollKeys.module.css
152
+ var _default2 = {};
153
+ function createViewPortDimension(getState) {
154
+ const getStateWithFunctions = /* @__PURE__ */ __name(() => {
155
+ const state2 = getState();
156
+ const onZoomChange = /* @__PURE__ */ __name((zoom) => state2.onZoomChange?.(clamp(zoom, state2.minZoom, state2.maxZoom)), "onZoomChange");
157
+ const onPositionChange = /* @__PURE__ */ __name((position) => state2.onPositionChange?.(position), "onPositionChange");
158
+ return {
159
+ ...state2,
160
+ onZoomChange,
161
+ onPositionChange,
162
+ calculatePixelOffset,
163
+ calculatePixelValue,
164
+ calculatePosition,
165
+ calculatePixelDimensions,
166
+ calculateVisibleRange,
167
+ calculateMaxPosition,
168
+ isVisible
169
+ };
170
+ }, "getStateWithFunctions");
171
+ const [state, setState] = createStore(getStateWithFunctions());
172
+ createEffect(() => setState(getStateWithFunctions()));
173
+ function calculatePixelValue(position = state.position) {
174
+ const virtualSize = state.pixelSize * state.zoom;
175
+ return position / state.range * virtualSize;
176
+ }
177
+ __name(calculatePixelValue, "calculatePixelValue");
178
+ function calculatePixelOffset(position) {
179
+ return calculatePixelValue(position) - calculatePixelValue(state.position);
180
+ }
181
+ __name(calculatePixelOffset, "calculatePixelOffset");
182
+ function calculatePosition(offset) {
183
+ const percentX = (offset - state.pixelOffset) / state.pixelSize;
184
+ const position = state.position + percentX * calculateVisibleRange();
185
+ return position;
186
+ }
187
+ __name(calculatePosition, "calculatePosition");
188
+ function calculateVisibleRange() {
189
+ return state.range / state.zoom;
190
+ }
191
+ __name(calculateVisibleRange, "calculateVisibleRange");
192
+ function calculateMaxPosition() {
193
+ return state.range - state.range / state.zoom;
194
+ }
195
+ __name(calculateMaxPosition, "calculateMaxPosition");
196
+ function calculatePixelDimensions(position, length) {
197
+ const offset = calculatePixelOffset(position);
198
+ const size = calculatePixelValue(length);
199
+ return { offset, size };
200
+ }
201
+ __name(calculatePixelDimensions, "calculatePixelDimensions");
202
+ function isVisible({ offset, size }) {
203
+ return offset + size > 0 && offset < state.pixelSize;
204
+ }
205
+ __name(isVisible, "isVisible");
206
+ return state;
207
+ }
208
+ __name(createViewPortDimension, "createViewPortDimension");
209
+ var clamp = /* @__PURE__ */ __name((value, min, max) => Math.max(min, Math.min(max, value)), "clamp");
210
+
211
+ // src/viewport/ScrollZoomViewPort.tsx
212
+ var defaultViewPortProps = {
213
+ pixelOffset: 0,
214
+ position: 0,
215
+ range: 1,
216
+ zoom: 1,
217
+ minZoom: 1,
218
+ maxZoom: Infinity
219
+ };
220
+ var ScrollZoomViewPort = /* @__PURE__ */ __name((props) => {
221
+ const viewPorts = Object.entries(props.dimensions).map(([name, viewPortProps]) => createViewPortDimension(() => ({
222
+ name,
223
+ ...defaultViewPortProps,
224
+ ...viewPortProps()
225
+ })));
226
+ const parentViewPorts = useContext(ScrollZoomViewPortContext);
227
+ return createComponent(ScrollZoomViewPortContext.Provider, {
228
+ get value() {
229
+ return [...parentViewPorts, ...viewPorts];
230
+ },
231
+ get children() {
232
+ return props.children;
233
+ }
234
+ });
235
+ }, "ScrollZoomViewPort");
236
+ var ScrollZoomViewPortContext = createContext([]);
237
+ var useViewPortDimension = /* @__PURE__ */ __name((name) => {
238
+ const viewPorts = useContext(ScrollZoomViewPortContext);
239
+ const viewPort = name ? viewPorts.find((viewPort2) => viewPort2.name === name) : viewPorts[viewPorts.length - 1];
240
+ if (!viewPort)
241
+ throw new Error(`ViewPort dimension "${name}" not found.`);
242
+ return viewPort;
243
+ }, "useViewPortDimension");
244
+
245
+ // src/viewport/ScrollZoomContainer.module.css
246
+ var _default3 = {};
247
+
248
+ // src/viewport/ScrollZoomContainer.tsx
249
+ var _tmpl$ = /* @__PURE__ */ template(`<div><div><div><div></div></div></div></div>`, 8);
250
+ var ScrollZoomContainer = /* @__PURE__ */ __name((props) => {
251
+ let scrollContentRef;
252
+ const [ownProps, divProps] = splitProps(props, ["ref", "verticalDimensionName", "horizontalDimensionName", "showScrollbar"]);
253
+ const propsWithDefaults = mergeProps({
254
+ verticalDimensionName: "vertical",
255
+ horizontalDimensionName: "horizontal",
256
+ showScrollbar: true
257
+ }, ownProps);
258
+ const verticalViewPort = createMemo(() => useViewPortDimension(propsWithDefaults.verticalDimensionName));
259
+ const horizontalViewPort = createMemo(() => useViewPortDimension(propsWithDefaults.horizontalDimensionName));
260
+ const handleScroll = /* @__PURE__ */ __name((event) => {
261
+ event.preventDefault();
262
+ if (didUpdateScroll) {
263
+ didUpdateScroll = false;
264
+ return;
265
+ }
266
+ const maxVerticalPosition = verticalViewPort().calculateMaxPosition();
267
+ const maxPosition = horizontalViewPort().calculateMaxPosition();
268
+ const height = verticalViewPort().pixelSize;
269
+ const width = horizontalViewPort().pixelSize;
270
+ const {
271
+ scrollTop,
272
+ scrollLeft,
273
+ scrollWidth,
274
+ scrollHeight
275
+ } = event.currentTarget;
276
+ const scrollTopAmount = scrollTop / (scrollHeight - height);
277
+ const scrollLeftAmount = scrollLeft / (scrollWidth - width);
278
+ verticalViewPort().onPositionChange?.(maxVerticalPosition * scrollTopAmount);
279
+ horizontalViewPort().onPositionChange?.(maxPosition * scrollLeftAmount);
280
+ }, "handleScroll");
281
+ const handleWheel = /* @__PURE__ */ __name((event) => {
282
+ if (event.altKey) {
283
+ event.preventDefault();
284
+ if (Math.abs(event.deltaX) > Math.abs(event.deltaY)) {
285
+ horizontalViewPort()?.onZoomChange?.(horizontalViewPort().zoom / (1 + event.deltaX / horizontalViewPort().pixelSize));
286
+ const maxPosition = horizontalViewPort().calculateMaxPosition();
287
+ horizontalViewPort()?.onPositionChange?.(Math.min(maxPosition, horizontalViewPort()?.position));
288
+ } else {
289
+ verticalViewPort()?.onZoomChange?.(verticalViewPort().zoom / (1 + event.deltaY / verticalViewPort().pixelSize));
290
+ const maxVerticalPosition = verticalViewPort().calculateMaxPosition();
291
+ verticalViewPort()?.onPositionChange?.(Math.min(maxVerticalPosition, verticalViewPort()?.position));
292
+ }
293
+ } else if (!props.showScrollbar) {
294
+ event.preventDefault();
295
+ event.currentTarget.scrollLeft += event.deltaX;
296
+ event.currentTarget.scrollTop += event.deltaY;
297
+ }
298
+ }, "handleWheel");
299
+ let didUpdateScroll = false;
300
+ createEffect(() => {
301
+ const maxVerticalPosition = verticalViewPort().calculateMaxPosition();
302
+ const maxPosition = horizontalViewPort().calculateMaxPosition();
303
+ const scrollTopAmount = maxVerticalPosition > 0 ? verticalViewPort().position / maxVerticalPosition : 0;
304
+ const scrollLeftAmount = maxPosition > 0 ? horizontalViewPort().position / maxPosition : 0;
305
+ const height = verticalViewPort().pixelSize;
306
+ const width = horizontalViewPort().pixelSize;
307
+ if (!scrollContentRef?.parentElement)
308
+ return;
309
+ const scrollDivHeight = verticalViewPort().zoom * verticalViewPort().pixelSize;
310
+ const scrollTop = scrollTopAmount * (scrollDivHeight - height);
311
+ const scrollDivWidth = horizontalViewPort().zoom * horizontalViewPort().pixelSize;
312
+ const scrollLeft = scrollLeftAmount * (scrollDivWidth - width);
313
+ didUpdateScroll = true;
314
+ scrollContentRef.style.height = `${scrollDivHeight}px`;
315
+ scrollContentRef.style.width = `${scrollDivWidth}px`;
316
+ scrollContentRef.parentElement.scrollTo({
317
+ left: scrollLeft,
318
+ top: scrollTop
319
+ });
320
+ });
321
+ return (() => {
322
+ const _el$ = _tmpl$.cloneNode(true), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$4 = _el$3.firstChild;
323
+ _el$.addEventListener("wheel", handleWheel);
324
+ _el$.addEventListener("scroll", handleScroll);
325
+ const _ref$ = props.ref;
326
+ typeof _ref$ === "function" ? use(_ref$, _el$) : props.ref = _el$;
327
+ spread(_el$, mergeProps$1(divProps, {
328
+ get style() {
329
+ return {
330
+ overflow: propsWithDefaults.showScrollbar ? "scroll" : "hidden",
331
+ ...typeof divProps.style === "object" && divProps.style
332
+ };
333
+ },
334
+ get classList() {
335
+ return {
336
+ [_default3.ScrollZoomContainer]: true,
337
+ ...divProps.classList
338
+ };
339
+ }
340
+ }), false, true);
341
+ const _ref$2 = scrollContentRef;
342
+ typeof _ref$2 === "function" ? use(_ref$2, _el$2) : scrollContentRef = _el$2;
343
+ _el$3.style.setProperty("top", "0");
344
+ _el$3.style.setProperty("left", "0");
345
+ _el$3.style.setProperty("position", "sticky");
346
+ _el$3.style.setProperty("display", "flex");
347
+ _el$4.style.setProperty("position", "relative");
348
+ insert(_el$4, () => props.children);
349
+ effect((_p$) => {
350
+ const _v$ = `${verticalViewPort().pixelSize}px`, _v$2 = `${horizontalViewPort().pixelSize}px`, _v$3 = `${verticalViewPort().pixelSize}px`, _v$4 = `${horizontalViewPort().pixelSize}px`;
351
+ _v$ !== _p$._v$ && _el$3.style.setProperty("height", _p$._v$ = _v$);
352
+ _v$2 !== _p$._v$2 && _el$3.style.setProperty("width", _p$._v$2 = _v$2);
353
+ _v$3 !== _p$._v$3 && _el$4.style.setProperty("height", _p$._v$3 = _v$3);
354
+ _v$4 !== _p$._v$4 && _el$4.style.setProperty("width", _p$._v$4 = _v$4);
355
+ return _p$;
356
+ }, {
357
+ _v$: void 0,
358
+ _v$2: void 0,
359
+ _v$3: void 0,
360
+ _v$4: void 0
361
+ });
362
+ return _el$;
363
+ })();
364
+ }, "ScrollZoomContainer");
365
+ var ScrollZoomContainer_default = ScrollZoomContainer;
366
+
367
+ // src/PianoRollKeys.tsx
368
+ var _tmpl$2 = /* @__PURE__ */ template(`<div></div>`, 2);
369
+ var PianoRollKeys = /* @__PURE__ */ __name(() => {
370
+ const verticalViewPort = createMemo(() => useViewPortDimension("vertical"));
371
+ const horizontalViewPort = createMemo(() => useViewPortDimension("horizontalKeys"));
372
+ const [isMouseDown, setIsMouseDown] = createSignal(false);
373
+ const context = usePianoRollContext();
374
+ const handleMouseUp = /* @__PURE__ */ __name(() => {
375
+ setIsMouseDown(false);
376
+ }, "handleMouseUp");
377
+ createEffect(() => {
378
+ if (isMouseDown()) {
379
+ window.addEventListener("mouseup", handleMouseUp);
380
+ } else {
381
+ window.removeEventListener("mouseup", handleMouseUp);
382
+ }
383
+ });
384
+ return (() => {
385
+ const _el$ = _tmpl$2.cloneNode(true);
386
+ insert(_el$, createComponent(ScrollZoomContainer_default, {
387
+ horizontalDimensionName: "horizontalKeys",
388
+ showScrollbar: false,
389
+ get children() {
390
+ return createComponent(Index, {
391
+ each: keys,
392
+ children: (key) => {
393
+ const isDown = createMemo(() => context.isKeyDown(context.selectedTrackIndex, key().number));
394
+ const virtualDimensions = createMemo(() => verticalViewPort().calculatePixelDimensions(127 - key().number, 1));
395
+ const previousIsBlack = blackKeys.includes((key().number - 1) % 12);
396
+ const nextIsBlack = blackKeys.includes((key().number + 1) % 12);
397
+ return createComponent(Show, {
398
+ get when() {
399
+ return verticalViewPort().isVisible(virtualDimensions());
400
+ },
401
+ get children() {
402
+ const _el$2 = _tmpl$2.cloneNode(true);
403
+ _el$2.addEventListener("mouseleave", () => {
404
+ if (isMouseDown()) {
405
+ context.onNoteUp(context.selectedTrackIndex, key().number);
406
+ }
407
+ });
408
+ _el$2.addEventListener("mouseenter", () => {
409
+ if (isMouseDown()) {
410
+ context.onNoteDown(context.selectedTrackIndex, key().number);
411
+ }
412
+ });
413
+ _el$2.$$mouseup = () => {
414
+ context.onNoteUp(context.selectedTrackIndex, key().number);
415
+ };
416
+ _el$2.$$mousedown = () => {
417
+ setIsMouseDown(true);
418
+ context.onNoteDown(context.selectedTrackIndex, key().number);
419
+ };
420
+ effect((_p$) => {
421
+ const _v$3 = {
422
+ [_default2["Key"]]: true,
423
+ [_default2["black"]]: key().isBlack,
424
+ [_default2["white"]]: !key().isBlack,
425
+ [_default2["down"]]: isDown()
426
+ }, _v$4 = key().name, _v$5 = key().number % 12, _v$6 = `${virtualDimensions().offset - virtualDimensions().size * (!key().isBlack ? nextIsBlack ? 1 / 2 : 0 : 0)}px`, _v$7 = `${virtualDimensions().size + (!key().isBlack && nextIsBlack ? virtualDimensions().size / 2 : 0) + (!key().isBlack && previousIsBlack ? virtualDimensions().size / 2 : 0)}px`, _v$8 = `0px 0px ${Math.min(virtualDimensions().size / 20, 1)}px ${Math.min(virtualDimensions().size / 50, 1)}px rgba(0, 0, 0, 0.5) ${key().isBlack ? "" : "inset"}` + isDown() ? `0px 0px ${Math.min(virtualDimensions().size / 8, 2)}px ${Math.min(virtualDimensions().size / 20, 2)}px rgba(0, 0, 0, 0.5) inset` : "";
427
+ _p$._v$3 = classList(_el$2, _v$3, _p$._v$3);
428
+ _v$4 !== _p$._v$4 && setAttribute(_el$2, "title", _p$._v$4 = _v$4);
429
+ _v$5 !== _p$._v$5 && setAttribute(_el$2, "data-index", _p$._v$5 = _v$5);
430
+ _v$6 !== _p$._v$6 && _el$2.style.setProperty("top", _p$._v$6 = _v$6);
431
+ _v$7 !== _p$._v$7 && _el$2.style.setProperty("height", _p$._v$7 = _v$7);
432
+ _v$8 !== _p$._v$8 && _el$2.style.setProperty("box-shadow", _p$._v$8 = _v$8);
433
+ return _p$;
434
+ }, {
435
+ _v$3: void 0,
436
+ _v$4: void 0,
437
+ _v$5: void 0,
438
+ _v$6: void 0,
439
+ _v$7: void 0,
440
+ _v$8: void 0
441
+ });
442
+ return _el$2;
443
+ }
444
+ });
445
+ }
446
+ });
447
+ }
448
+ }));
449
+ effect((_p$) => {
450
+ const _v$ = _default2.PianoRollKeys, _v$2 = `${horizontalViewPort().pixelSize}px`;
451
+ _v$ !== _p$._v$ && className(_el$, _p$._v$ = _v$);
452
+ _v$2 !== _p$._v$2 && _el$.style.setProperty("width", _p$._v$2 = _v$2);
453
+ return _p$;
454
+ }, {
455
+ _v$: void 0,
456
+ _v$2: void 0
457
+ });
458
+ return _el$;
459
+ })();
460
+ }, "PianoRollKeys");
461
+ var blackKeys = [1, 3, 6, 8, 10];
462
+ var keyNames = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
463
+ var keys = Array.from({
464
+ length: 128
465
+ }).map((_, index) => ({
466
+ number: index,
467
+ name: `${keyNames[index % 12]} ${Math.floor(index / 12) - 2}`,
468
+ isBlack: blackKeys.includes(index % 12)
469
+ }));
470
+ var PianoRollKeys_default = PianoRollKeys;
471
+ delegateEvents(["mousedown", "mouseup"]);
472
+
473
+ // src/PianoRollNotes.module.css
474
+ var _default4 = {};
475
+
476
+ // src/PianoRollNotes.tsx
477
+ var _tmpl$3 = /* @__PURE__ */ template(`<div></div>`, 2);
478
+ var PianoRollNotes = /* @__PURE__ */ __name((props) => {
479
+ const context = usePianoRollContext();
480
+ const verticalViewPort = createMemo(() => useViewPortDimension(context.mode === "keys" ? "vertical" : "verticalTracks"));
481
+ const horizontalViewPort = createMemo(() => useViewPortDimension("horizontal"));
482
+ const gridDivisionTicks = createMemo(() => context.ppq * 4 / context.gridDivision);
483
+ const [isDragging, setIsDragging] = createSignal(false);
484
+ const [noteDragMode, setNoteDragMode] = createSignal();
485
+ const [currentNoteIndex, setCurrentNoteIndex] = createSignal(-1);
486
+ const [currentNoteTrackIndex, setCurrentNoteTrackIndex] = createSignal(-1);
487
+ const [diffPosition, setDiffPosition] = createSignal(0);
488
+ const [getInitialNote, setInitialNote] = createSignal();
489
+ const calculateNoteDragValues = /* @__PURE__ */ __name((event) => {
490
+ const targetTrackIndex = context.mode === "tracks" ? clamp(Math.floor(verticalViewPort().calculatePosition(event.clientY)), 0, context.tracks.length - 1) : currentNoteTrackIndex() > -1 ? currentNoteTrackIndex() : context.selectedTrackIndex;
491
+ const midi = context.mode === "keys" ? Math.floor(128 - verticalViewPort().calculatePosition(event.clientY)) : getInitialNote()?.midi ?? 60;
492
+ const horiontalPosition = horizontalViewPort().calculatePosition(event.clientX);
493
+ return {
494
+ targetTrackIndex,
495
+ midi,
496
+ horiontalPosition
497
+ };
498
+ }, "calculateNoteDragValues");
499
+ const handleMouseMove = /* @__PURE__ */ __name((mouseMoveEvent) => {
500
+ mouseMoveEvent.preventDefault();
501
+ mouseMoveEvent.stopPropagation();
502
+ const note = getInitialNote();
503
+ if (!note)
504
+ return;
505
+ const {
506
+ targetTrackIndex,
507
+ midi,
508
+ horiontalPosition
509
+ } = calculateNoteDragValues(mouseMoveEvent);
510
+ const ticks = context.snapValueToGridIfEnabled(horiontalPosition - diffPosition(), mouseMoveEvent.altKey);
511
+ const updatedNote = {
512
+ ...note,
513
+ midi,
514
+ ...noteDragMode() === "move" && {
515
+ ticks
516
+ },
517
+ ...noteDragMode() === "trimStart" && {
518
+ ticks: ticks < note.ticks + note.durationTicks ? ticks : note.ticks + note.durationTicks,
519
+ durationTicks: ticks < note.ticks + note.durationTicks ? note.ticks + note.durationTicks - ticks : context.snapValueToGridIfEnabled(horiontalPosition - note.ticks, mouseMoveEvent.altKey)
520
+ },
521
+ ...noteDragMode() === "trimEnd" && {
522
+ ticks: ticks < note.ticks ? ticks : note.ticks,
523
+ durationTicks: ticks < note.ticks ? note.ticks - ticks : context.snapValueToGridIfEnabled(horiontalPosition - note.ticks, mouseMoveEvent.altKey)
524
+ }
525
+ };
526
+ const previousTrackIndex = currentNoteTrackIndex();
527
+ const previousNoteIndex = currentNoteIndex();
528
+ if (targetTrackIndex === previousTrackIndex) {
529
+ context.onNoteChange?.(previousTrackIndex, previousNoteIndex, updatedNote);
530
+ } else {
531
+ if (context.onInsertNote) {
532
+ context.onRemoveNote?.(previousTrackIndex, previousNoteIndex);
533
+ const newNoteIndex = context.onInsertNote(targetTrackIndex, updatedNote);
534
+ setCurrentNoteTrackIndex(targetTrackIndex);
535
+ setCurrentNoteIndex(newNoteIndex);
536
+ }
537
+ }
538
+ }, "handleMouseMove");
539
+ const stopDragging = /* @__PURE__ */ __name(() => {
540
+ window.removeEventListener("mousemove", handleMouseMove);
541
+ window.removeEventListener("mouseup", stopDragging);
542
+ setIsDragging(false);
543
+ setCurrentNoteIndex(-1);
544
+ setCurrentNoteTrackIndex(-1);
545
+ }, "stopDragging");
546
+ const startDragging = /* @__PURE__ */ __name(() => {
547
+ window.addEventListener("mousemove", handleMouseMove);
548
+ window.addEventListener("mouseup", stopDragging);
549
+ }, "startDragging");
550
+ const getClasses = /* @__PURE__ */ __name((noteDragMode2) => {
551
+ return noteDragMode2 ? [_default4.Note, _default4[noteDragMode2]] : [_default4.Note];
552
+ }, "getClasses");
553
+ return (() => {
554
+ const _el$ = _tmpl$3.cloneNode(true);
555
+ _el$.$$mousedown = (mouseDownEvent) => {
556
+ mouseDownEvent.preventDefault();
557
+ mouseDownEvent.stopPropagation();
558
+ const {
559
+ targetTrackIndex,
560
+ midi,
561
+ horiontalPosition
562
+ } = calculateNoteDragValues(mouseDownEvent);
563
+ const ticks = context.snapValueToGridIfEnabled(horiontalPosition, mouseDownEvent.altKey, context);
564
+ const durationTicks = gridDivisionTicks();
565
+ const newNote = {
566
+ midi,
567
+ ticks,
568
+ durationTicks,
569
+ velocity: 100
570
+ };
571
+ const newNoteIndex = context.onInsertNote(targetTrackIndex, newNote);
572
+ setIsDragging(true);
573
+ setCurrentNoteTrackIndex(targetTrackIndex);
574
+ setCurrentNoteIndex(newNoteIndex);
575
+ setInitialNote(newNote);
576
+ setDiffPosition(0);
577
+ setNoteDragMode("trimEnd");
578
+ startDragging();
579
+ };
580
+ const _ref$ = props.ref;
581
+ typeof _ref$ === "function" ? use(_ref$, _el$) : props.ref = _el$;
582
+ insert(_el$, createComponent(For, {
583
+ get each() {
584
+ return context.tracks;
585
+ },
586
+ children: (track, trackIndex) => {
587
+ return createComponent(Show, {
588
+ get when() {
589
+ return trackIndex() === context.selectedTrackIndex || context.showAllTracks || context.mode === "tracks";
590
+ },
591
+ get children() {
592
+ return createComponent(For, {
593
+ get each() {
594
+ return track.notes;
595
+ },
596
+ children: (note, noteIndex) => {
597
+ const verticalVirtualDimensions = createMemo(() => verticalViewPort().calculatePixelDimensions(context.mode === "keys" ? 127 - note.midi : trackIndex(), 1));
598
+ const horizontalDimensions = createMemo(() => horizontalViewPort().calculatePixelDimensions(note.ticks, note.durationTicks));
599
+ return createComponent(Show, {
600
+ get when() {
601
+ return memo(() => !!verticalViewPort().isVisible(verticalVirtualDimensions()))() && horizontalViewPort().isVisible(horizontalDimensions());
602
+ },
603
+ get children() {
604
+ const _el$2 = _tmpl$3.cloneNode(true);
605
+ _el$2.$$mousedown = (event) => {
606
+ event.stopPropagation();
607
+ const initialPosition = horizontalViewPort().calculatePosition(event.clientX);
608
+ setDiffPosition(noteDragMode() === "trimEnd" ? -(note.ticks + note.durationTicks - initialPosition) : initialPosition - note.ticks);
609
+ setIsDragging(true);
610
+ setCurrentNoteIndex(noteIndex());
611
+ setCurrentNoteTrackIndex(trackIndex());
612
+ setInitialNote(note);
613
+ startDragging();
614
+ };
615
+ _el$2.$$dblclick = (event) => {
616
+ event.stopPropagation();
617
+ context.onRemoveNote?.(trackIndex(), noteIndex());
618
+ };
619
+ _el$2.$$mousemove = (event) => {
620
+ if (isDragging())
621
+ return;
622
+ event.stopPropagation();
623
+ const relativeX = horizontalViewPort().calculatePixelValue(horizontalViewPort().calculatePosition(event.clientX));
624
+ const noteStartX = horizontalViewPort().calculatePixelValue(note.ticks);
625
+ const noteEndX = horizontalViewPort().calculatePixelValue(note.ticks + note.durationTicks);
626
+ setNoteDragMode(relativeX - noteStartX < 3 ? "trimStart" : noteEndX - relativeX < 3 ? "trimEnd" : "move");
627
+ };
628
+ setAttribute(_el$2, "draggable", false);
629
+ effect((_p$) => {
630
+ const _v$ = getClasses(noteDragMode()).join(" "), _v$2 = `${track.color}`, _v$3 = (note.velocity / 128 + 2) / 3, _v$4 = `${verticalVirtualDimensions().offset}px`, _v$5 = `${verticalVirtualDimensions().size}px`, _v$6 = `${horizontalDimensions().offset}px`, _v$7 = `${horizontalDimensions().size}px`;
631
+ _v$ !== _p$._v$ && className(_el$2, _p$._v$ = _v$);
632
+ _v$2 !== _p$._v$2 && _el$2.style.setProperty("background-color", _p$._v$2 = _v$2);
633
+ _v$3 !== _p$._v$3 && _el$2.style.setProperty("opacity", _p$._v$3 = _v$3);
634
+ _v$4 !== _p$._v$4 && _el$2.style.setProperty("top", _p$._v$4 = _v$4);
635
+ _v$5 !== _p$._v$5 && _el$2.style.setProperty("height", _p$._v$5 = _v$5);
636
+ _v$6 !== _p$._v$6 && _el$2.style.setProperty("left", _p$._v$6 = _v$6);
637
+ _v$7 !== _p$._v$7 && _el$2.style.setProperty("width", _p$._v$7 = _v$7);
638
+ return _p$;
639
+ }, {
640
+ _v$: void 0,
641
+ _v$2: void 0,
642
+ _v$3: void 0,
643
+ _v$4: void 0,
644
+ _v$5: void 0,
645
+ _v$6: void 0,
646
+ _v$7: void 0
647
+ });
648
+ return _el$2;
649
+ }
650
+ });
651
+ }
652
+ });
653
+ }
654
+ });
655
+ }
656
+ }));
657
+ effect((_$p) => classList(_el$, {
658
+ [_default4.PianoRollNotes]: true
659
+ }, _$p));
660
+ return _el$;
661
+ })();
662
+ }, "PianoRollNotes");
663
+ var PianoRollNotes_default = PianoRollNotes;
664
+ delegateEvents(["mousedown", "mousemove", "dblclick"]);
665
+
666
+ // src/PianoRollGrid.module.css
667
+ var _default5 = {};
668
+ var usePianoRollGrid = /* @__PURE__ */ __name(() => {
669
+ const context = usePianoRollContext();
670
+ const horizontalViewPort = createMemo(() => useViewPortDimension("horizontal"));
671
+ const measureTicks = createMemo(() => context.ppq * 4);
672
+ const selectedGridDivisorTicks = createMemo(() => measureTicks() / context.gridDivision);
673
+ function calculateVisibleGridDivisorTicks(value) {
674
+ const visibleRange = horizontalViewPort().calculateVisibleRange();
675
+ if (visibleRange <= 0)
676
+ return 0;
677
+ if (visibleRange / value > 100) {
678
+ return calculateVisibleGridDivisorTicks(value * 2);
679
+ }
680
+ if (visibleRange / value < 30) {
681
+ return calculateVisibleGridDivisorTicks(value / 2);
682
+ }
683
+ return value;
684
+ }
685
+ __name(calculateVisibleGridDivisorTicks, "calculateVisibleGridDivisorTicks");
686
+ const gridDivisorTicks = createMemo(
687
+ () => calculateVisibleGridDivisorTicks(selectedGridDivisorTicks())
688
+ );
689
+ const gridArray = createMemo(() => {
690
+ const numberOfLines = Math.ceil(
691
+ horizontalViewPort().calculateVisibleRange() / gridDivisorTicks() + 1
692
+ );
693
+ const startIndex = Math.floor(context.position / gridDivisorTicks());
694
+ return Array.from({ length: numberOfLines }).map((_, i) => {
695
+ const index = i + startIndex;
696
+ const ticks = index * gridDivisorTicks();
697
+ const measurePosition = ticks / context.ppq / 4 + 1;
698
+ const bars = Math.floor(measurePosition);
699
+ const beats = Math.floor((measurePosition - bars) * 4);
700
+ const label = `${bars}${beats ? `.${beats}` : ""}`;
701
+ return {
702
+ index,
703
+ ticks,
704
+ isHighlighted: Math.ceil((index + 1) * selectedGridDivisorTicks() / measureTicks()) % 2 === 0,
705
+ hasHighlightedBorder: index * gridDivisorTicks() % selectedGridDivisorTicks() === 0,
706
+ showLabel: index * selectedGridDivisorTicks() / measureTicks() % 2 === 0,
707
+ virtualDimensions: horizontalViewPort().calculatePixelDimensions(ticks, gridDivisorTicks()),
708
+ label
709
+ };
710
+ });
711
+ });
712
+ return gridArray;
713
+ }, "usePianoRollGrid");
714
+ var usePianoRollGrid_default = usePianoRollGrid;
715
+
716
+ // src/PianoRollGrid.tsx
717
+ var _tmpl$4 = /* @__PURE__ */ template(`<div></div>`, 2);
718
+ var PianoRollGrid = /* @__PURE__ */ __name(() => {
719
+ const context = usePianoRollContext();
720
+ const verticalViewPort = createMemo(() => useViewPortDimension(context.mode === "keys" ? "vertical" : "verticalTracks"));
721
+ const horizontalViewPort = createMemo(() => useViewPortDimension("horizontal"));
722
+ const gridArray = usePianoRollGrid_default();
723
+ return (() => {
724
+ const _el$ = _tmpl$4.cloneNode(true);
725
+ insert(_el$, createComponent(Index, {
726
+ get each() {
727
+ return gridArray();
728
+ },
729
+ children: (entry) => {
730
+ return createComponent(Show, {
731
+ get when() {
732
+ return horizontalViewPort().isVisible(entry().virtualDimensions);
733
+ },
734
+ get children() {
735
+ const _el$2 = _tmpl$4.cloneNode(true);
736
+ effect((_p$) => {
737
+ const _v$ = {
738
+ [_default5["PianoRollGrid-Time"]]: true,
739
+ [_default5["Highlighted"]]: entry().isHighlighted,
740
+ [_default5["HighlightedBorder"]]: entry().hasHighlightedBorder
741
+ }, _v$2 = `${entry().virtualDimensions.offset}px`, _v$3 = `${entry().virtualDimensions.size}px`;
742
+ _p$._v$ = classList(_el$2, _v$, _p$._v$);
743
+ _v$2 !== _p$._v$2 && _el$2.style.setProperty("left", _p$._v$2 = _v$2);
744
+ _v$3 !== _p$._v$3 && _el$2.style.setProperty("width", _p$._v$3 = _v$3);
745
+ return _p$;
746
+ }, {
747
+ _v$: void 0,
748
+ _v$2: void 0,
749
+ _v$3: void 0
750
+ });
751
+ return _el$2;
752
+ }
753
+ });
754
+ }
755
+ }), null);
756
+ insert(_el$, createComponent(Show, {
757
+ get when() {
758
+ return context.mode === "keys";
759
+ },
760
+ get children() {
761
+ return createComponent(Index, {
762
+ each: keys,
763
+ children: (key) => {
764
+ const virtualDimensions = createMemo(() => verticalViewPort().calculatePixelDimensions(127 - key().number, 1));
765
+ return createComponent(Show, {
766
+ get when() {
767
+ return verticalViewPort().isVisible(virtualDimensions());
768
+ },
769
+ get children() {
770
+ const _el$3 = _tmpl$4.cloneNode(true);
771
+ effect((_p$) => {
772
+ const _v$4 = {
773
+ [_default5["PianoRollGrid-Row"]]: true,
774
+ [_default5["Key"]]: true,
775
+ [_default5["IsDark"]]: key().isBlack,
776
+ [_default5["IsBlackAndNextIsWhite"]]: !key().isBlack && !blackKeys.includes((key().number + 1) % 12)
777
+ }, _v$5 = `${virtualDimensions().offset}px`, _v$6 = `${virtualDimensions().size}px`;
778
+ _p$._v$4 = classList(_el$3, _v$4, _p$._v$4);
779
+ _v$5 !== _p$._v$5 && _el$3.style.setProperty("top", _p$._v$5 = _v$5);
780
+ _v$6 !== _p$._v$6 && _el$3.style.setProperty("height", _p$._v$6 = _v$6);
781
+ return _p$;
782
+ }, {
783
+ _v$4: void 0,
784
+ _v$5: void 0,
785
+ _v$6: void 0
786
+ });
787
+ return _el$3;
788
+ }
789
+ });
790
+ }
791
+ });
792
+ }
793
+ }), null);
794
+ insert(_el$, createComponent(Show, {
795
+ get when() {
796
+ return context.mode === "tracks";
797
+ },
798
+ get children() {
799
+ return createComponent(For, {
800
+ get each() {
801
+ return context.tracks;
802
+ },
803
+ children: (track, index) => {
804
+ const virtualDimensions = createMemo(() => verticalViewPort().calculatePixelDimensions(index(), 1));
805
+ return createComponent(Show, {
806
+ get when() {
807
+ return verticalViewPort().isVisible(virtualDimensions());
808
+ },
809
+ get children() {
810
+ const _el$4 = _tmpl$4.cloneNode(true);
811
+ effect((_p$) => {
812
+ const _v$7 = {
813
+ [_default5["PianoRollGrid-Row"]]: true,
814
+ [_default5["IsDark"]]: index() % 2 === 0
815
+ }, _v$8 = `${virtualDimensions().offset}px`, _v$9 = `${virtualDimensions().size}px`;
816
+ _p$._v$7 = classList(_el$4, _v$7, _p$._v$7);
817
+ _v$8 !== _p$._v$8 && _el$4.style.setProperty("top", _p$._v$8 = _v$8);
818
+ _v$9 !== _p$._v$9 && _el$4.style.setProperty("height", _p$._v$9 = _v$9);
819
+ return _p$;
820
+ }, {
821
+ _v$7: void 0,
822
+ _v$8: void 0,
823
+ _v$9: void 0
824
+ });
825
+ return _el$4;
826
+ }
827
+ });
828
+ }
829
+ });
830
+ }
831
+ }), null);
832
+ effect(() => className(_el$, _default5.PianoRollGrid));
833
+ return _el$;
834
+ })();
835
+ }, "PianoRollGrid");
836
+ var PianoRollGrid_default = PianoRollGrid;
837
+ var _tmpl$5 = /* @__PURE__ */ template(`<input step="0.01">`, 1);
838
+ var ZoomSliderControl = /* @__PURE__ */ __name((props) => {
839
+ const propsWithDefaults = mergeProps({
840
+ orientation: "horizontal"
841
+ }, props);
842
+ const dimension = createMemo(() => useViewPortDimension(props.dimensionName ?? propsWithDefaults.orientation));
843
+ return (() => {
844
+ const _el$ = _tmpl$5.cloneNode(true);
845
+ spread(_el$, mergeProps$1({
846
+ get min() {
847
+ return dimension().minZoom;
848
+ },
849
+ get max() {
850
+ return dimension().maxZoom;
851
+ }
852
+ }, props, {
853
+ get value() {
854
+ return dimension().zoom;
855
+ },
856
+ "onInput": (event) => {
857
+ const value = event.currentTarget.valueAsNumber;
858
+ dimension().onZoomChange?.(value);
859
+ },
860
+ "type": "range"
861
+ }, () => ({
862
+ orient: propsWithDefaults.orientation
863
+ }), {
864
+ get style() {
865
+ return propsWithDefaults.orientation === "vertical" ? {
866
+ margin: 0,
867
+ "margin-left": "4px",
868
+ "margin-right": "4px",
869
+ width: "16px",
870
+ transform: "rotate(180deg)",
871
+ ...{
872
+ "writing-mode": "bt-lr",
873
+ "-webkit-appearance": "slider-vertical"
874
+ },
875
+ ...typeof props.style === "object" && props.style
876
+ } : {
877
+ margin: 0,
878
+ "margin-top": "4px",
879
+ "margin-bottom": "4px",
880
+ ...typeof props.style === "object" && props.style
881
+ };
882
+ }
883
+ }), false, false);
884
+ return _el$;
885
+ })();
886
+ }, "ZoomSliderControl");
887
+ var ZoomSliderControl_default = ZoomSliderControl;
888
+
889
+ // src/PianoRollTrackList.module.css
890
+ var _default6 = {};
891
+ var _tmpl$6 = /* @__PURE__ */ template(`<div></div>`, 2);
892
+ var _tmpl$22 = /* @__PURE__ */ template(`<div> </div>`, 2);
893
+ var PianoRollTrackList = /* @__PURE__ */ __name(() => {
894
+ const viewPort = createMemo(() => useViewPortDimension("verticalTracks"));
895
+ const context = usePianoRollContext();
896
+ return (() => {
897
+ const _el$ = _tmpl$6.cloneNode(true);
898
+ insert(_el$, createComponent(Index, {
899
+ get each() {
900
+ return context.tracks;
901
+ },
902
+ children: (track, index) => {
903
+ const virtualDimensions = createMemo(() => viewPort().calculatePixelDimensions(index, 1));
904
+ return createComponent(Show, {
905
+ get when() {
906
+ return virtualDimensions().size > 0;
907
+ },
908
+ get children() {
909
+ const _el$2 = _tmpl$22.cloneNode(true), _el$3 = _el$2.firstChild;
910
+ _el$2.$$click = () => context.onSelectedTrackIndexChange?.(index);
911
+ insert(_el$2, index, _el$3);
912
+ insert(_el$2, () => track().name || "[unnamed track]", null);
913
+ effect((_p$) => {
914
+ const _v$ = {
915
+ [_default6["Track"]]: true,
916
+ [_default6["selected"]]: index === context.selectedTrackIndex
917
+ }, _v$2 = track().name, _v$3 = `${virtualDimensions().offset}px`, _v$4 = `${virtualDimensions().size}px`;
918
+ _p$._v$ = classList(_el$2, _v$, _p$._v$);
919
+ _v$2 !== _p$._v$2 && setAttribute(_el$2, "title", _p$._v$2 = _v$2);
920
+ _v$3 !== _p$._v$3 && _el$2.style.setProperty("top", _p$._v$3 = _v$3);
921
+ _v$4 !== _p$._v$4 && _el$2.style.setProperty("height", _p$._v$4 = _v$4);
922
+ return _p$;
923
+ }, {
924
+ _v$: void 0,
925
+ _v$2: void 0,
926
+ _v$3: void 0,
927
+ _v$4: void 0
928
+ });
929
+ return _el$2;
930
+ }
931
+ });
932
+ }
933
+ }));
934
+ effect(() => className(_el$, _default6.PianoRollTrackList));
935
+ return _el$;
936
+ })();
937
+ }, "PianoRollTrackList");
938
+ var PianoRollTrackList_default = PianoRollTrackList;
939
+ delegateEvents(["click"]);
940
+ var PianoRollScrollZoomViewPort = /* @__PURE__ */ __name((props) => {
941
+ const zoomFactor = 500;
942
+ const context = usePianoRollContext();
943
+ return createComponent(ScrollZoomViewPort, {
944
+ dimensions: {
945
+ horizontal: () => ({
946
+ pixelOffset: context.notesScrollerClientRect.left,
947
+ pixelSize: context.notesScrollerClientRect.width,
948
+ position: context.position,
949
+ range: context.duration,
950
+ zoom: context.zoom * (zoomFactor / context.notesScrollerClientRect.width),
951
+ onPositionChange: context.onPositionChange,
952
+ onZoomChange: (zoom) => context.onZoomChange?.(zoom / (zoomFactor / context.notesScrollerClientRect.width)),
953
+ minZoom: 1,
954
+ maxZoom: 500
955
+ }),
956
+ vertical: () => ({
957
+ pixelOffset: context.notesScrollerClientRect.top,
958
+ pixelSize: context.notesScrollerClientRect.height,
959
+ position: context.verticalPosition,
960
+ range: 128,
961
+ zoom: context.verticalZoom * (zoomFactor / context.notesScrollerClientRect.height),
962
+ onPositionChange: context.onVerticalPositionChange,
963
+ onZoomChange: (verticalZoom) => context.onVerticalZoomChange?.(verticalZoom / (zoomFactor / context.notesScrollerClientRect.height)),
964
+ minZoom: 1,
965
+ maxZoom: 10
966
+ }),
967
+ horizontalTracks: () => ({
968
+ pixelOffset: 0,
969
+ pixelSize: context.tracksScrollerClientRect.width
970
+ }),
971
+ verticalTracks: () => ({
972
+ pixelOffset: context.tracksScrollerClientRect.top,
973
+ pixelSize: context.tracksScrollerClientRect.height,
974
+ position: context.verticalTrackPosition,
975
+ range: context.tracks.length,
976
+ zoom: context.verticalTrackZoom * (zoomFactor / context.tracksScrollerClientRect.height),
977
+ onPositionChange: context.onVerticalTrackPositionChange,
978
+ onZoomChange: (verticalTrackZoom) => context.onVerticalTrackZoomChange?.(verticalTrackZoom / (zoomFactor / context.tracksScrollerClientRect.height)),
979
+ minZoom: 0.8,
980
+ maxZoom: 3
981
+ }),
982
+ horizontalKeys: () => ({
983
+ pixelSize: 60
984
+ })
985
+ },
986
+ get children() {
987
+ return props.children;
988
+ }
989
+ });
990
+ }, "PianoRollScrollZoomViewPort");
991
+ var PianoRollScrollZoomViewPort_default = PianoRollScrollZoomViewPort;
992
+ var defaultRect = {
993
+ left: 0,
994
+ width: 0,
995
+ top: 0,
996
+ height: 0,
997
+ bottom: 0,
998
+ right: 0,
999
+ x: 0,
1000
+ y: 0
1001
+ };
1002
+ function useBoundingClientRect(containerRef) {
1003
+ const [boundingClientRect, setBoundingClientRect] = createSignal(defaultRect);
1004
+ createEffect(() => {
1005
+ const container = containerRef();
1006
+ if (!container)
1007
+ return;
1008
+ const updateBoundingClientRect = /* @__PURE__ */ __name(() => {
1009
+ setBoundingClientRect(container.getBoundingClientRect() ?? defaultRect);
1010
+ }, "updateBoundingClientRect");
1011
+ const resizeObserver = new ResizeObserver(updateBoundingClientRect);
1012
+ resizeObserver.observe(container);
1013
+ updateBoundingClientRect();
1014
+ return () => {
1015
+ resizeObserver.disconnect();
1016
+ };
1017
+ });
1018
+ return boundingClientRect;
1019
+ }
1020
+ __name(useBoundingClientRect, "useBoundingClientRect");
1021
+
1022
+ // src/PianoRollNotesScroller.tsx
1023
+ var PianoRollNotesScroller = /* @__PURE__ */ __name((props) => {
1024
+ const context = usePianoRollContext();
1025
+ const [ref, setRef] = createSignal();
1026
+ const clientRect = useBoundingClientRect(ref);
1027
+ createEffect(() => {
1028
+ context.onNotesScrollerClientRectChange(clientRect());
1029
+ });
1030
+ return createComponent(ScrollZoomContainer_default, {
1031
+ ref: setRef,
1032
+ get verticalDimensionName() {
1033
+ return context.mode === "keys" ? "vertical" : "verticalTracks";
1034
+ },
1035
+ get children() {
1036
+ return props.children;
1037
+ }
1038
+ });
1039
+ }, "PianoRollNotesScroller");
1040
+ var PianoRollNotesScroller_default = PianoRollNotesScroller;
1041
+ var PianoRollTrackListScroller = /* @__PURE__ */ __name((props) => {
1042
+ const context = usePianoRollContext();
1043
+ const [ref, setRef] = createSignal();
1044
+ const clientRect = useBoundingClientRect(ref);
1045
+ createEffect(() => {
1046
+ context.onTracksScrollerClientRectChange(clientRect());
1047
+ });
1048
+ return createComponent(ScrollZoomContainer_default, {
1049
+ ref: setRef,
1050
+ horizontalDimensionName: "horizontalTracks",
1051
+ verticalDimensionName: "verticalTracks",
1052
+ get showScrollbar() {
1053
+ return context.mode === "keys";
1054
+ },
1055
+ get children() {
1056
+ return props.children;
1057
+ }
1058
+ });
1059
+ }, "PianoRollTrackListScroller");
1060
+ var PianoRollTrackListScroller_default = PianoRollTrackListScroller;
1061
+
1062
+ // src/PianoRollScale.module.css
1063
+ var _default7 = {};
1064
+
1065
+ // src/PianoRollScale.tsx
1066
+ var _tmpl$7 = /* @__PURE__ */ template(`<div></div>`, 2);
1067
+ var PianoRollScale = /* @__PURE__ */ __name(() => {
1068
+ const horizontalViewPort = createMemo(() => useViewPortDimension("horizontal"));
1069
+ const grid = usePianoRollGrid_default();
1070
+ const context = usePianoRollContext();
1071
+ const updatePlayheadPosition = /* @__PURE__ */ __name((event) => {
1072
+ const position = horizontalViewPort().calculatePosition(event.clientX);
1073
+ context.onPlayHeadPositionChange(position, event);
1074
+ }, "updatePlayheadPosition");
1075
+ return (() => {
1076
+ const _el$ = _tmpl$7.cloneNode(true);
1077
+ _el$.$$mousedown = (event) => {
1078
+ updatePlayheadPosition(event);
1079
+ const handleMouseUp = /* @__PURE__ */ __name(() => {
1080
+ window.removeEventListener("mousemove", updatePlayheadPosition);
1081
+ window.removeEventListener("mouseup", handleMouseUp);
1082
+ }, "handleMouseUp");
1083
+ window.addEventListener("mousemove", updatePlayheadPosition);
1084
+ window.addEventListener("mouseup", handleMouseUp);
1085
+ };
1086
+ insert(_el$, createComponent(Index, {
1087
+ get each() {
1088
+ return grid();
1089
+ },
1090
+ children: (entry) => {
1091
+ return createComponent(Show, {
1092
+ get when() {
1093
+ return horizontalViewPort().isVisible(entry().virtualDimensions);
1094
+ },
1095
+ get children() {
1096
+ const _el$2 = _tmpl$7.cloneNode(true);
1097
+ insert(_el$2, createComponent(Show, {
1098
+ get when() {
1099
+ return entry().showLabel;
1100
+ },
1101
+ get children() {
1102
+ const _el$3 = _tmpl$7.cloneNode(true);
1103
+ insert(_el$3, () => entry().label);
1104
+ effect(() => className(_el$3, _default7["PianoRollScale-Label"]));
1105
+ return _el$3;
1106
+ }
1107
+ }));
1108
+ effect((_p$) => {
1109
+ const _v$ = {
1110
+ [_default7["PianoRollScale-Time"]]: true,
1111
+ [_default7["Highlighted"]]: entry().isHighlighted,
1112
+ [_default7["HighlightedBorder"]]: entry().hasHighlightedBorder
1113
+ }, _v$2 = `${entry().virtualDimensions.offset}px`, _v$3 = `${entry().virtualDimensions.size}px`;
1114
+ _p$._v$ = classList(_el$2, _v$, _p$._v$);
1115
+ _v$2 !== _p$._v$2 && _el$2.style.setProperty("left", _p$._v$2 = _v$2);
1116
+ _v$3 !== _p$._v$3 && _el$2.style.setProperty("width", _p$._v$3 = _v$3);
1117
+ return _p$;
1118
+ }, {
1119
+ _v$: void 0,
1120
+ _v$2: void 0,
1121
+ _v$3: void 0
1122
+ });
1123
+ return _el$2;
1124
+ }
1125
+ });
1126
+ }
1127
+ }));
1128
+ effect(() => className(_el$, _default7.PianoRollScale));
1129
+ return _el$;
1130
+ })();
1131
+ }, "PianoRollScale");
1132
+ var PianoRollScale_default = PianoRollScale;
1133
+ delegateEvents(["mousedown"]);
1134
+
1135
+ // src/PianoRoll.tsx
1136
+ var _tmpl$8 = /* @__PURE__ */ template(`<div><button><div></div></button></div>`, 6);
1137
+ var _tmpl$23 = /* @__PURE__ */ template(`<div><div></div><div></div></div>`, 6);
1138
+ var _tmpl$32 = /* @__PURE__ */ template(`<div><div><div></div></div></div>`, 6);
1139
+ var _tmpl$42 = /* @__PURE__ */ template(`<div></div>`, 2);
1140
+ var PianoRoll = /* @__PURE__ */ __name((allProps) => {
1141
+ const propsWithDefaults = mergeProps({
1142
+ showAllTracks: false
1143
+ }, allProps);
1144
+ const [context, divProps] = splitContextProps(propsWithDefaults);
1145
+ return createComponent(PianoRollContextProvider, {
1146
+ value: context,
1147
+ get children() {
1148
+ const _el$ = _tmpl$42.cloneNode(true);
1149
+ spread(_el$, mergeProps$1(divProps, {
1150
+ get ["class"]() {
1151
+ return _default.PianoRoll;
1152
+ }
1153
+ }), false, true);
1154
+ insert(_el$, createComponent(PianoRollScrollZoomViewPort_default, {
1155
+ get children() {
1156
+ return [(() => {
1157
+ const _el$2 = _tmpl$32.cloneNode(true), _el$3 = _el$2.firstChild, _el$7 = _el$3.firstChild;
1158
+ insert(_el$2, createComponent(ZoomSliderControl_default, {
1159
+ orientation: "vertical",
1160
+ dimensionName: "verticalTracks"
1161
+ }), _el$3);
1162
+ insert(_el$3, createComponent(Show, {
1163
+ get when() {
1164
+ return context.showTrackList;
1165
+ },
1166
+ get children() {
1167
+ const _el$4 = _tmpl$8.cloneNode(true), _el$5 = _el$4.firstChild, _el$6 = _el$5.firstChild;
1168
+ _el$4.style.setProperty("height", "30px");
1169
+ _el$4.style.setProperty("border-right", "1px black solid");
1170
+ _el$5.$$click = () => context.onModeChange(context.mode === "keys" ? "tracks" : "keys");
1171
+ _el$5.style.setProperty("font-size", "16px");
1172
+ _el$5.style.setProperty("line-height", "16px");
1173
+ _el$5.style.setProperty("cursor", "pointer");
1174
+ _el$5.style.setProperty("overflow", "hidden");
1175
+ insert(_el$6, () => context.mode === "keys" ? "\u2261" : "\u{1F3B9}");
1176
+ effect(() => setAttribute(_el$5, "title", context.mode === "keys" ? "Tracks Mode" : "Keys Mode"));
1177
+ return _el$4;
1178
+ }
1179
+ }), _el$7);
1180
+ _el$7.style.setProperty("flex", "1");
1181
+ _el$7.style.setProperty("display", "flex");
1182
+ _el$7.style.setProperty("overflow", "hidden");
1183
+ insert(_el$7, createComponent(Show, {
1184
+ get when() {
1185
+ return context.showTrackList;
1186
+ },
1187
+ get children() {
1188
+ return createComponent(PianoRollTrackListScroller_default, {
1189
+ get children() {
1190
+ return createComponent(PianoRollTrackList_default, {});
1191
+ }
1192
+ });
1193
+ }
1194
+ }), null);
1195
+ insert(_el$7, createComponent(Show, {
1196
+ get when() {
1197
+ return context.mode === "keys";
1198
+ },
1199
+ get children() {
1200
+ return createComponent(PianoRollKeys_default, {});
1201
+ }
1202
+ }), null);
1203
+ insert(_el$2, createComponent(PianoRollNotesScroller_default, {
1204
+ get children() {
1205
+ return [memo(() => allProps.children), (() => {
1206
+ const _el$8 = _tmpl$23.cloneNode(true), _el$9 = _el$8.firstChild, _el$10 = _el$9.nextSibling;
1207
+ _el$8.style.setProperty("width", "100%");
1208
+ _el$8.style.setProperty("display", "flex");
1209
+ _el$8.style.setProperty("height", "100%");
1210
+ _el$8.style.setProperty("flex-direction", "column");
1211
+ _el$8.style.setProperty("overflow", "hidden");
1212
+ _el$9.style.setProperty("height", "30px");
1213
+ insert(_el$9, createComponent(PianoRollScale_default, {}));
1214
+ _el$10.style.setProperty("flex", "1");
1215
+ _el$10.style.setProperty("position", "relative");
1216
+ _el$10.style.setProperty("overflow", "hidden");
1217
+ insert(_el$10, createComponent(PianoRollGrid_default, {}), null);
1218
+ insert(_el$10, createComponent(PianoRollNotes_default, {}), null);
1219
+ return _el$8;
1220
+ })()];
1221
+ }
1222
+ }), null);
1223
+ insert(_el$2, createComponent(ZoomSliderControl_default, {
1224
+ orientation: "vertical",
1225
+ get disabled() {
1226
+ return context.mode !== "keys";
1227
+ }
1228
+ }), null);
1229
+ effect((_p$) => {
1230
+ const _v$ = _default.PianoRollContainer, _v$2 = {
1231
+ [_default.PianoRollLeftColumn]: true,
1232
+ [_default.showTrackList]: context.showTrackList
1233
+ };
1234
+ _v$ !== _p$._v$ && className(_el$2, _p$._v$ = _v$);
1235
+ _p$._v$2 = classList(_el$3, _v$2, _p$._v$2);
1236
+ return _p$;
1237
+ }, {
1238
+ _v$: void 0,
1239
+ _v$2: void 0
1240
+ });
1241
+ return _el$2;
1242
+ })(), createComponent(ZoomSliderControl_default, {
1243
+ orientation: "horizontal",
1244
+ get style() {
1245
+ return {
1246
+ "margin-left": `${context.tracksScrollerClientRect.width + 24}px`,
1247
+ "margin-right": "24px"
1248
+ };
1249
+ }
1250
+ })];
1251
+ }
1252
+ }));
1253
+ return _el$;
1254
+ }
1255
+ });
1256
+ }, "PianoRoll");
1257
+ var PianoRoll_default = PianoRoll;
1258
+ delegateEvents(["click"]);
1259
+ var _tmpl$9 = /* @__PURE__ */ template(`<div class="PlayHead"></div>`, 2);
1260
+ var PlayHead = /* @__PURE__ */ __name((allProps) => {
1261
+ const [props, divProps] = splitProps(allProps, ["position", "sync", "onPositionChange", "dimensionName"]);
1262
+ const viewPort = useViewPortDimension(props.dimensionName ?? "horizontal");
1263
+ createEffect(() => {
1264
+ if (!props.sync)
1265
+ return;
1266
+ const maxPosition = viewPort.range;
1267
+ const newPosition = clamp(props.position - viewPort.range / viewPort.zoom / 2, 0, maxPosition);
1268
+ viewPort.onPositionChange?.(newPosition);
1269
+ });
1270
+ const leftPosition = createMemo(() => viewPort.calculatePixelOffset(props.position));
1271
+ return (() => {
1272
+ const _el$ = _tmpl$9.cloneNode(true);
1273
+ spread(_el$, mergeProps$1(divProps, {
1274
+ get style() {
1275
+ return {
1276
+ position: "absolute",
1277
+ height: "100%",
1278
+ width: "2px",
1279
+ left: `${leftPosition()}px`,
1280
+ "background-color": "green",
1281
+ cursor: "pointer",
1282
+ ...typeof divProps.style === "object" && divProps.style
1283
+ };
1284
+ },
1285
+ "onMouseDown": (event) => {
1286
+ event.preventDefault();
1287
+ event.stopPropagation();
1288
+ const handleMouseMove = /* @__PURE__ */ __name((event2) => {
1289
+ const newPosition = viewPort.calculatePosition(event2.clientX);
1290
+ props.onPositionChange?.(newPosition, event2);
1291
+ }, "handleMouseMove");
1292
+ const handleMouseUp = /* @__PURE__ */ __name(() => {
1293
+ window.removeEventListener("mousemove", handleMouseMove);
1294
+ window.removeEventListener("mouseup", handleMouseUp);
1295
+ }, "handleMouseUp");
1296
+ window.addEventListener("mousemove", handleMouseMove);
1297
+ window.addEventListener("mouseup", handleMouseUp);
1298
+ }
1299
+ }), false, false);
1300
+ return _el$;
1301
+ })();
1302
+ }, "PlayHead");
1303
+ var PlayHead_default = PlayHead;
1304
+ var useNotes = /* @__PURE__ */ __name(() => {
1305
+ const [notes, onNotesChange] = createSignal([]);
1306
+ const onNoteChange = /* @__PURE__ */ __name((index, note) => {
1307
+ onNotesChange([...notes().slice(0, index), note, ...notes().splice(index + 1)]);
1308
+ }, "onNoteChange");
1309
+ const onInsertNote = /* @__PURE__ */ __name((note) => {
1310
+ const index = Math.max(
1311
+ notes().findIndex(({ ticks }) => ticks > note.ticks),
1312
+ 0
1313
+ );
1314
+ onNotesChange([...notes().slice(0, index), note, ...notes().splice(index)]);
1315
+ return index;
1316
+ }, "onInsertNote");
1317
+ const onRemoveNote = /* @__PURE__ */ __name((index) => {
1318
+ onNotesChange([...notes().slice(0, index), ...notes().splice(index + 1)]);
1319
+ }, "onRemoveNote");
1320
+ return {
1321
+ notes,
1322
+ onNotesChange,
1323
+ onNoteChange,
1324
+ onInsertNote,
1325
+ onRemoveNote
1326
+ };
1327
+ }, "useNotes");
1328
+ var useNotes_default = useNotes;
1329
+
1330
+ export { PianoRoll_default as PianoRoll, PlayHead_default as PlayHead, usePianoRollState_default as createPianoRollstate, useNotes_default as useNotes };