solid-pianoroll 0.0.15 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createComponent, 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';
1
+ import { createComponent, use, spread, mergeProps as mergeProps$1, insert, effect, template, delegateEvents, className, classList, setAttribute, memo } from 'solid-js/web';
2
+ import { createContext, splitProps, useContext, createEffect, mergeProps, createMemo, createSignal, Index, Show, batch, For } from 'solid-js';
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$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);
30
+ var css_248z$5 = ".PianoRoll-module_PianoRoll__rkRYx {\n box-sizing: border-box;\n display: flex;\n overflow: hidden;\n flex-direction: column;\n height: 100%;\n}\n\n.PianoRoll-module_PianoRollContainer__Oyxr7 {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n";
31
+ var styles$5 = {"PianoRoll":"PianoRoll-module_PianoRoll__rkRYx","PianoRollContainer":"PianoRoll-module_PianoRollContainer__Oyxr7"};
32
+ styleInject(css_248z$5);
33
33
 
34
34
  const PianoRollContext = createContext();
35
35
  const PianoRollContextProvider = PianoRollContext.Provider;
@@ -38,11 +38,11 @@ const usePianoRollContext = () => {
38
38
  if (!context) throw new Error("No PianoRollContext found");
39
39
  return context;
40
40
  };
41
- const splitContextProps = allProps => splitProps(allProps, ["mode", "duration", "gridDivision", "tracks", "position", "ppq", "snapToGrid", "verticalPosition", "verticalZoom", "zoom", "onInsertNote", "onNoteChange", "onPositionChange", "onRemoveNote", "onVerticalPositionChange", "onVerticalZoomChange", "onZoomChange", "showAllTracks", "selectedTrackIndex", "onSelectedTrackIndexChange"]);
41
+ const splitContextProps = allProps => splitProps(allProps, ["ppq", "mode", "position", "zoom", "verticalZoom", "verticalPosition", "verticalTrackZoom", "verticalTrackPosition", "gridDivision", "snapToGrid", "duration", "tracks", "selectedTrackIndex", "pressedKeys", "onPpqChange", "onModeChange", "onPositionChange", "onZoomChange", "onVerticalZoomChange", "onVerticalPositionChange", "onVerticalTrackZoomChange", "onVerticalTrackPositionChange", "onGridDivisionChange", "onSnapToGridChange", "onDurationChange", "onTracksChange", "onNoteChange", "onInsertNote", "onRemoveNote", "onSelectedTrackIndexChange", "onPressedKeysChange", "showAllTracks", "showTrackList"]);
42
42
 
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);
43
+ var css_248z$4 = ".PianoRollKeys-module_PianoRollKeys__5vnQ0 {\n position: relative;\n height: 100%;\n width: 50px;\n border-width: 0;\n border-color: #000;\n border-style: solid;\n border-right-width: 1px;\n border-left-width: 1px;\n background: white;\n cursor: pointer;\n overflow: hidden;\n}\n\n.PianoRollKeys-module_Key__jybNC {\n transition-property: background-color, box-shadow;\n transition-duration: 200ms;\n transition-timing-function: ease-out;\n border-width: 0px;\n position: absolute;\n box-sizing: border-box;\n width: 120%;\n left: -20%;\n border-color: #000;\n border-style: solid;\n}\n\n.PianoRollKeys-module_black__7rncB {\n background: black;\n border-top-right-radius: 12%;\n border-bottom-right-radius: 12%;\n margin-left: -20%;\n z-index: 1;\n}\n\n.PianoRollKeys-module_white__fR2Lm {\n background: white;\n border-top-width: 0.5px;\n border-bottom-width: 0.5px;\n}\n\n.PianoRollKeys-module_down__GcNeZ {\n transition: none;\n background: red;\n}\n";
44
+ var styles$4 = {"PianoRollKeys":"PianoRollKeys-module_PianoRollKeys__5vnQ0","Key":"PianoRollKeys-module_Key__jybNC","black":"PianoRollKeys-module_black__7rncB","white":"PianoRollKeys-module_white__fR2Lm","down":"PianoRollKeys-module_down__GcNeZ"};
45
+ styleInject(css_248z$4);
46
46
 
47
47
  function createViewPortDimension(getState) {
48
48
  const getStateWithFunctions = () => ({
@@ -116,55 +116,248 @@ const useViewPortDimension = name => {
116
116
  return viewPort;
117
117
  };
118
118
 
119
- const _tmpl$$8 = /*#__PURE__*/template(`<div></div>`, 2);
120
- const PianoRollKeys = () => {
121
- const viewPort = createMemo(() => useViewPortDimension("vertical"));
119
+ var css_248z$3 = ".ScrollContainer-module_ScrollContainer__As0vK {\n height: 100%;\n width: 100%;\n z-index: 4;\n pointer-events: auto;\n}\n";
120
+ var styles$3 = {"ScrollContainer":"ScrollContainer-module_ScrollContainer__As0vK"};
121
+ styleInject(css_248z$3);
122
+
123
+ const _tmpl$$8 = /*#__PURE__*/template(`<div><div><div><div></div></div></div></div>`, 8);
124
+ const ScrollContainer = props => {
125
+ let scrollContentRef;
126
+ const [ownProps, divProps] = splitProps(props, ["ref", "verticalDimensionName", "horizontalDimensionName", "showScrollbar"]);
127
+ const propsWithDefaults = mergeProps({
128
+ verticalDimensionName: "vertical",
129
+ horizontalDimensionName: "horizontal",
130
+ showScrollbar: true
131
+ }, ownProps);
132
+ const verticalViewPort = createMemo(() => useViewPortDimension(propsWithDefaults.verticalDimensionName));
133
+ const horizontalViewPort = createMemo(() => useViewPortDimension(propsWithDefaults.horizontalDimensionName));
134
+ const handleScroll = event => {
135
+ event.preventDefault();
136
+ if (didUpdateScroll) {
137
+ didUpdateScroll = false;
138
+ return;
139
+ }
140
+ const maxVerticalPosition = verticalViewPort().calculateMaxPosition();
141
+ const maxPosition = horizontalViewPort().calculateMaxPosition();
142
+ const height = verticalViewPort().pixelSize;
143
+ const width = horizontalViewPort().pixelSize;
144
+ const {
145
+ scrollTop,
146
+ scrollLeft,
147
+ scrollWidth,
148
+ scrollHeight
149
+ } = event.currentTarget;
150
+ const scrollTopAmount = scrollTop / (scrollHeight - height);
151
+ const scrollLeftAmount = scrollLeft / (scrollWidth - width);
152
+ verticalViewPort().onPositionChange?.(maxVerticalPosition * scrollTopAmount);
153
+ horizontalViewPort().onPositionChange?.(maxPosition * scrollLeftAmount);
154
+ };
155
+ const handleWheel = event => {
156
+ if (event.altKey) {
157
+ event.preventDefault();
158
+ if (Math.abs(event.deltaX) > Math.abs(event.deltaY)) {
159
+ horizontalViewPort()?.onZoomChange?.(horizontalViewPort().zoom * (1 + event.deltaX / horizontalViewPort().pixelSize));
160
+ const maxPosition = horizontalViewPort().calculateMaxPosition();
161
+ horizontalViewPort()?.onPositionChange?.(Math.min(maxPosition, horizontalViewPort()?.position));
162
+ } else {
163
+ verticalViewPort()?.onZoomChange?.(verticalViewPort().zoom * (1 + event.deltaY / verticalViewPort().pixelSize));
164
+ const maxVerticalPosition = verticalViewPort().calculateMaxPosition();
165
+ verticalViewPort()?.onPositionChange?.(Math.min(maxVerticalPosition, verticalViewPort()?.position));
166
+ }
167
+ } else if (!props.showScrollbar) {
168
+ event.preventDefault();
169
+ event.currentTarget.scrollLeft += event.deltaX;
170
+ event.currentTarget.scrollTop += event.deltaY;
171
+ }
172
+ };
173
+ let didUpdateScroll = false;
174
+ createEffect(() => {
175
+ const maxVerticalPosition = verticalViewPort().calculateMaxPosition();
176
+ const maxPosition = horizontalViewPort().calculateMaxPosition();
177
+ const scrollTopAmount = maxVerticalPosition > 0 ? verticalViewPort().position / maxVerticalPosition : 0;
178
+ const scrollLeftAmount = maxPosition > 0 ? horizontalViewPort().position / maxPosition : 0;
179
+ const height = verticalViewPort().pixelSize;
180
+ const width = horizontalViewPort().pixelSize;
181
+ if (!scrollContentRef?.parentElement) return;
182
+ const scrollDivHeight = verticalViewPort().zoom * verticalViewPort().pixelSize;
183
+ const scrollTop = scrollTopAmount * (scrollDivHeight - height);
184
+ const scrollDivWidth = horizontalViewPort().zoom * horizontalViewPort().pixelSize;
185
+ const scrollLeft = scrollLeftAmount * (scrollDivWidth - width);
186
+ didUpdateScroll = true;
187
+ scrollContentRef.style.height = `${scrollDivHeight}px`;
188
+ scrollContentRef.style.width = `${scrollDivWidth}px`;
189
+ scrollContentRef.parentElement.scrollTo({
190
+ left: scrollLeft,
191
+ top: scrollTop
192
+ });
193
+ });
122
194
  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
- });
195
+ const _el$ = _tmpl$$8.cloneNode(true),
196
+ _el$2 = _el$.firstChild,
197
+ _el$3 = _el$2.firstChild,
198
+ _el$4 = _el$3.firstChild;
199
+ _el$.addEventListener("wheel", handleWheel);
200
+ _el$.addEventListener("scroll", handleScroll);
201
+ const _ref$ = props.ref;
202
+ typeof _ref$ === "function" ? use(_ref$, _el$) : props.ref = _el$;
203
+ spread(_el$, mergeProps$1({
204
+ get style() {
205
+ return {
206
+ overflow: propsWithDefaults.showScrollbar ? "scroll" : "hidden",
207
+ ...(typeof divProps.style === "object" && divProps.style)
208
+ };
209
+ },
210
+ get classList() {
211
+ return {
212
+ [styles$3.ScrollContainer]: true,
213
+ ...divProps.classList
214
+ };
162
215
  }
163
- }));
164
- effect(() => className(_el$, styles$3.PianoRollKeys));
216
+ }, divProps), false, true);
217
+ const _ref$2 = scrollContentRef;
218
+ typeof _ref$2 === "function" ? use(_ref$2, _el$2) : scrollContentRef = _el$2;
219
+ _el$3.style.setProperty("top", "0");
220
+ _el$3.style.setProperty("left", "0");
221
+ _el$3.style.setProperty("position", "sticky");
222
+ _el$3.style.setProperty("display", "flex");
223
+ _el$4.style.setProperty("position", "relative");
224
+ insert(_el$4, () => props.children);
225
+ effect(_p$ => {
226
+ const _v$ = `${verticalViewPort().pixelSize}px`,
227
+ _v$2 = `${horizontalViewPort().pixelSize}px`,
228
+ _v$3 = `${verticalViewPort().pixelSize}px`,
229
+ _v$4 = `${horizontalViewPort().pixelSize}px`;
230
+ _v$ !== _p$._v$ && _el$3.style.setProperty("height", _p$._v$ = _v$);
231
+ _v$2 !== _p$._v$2 && _el$3.style.setProperty("width", _p$._v$2 = _v$2);
232
+ _v$3 !== _p$._v$3 && _el$4.style.setProperty("height", _p$._v$3 = _v$3);
233
+ _v$4 !== _p$._v$4 && _el$4.style.setProperty("width", _p$._v$4 = _v$4);
234
+ return _p$;
235
+ }, {
236
+ _v$: undefined,
237
+ _v$2: undefined,
238
+ _v$3: undefined,
239
+ _v$4: undefined
240
+ });
165
241
  return _el$;
166
242
  })();
167
243
  };
244
+
245
+ const _tmpl$$7 = /*#__PURE__*/template(`<div></div>`, 2);
246
+ const PianoRollKeys = () => {
247
+ const viewPort = createMemo(() => useViewPortDimension("vertical"));
248
+ const [isMouseDown, setIsMouseDown] = createSignal(false);
249
+ const context = usePianoRollContext();
250
+ const handleMouseUp = () => {
251
+ setIsMouseDown(false);
252
+ };
253
+ createEffect(() => {
254
+ if (isMouseDown()) {
255
+ window.addEventListener("mouseup", handleMouseUp);
256
+ } else {
257
+ window.removeEventListener("mouseup", handleMouseUp);
258
+ }
259
+ });
260
+ return createComponent(ScrollContainer, {
261
+ get classList() {
262
+ return {
263
+ [styles$4.PianoRollKeys]: true
264
+ };
265
+ },
266
+ showScrollbar: false,
267
+ get children() {
268
+ return createComponent(Index, {
269
+ each: keys,
270
+ children: key => {
271
+ const isDown = createMemo(() => context.pressedKeys.includes(key().number));
272
+ const handleKeyDown = () => {
273
+ context.onPressedKeysChange?.([...context.pressedKeys, key().number]);
274
+ };
275
+ const handleKeyUp = () => {
276
+ context.onPressedKeysChange?.([...context.pressedKeys].filter(number => number !== key().number));
277
+ };
278
+ const virtualDimensions = createMemo(() => viewPort().calculatePixelDimensions(127 - key().number, 1));
279
+ const previousIsBlack = blackKeys.includes((key().number - 1) % 12);
280
+ const nextIsBlack = blackKeys.includes((key().number + 1) % 12);
281
+ return createComponent(Show, {
282
+ get when() {
283
+ return virtualDimensions().size > 0;
284
+ },
285
+ get children() {
286
+ const _el$ = _tmpl$$7.cloneNode(true);
287
+ _el$.addEventListener("mouseleave", () => {
288
+ if (isMouseDown()) {
289
+ handleKeyUp();
290
+ }
291
+ });
292
+ _el$.addEventListener("mouseenter", () => {
293
+ if (isMouseDown()) {
294
+ handleKeyDown();
295
+ }
296
+ });
297
+ _el$.$$mouseup = () => {
298
+ handleKeyUp();
299
+ };
300
+ _el$.$$mousedown = () => {
301
+ setIsMouseDown(true);
302
+ handleKeyDown();
303
+ };
304
+ insert(_el$, createComponent(Show, {
305
+ get when() {
306
+ return key().isBlack && false;
307
+ },
308
+ get children() {
309
+ const _el$2 = _tmpl$$7.cloneNode(true);
310
+ effect(_p$ => {
311
+ const _v$ = styles$4["black-separator"],
312
+ _v$2 = `${virtualDimensions().offset}px`,
313
+ _v$3 = `${virtualDimensions().size / 2 - 0.25}px`;
314
+ _v$ !== _p$._v$ && className(_el$2, _p$._v$ = _v$);
315
+ _v$2 !== _p$._v$2 && _el$2.style.setProperty("top", _p$._v$2 = _v$2);
316
+ _v$3 !== _p$._v$3 && _el$2.style.setProperty("height", _p$._v$3 = _v$3);
317
+ return _p$;
318
+ }, {
319
+ _v$: undefined,
320
+ _v$2: undefined,
321
+ _v$3: undefined
322
+ });
323
+ return _el$2;
324
+ }
325
+ }));
326
+ effect(_p$ => {
327
+ const _v$4 = {
328
+ [styles$4["Key"]]: true,
329
+ [styles$4["black"]]: key().isBlack,
330
+ [styles$4["white"]]: !key().isBlack,
331
+ [styles$4["down"]]: isDown()
332
+ },
333
+ _v$5 = key().name,
334
+ _v$6 = key().number % 12,
335
+ _v$7 = `${virtualDimensions().offset - (!key().isBlack && nextIsBlack ? virtualDimensions().size / 2 : 0)}px`,
336
+ _v$8 = `${virtualDimensions().size + (!key().isBlack && nextIsBlack ? virtualDimensions().size / 2 : 0) + (!key().isBlack && previousIsBlack ? virtualDimensions().size / 2 : 0)}px`,
337
+ _v$9 = [`0px 0px ${Math.min(virtualDimensions().size / 20, 1)}px ${Math.min(virtualDimensions().size / 50, 1)}px rgba(0, 0, 0, 0.5) ${key().isBlack ? "" : "inset"}`, isDown() && `0px 0px ${Math.min(virtualDimensions().size / 8, 2)}px ${Math.min(virtualDimensions().size / 20, 2)}px rgba(0, 0, 0, 0.5) inset`].filter(item => !!item).join(", ");
338
+ _p$._v$4 = classList(_el$, _v$4, _p$._v$4);
339
+ _v$5 !== _p$._v$5 && setAttribute(_el$, "title", _p$._v$5 = _v$5);
340
+ _v$6 !== _p$._v$6 && setAttribute(_el$, "data-index", _p$._v$6 = _v$6);
341
+ _v$7 !== _p$._v$7 && _el$.style.setProperty("top", _p$._v$7 = _v$7);
342
+ _v$8 !== _p$._v$8 && _el$.style.setProperty("height", _p$._v$8 = _v$8);
343
+ _v$9 !== _p$._v$9 && _el$.style.setProperty("box-shadow", _p$._v$9 = _v$9);
344
+ return _p$;
345
+ }, {
346
+ _v$4: undefined,
347
+ _v$5: undefined,
348
+ _v$6: undefined,
349
+ _v$7: undefined,
350
+ _v$8: undefined,
351
+ _v$9: undefined
352
+ });
353
+ return _el$;
354
+ }
355
+ });
356
+ }
357
+ });
358
+ }
359
+ });
360
+ };
168
361
  const blackKeys = [1, 3, 6, 8, 10];
169
362
  const keyNames = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
170
363
  const keys = Array.from({
@@ -174,15 +367,16 @@ const keys = Array.from({
174
367
  name: `${keyNames[index % 12]} ${Math.floor(index / 12) - 2}`,
175
368
  isBlack: blackKeys.includes(index % 12)
176
369
  }));
370
+ delegateEvents(["mousedown", "mouseup"]);
177
371
 
178
372
  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}";
179
373
  var styles$2 = {"PianoRollNotes":"PianoRollNotes-module_PianoRollNotes__6pF-y","Note":"PianoRollNotes-module_Note__-jxLb","trimStart":"PianoRollNotes-module_trimStart__vuBlj","trimEnd":"PianoRollNotes-module_trimEnd__zPdjr"};
180
374
  styleInject(css_248z$2);
181
375
 
182
- const _tmpl$$7 = /*#__PURE__*/template(`<div></div>`, 2);
376
+ const _tmpl$$6 = /*#__PURE__*/template(`<div></div>`, 2);
183
377
  const PianoRollNotes = props => {
184
378
  const context = usePianoRollContext();
185
- const verticalViewPort = createMemo(() => useViewPortDimension("vertical"));
379
+ const verticalViewPort = createMemo(() => useViewPortDimension(context.mode === "keys" ? "vertical" : "verticalTracks"));
186
380
  const horizontalViewPort = createMemo(() => useViewPortDimension("horizontal"));
187
381
  const gridDivisionTicks = createMemo(() => context.ppq * 4 / context.gridDivision);
188
382
  const snapValueToGridIfEnabled = (value, altKey) => context.snapToGrid && !altKey ? Math.round(value / gridDivisionTicks()) * gridDivisionTicks() : value;
@@ -190,7 +384,7 @@ const PianoRollNotes = props => {
190
384
  const position = horizontalViewPort().calculatePosition(event.clientX);
191
385
  const existingNote = currentNote();
192
386
  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;
387
+ const targetTrackIndex = context.mode === "tracks" ? clamp(Math.floor(verticalViewPort().calculatePosition(event.clientY)), 0, context.tracks.length - 1) : context.selectedTrackIndex ?? 0;
194
388
  const eventPositionTicks = Math.floor(position / gridDivisionTicks()) * gridDivisionTicks();
195
389
  const velocity = 127;
196
390
  const ticks = existingNote?.ticks ?? eventPositionTicks;
@@ -218,7 +412,7 @@ const PianoRollNotes = props => {
218
412
  return noteDragMode ? [styles$2.Note, styles$2[noteDragMode]] : [styles$2.Note];
219
413
  };
220
414
  return (() => {
221
- const _el$ = _tmpl$$7.cloneNode(true);
415
+ const _el$ = _tmpl$$6.cloneNode(true);
222
416
  _el$.$$click = event => {
223
417
  if (currentNote()) {
224
418
  setCurrentNoteIndex(-1);
@@ -276,7 +470,7 @@ const PianoRollNotes = props => {
276
470
  return memo(() => !!verticalViewPort().isVisible(verticalVirtualDimensions()))() && horizontalViewPort().isVisible(horizontalDimensions());
277
471
  },
278
472
  get children() {
279
- const _el$2 = _tmpl$$7.cloneNode(true);
473
+ const _el$2 = _tmpl$$6.cloneNode(true);
280
474
  _el$2.$$mousedown = event => {
281
475
  event.stopPropagation();
282
476
  setIsDragging(true);
@@ -305,7 +499,7 @@ const PianoRollNotes = props => {
305
499
  };
306
500
  const previousTrackIndex = currentNoteTrackIndex();
307
501
  const previousNoteIndex = currentNoteIndex();
308
- const targetTrackIndex = context.mode === "tracks" ? Math.floor(verticalViewPort().calculatePosition(mouseMoveEvent.clientY)) : previousTrackIndex;
502
+ const targetTrackIndex = context.mode === "tracks" ? clamp(Math.floor(verticalViewPort().calculatePosition(mouseMoveEvent.clientY)), 0, context.tracks.length - 1) : previousTrackIndex;
309
503
  if (targetTrackIndex === previousTrackIndex) {
310
504
  context.onNoteChange?.(previousTrackIndex, previousNoteIndex, updatedNote);
311
505
  } else {
@@ -379,109 +573,6 @@ const PianoRollNotes = props => {
379
573
  };
380
574
  delegateEvents(["mousedown", "mousemove", "mouseup", "dblclick", "click"]);
381
575
 
382
- const _tmpl$$6 = /*#__PURE__*/template(`<div class="PianoRoll-Scroller"><div><div><div></div></div></div></div>`, 8);
383
- const ScrollContainer = props => {
384
- let scrollContentRef;
385
- const verticalViewPort = createMemo(() => useViewPortDimension("vertical"));
386
- const horizontalViewPort = createMemo(() => useViewPortDimension("horizontal"));
387
- const handleScroll = event => {
388
- event.preventDefault();
389
- if (didUpdateScroll) {
390
- didUpdateScroll = false;
391
- return;
392
- }
393
- const maxVerticalPosition = verticalViewPort().calculateMaxPosition();
394
- const maxPosition = horizontalViewPort().calculateMaxPosition();
395
- const height = verticalViewPort().pixelSize;
396
- const width = horizontalViewPort().pixelSize;
397
- const {
398
- scrollTop,
399
- scrollLeft,
400
- scrollWidth,
401
- scrollHeight
402
- } = event.currentTarget;
403
- const scrollTopAmount = scrollTop / (scrollHeight - height);
404
- const scrollLeftAmount = scrollLeft / (scrollWidth - width);
405
- verticalViewPort().onPositionChange?.(maxVerticalPosition * scrollTopAmount);
406
- horizontalViewPort().onPositionChange?.(maxPosition * scrollLeftAmount);
407
- };
408
- const handleWheel = event => {
409
- if (event.altKey) {
410
- event.preventDefault();
411
- if (Math.abs(event.deltaX) > Math.abs(event.deltaY)) {
412
- horizontalViewPort()?.onZoomChange?.(horizontalViewPort().zoom * (1 + event.deltaX / horizontalViewPort().pixelSize));
413
- const maxPosition = horizontalViewPort().calculateMaxPosition();
414
- horizontalViewPort()?.onPositionChange?.(Math.min(maxPosition, horizontalViewPort()?.position));
415
- } else {
416
- verticalViewPort()?.onZoomChange?.(verticalViewPort().zoom * (1 + event.deltaY / verticalViewPort().pixelSize));
417
- const maxVerticalPosition = verticalViewPort().calculateMaxPosition();
418
- verticalViewPort()?.onPositionChange?.(Math.min(maxVerticalPosition, verticalViewPort()?.position));
419
- }
420
- }
421
- };
422
- let didUpdateScroll = false;
423
- createEffect(() => {
424
- const maxVerticalPosition = verticalViewPort().calculateMaxPosition();
425
- const maxPosition = horizontalViewPort().calculateMaxPosition();
426
- const scrollTopAmount = maxVerticalPosition > 0 ? verticalViewPort().position / maxVerticalPosition : 0;
427
- const scrollLeftAmount = maxPosition > 0 ? horizontalViewPort().position / maxPosition : 0;
428
- const height = verticalViewPort().pixelSize;
429
- const width = horizontalViewPort().pixelSize;
430
- if (!scrollContentRef?.parentElement) return;
431
- const scrollDivHeight = verticalViewPort().zoom * verticalViewPort().pixelSize;
432
- const scrollTop = scrollTopAmount * (scrollDivHeight - height);
433
- const scrollDivWidth = horizontalViewPort().zoom * horizontalViewPort().pixelSize;
434
- const scrollLeft = scrollLeftAmount * (scrollDivWidth - width);
435
- didUpdateScroll = true;
436
- scrollContentRef.style.height = `${scrollDivHeight}px`;
437
- scrollContentRef.style.width = `${scrollDivWidth}px`;
438
- scrollContentRef.parentElement.scrollTo({
439
- left: scrollLeft,
440
- top: scrollTop
441
- });
442
- });
443
- return (() => {
444
- const _el$ = _tmpl$$6.cloneNode(true),
445
- _el$2 = _el$.firstChild,
446
- _el$3 = _el$2.firstChild,
447
- _el$4 = _el$3.firstChild;
448
- _el$.addEventListener("wheel", handleWheel);
449
- _el$.addEventListener("scroll", handleScroll);
450
- const _ref$ = props.ref;
451
- typeof _ref$ === "function" ? use(_ref$, _el$) : props.ref = _el$;
452
- _el$.style.setProperty("height", "100%");
453
- _el$.style.setProperty("width", "100%");
454
- _el$.style.setProperty("z-index", "4");
455
- _el$.style.setProperty("overflow", "scroll");
456
- _el$.style.setProperty("pointer-events", "auto");
457
- const _ref$2 = scrollContentRef;
458
- typeof _ref$2 === "function" ? use(_ref$2, _el$2) : scrollContentRef = _el$2;
459
- _el$3.style.setProperty("top", "0");
460
- _el$3.style.setProperty("left", "0");
461
- _el$3.style.setProperty("position", "sticky");
462
- _el$3.style.setProperty("display", "flex");
463
- _el$4.style.setProperty("position", "relative");
464
- insert(_el$4, () => props.children);
465
- effect(_p$ => {
466
- const _v$ = `${verticalViewPort().pixelSize}px`,
467
- _v$2 = `${horizontalViewPort().pixelSize}px`,
468
- _v$3 = `${verticalViewPort().pixelSize}px`,
469
- _v$4 = `${horizontalViewPort().pixelSize}px`;
470
- _v$ !== _p$._v$ && _el$3.style.setProperty("height", _p$._v$ = _v$);
471
- _v$2 !== _p$._v$2 && _el$3.style.setProperty("width", _p$._v$2 = _v$2);
472
- _v$3 !== _p$._v$3 && _el$4.style.setProperty("height", _p$._v$3 = _v$3);
473
- _v$4 !== _p$._v$4 && _el$4.style.setProperty("width", _p$._v$4 = _v$4);
474
- return _p$;
475
- }, {
476
- _v$: undefined,
477
- _v$2: undefined,
478
- _v$3: undefined,
479
- _v$4: undefined
480
- });
481
- return _el$;
482
- })();
483
- };
484
-
485
576
  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
577
  var styles$1 = {"PianoRollGrid":"PianoRollGrid-module_PianoRollGrid__tG119","PianoRollGrid-Row":"PianoRollGrid-module_PianoRollGrid-Row__nRMCs","PianoRollGrid-Time":"PianoRollGrid-module_PianoRollGrid-Time__jLz3E"};
487
578
  styleInject(css_248z$1);
@@ -489,7 +580,7 @@ styleInject(css_248z$1);
489
580
  const _tmpl$$5 = /*#__PURE__*/template(`<div></div>`, 2);
490
581
  const PianoRollGrid = () => {
491
582
  const context = usePianoRollContext();
492
- const verticalViewPort = createMemo(() => useViewPortDimension("vertical"));
583
+ const verticalViewPort = createMemo(() => useViewPortDimension(context.mode === "keys" ? "vertical" : "verticalTracks"));
493
584
  const horizontalViewPort = createMemo(() => useViewPortDimension("horizontal"));
494
585
  const measureTicks = createMemo(() => context.ppq * 4);
495
586
  const selectedGridDivisorTicks = createMemo(() => measureTicks() / context.gridDivision);
@@ -672,14 +763,14 @@ function useBoundingClientRect(containerRef) {
672
763
 
673
764
  const _tmpl$$4 = /*#__PURE__*/template(`<input min="1" max="11" step="0.01">`, 1);
674
765
  const VerticalZoomControl = props => {
675
- const context = usePianoRollContext();
766
+ const dimension = createMemo(() => useViewPortDimension(props.dimensionName ?? "vertical"));
676
767
  return (() => {
677
768
  const _el$ = _tmpl$$4.cloneNode(true);
678
- spread(_el$, mergeProps(props, {
769
+ spread(_el$, mergeProps$1(props, {
679
770
  get value() {
680
- return context.verticalZoom;
771
+ return dimension().zoom;
681
772
  },
682
- "onInput": event => context.onVerticalZoomChange?.(event.currentTarget.valueAsNumber),
773
+ "onInput": event => dimension().onZoomChange?.(event.currentTarget.valueAsNumber),
683
774
  "type": "range"
684
775
  }, {
685
776
  orient: "vertical"
@@ -687,6 +778,7 @@ const VerticalZoomControl = props => {
687
778
  get style() {
688
779
  return {
689
780
  width: "16px",
781
+ transform: "rotate(180deg)",
690
782
  ...{
691
783
  "writing-mode": "bt-lr" /* IE */,
692
784
  "-webkit-appearance": "slider-vertical" /* WebKit */
@@ -700,14 +792,14 @@ const VerticalZoomControl = props => {
700
792
 
701
793
  const _tmpl$$3 = /*#__PURE__*/template(`<input max="500" min="1" step="0.01">`, 1);
702
794
  const HorizontalZoomControl = props => {
703
- const context = usePianoRollContext();
795
+ const dimension = createMemo(() => useViewPortDimension(props.dimensionName ?? "horizontal"));
704
796
  return (() => {
705
797
  const _el$ = _tmpl$$3.cloneNode(true);
706
- spread(_el$, mergeProps(props, {
798
+ spread(_el$, mergeProps$1(props, {
707
799
  get value() {
708
- return context.zoom;
800
+ return dimension().zoom;
709
801
  },
710
- "onInput": event => context.onZoomChange?.(event.currentTarget.valueAsNumber),
802
+ "onInput": event => dimension().onZoomChange?.(event.currentTarget.valueAsNumber),
711
803
  "type": "range",
712
804
  get style() {
713
805
  return {
@@ -728,7 +820,7 @@ styleInject(css_248z);
728
820
  const _tmpl$$2 = /*#__PURE__*/template(`<div></div>`, 2),
729
821
  _tmpl$2$1 = /*#__PURE__*/template(`<div> </div>`, 2);
730
822
  const PianoRollTrackList = () => {
731
- const viewPort = createMemo(() => useViewPortDimension("vertical"));
823
+ const viewPort = createMemo(() => useViewPortDimension("verticalTracks"));
732
824
  const context = usePianoRollContext();
733
825
  return (() => {
734
826
  const _el$ = _tmpl$$2.cloneNode(true);
@@ -737,7 +829,7 @@ const PianoRollTrackList = () => {
737
829
  return context.tracks;
738
830
  },
739
831
  children: (track, index) => {
740
- const virtualDimensions = createMemo(() => viewPort().calculatePixelDimensions(context.mode === "tracks" ? index : index * 8, context.mode === "tracks" ? 1 : 8));
832
+ const virtualDimensions = createMemo(() => viewPort().calculatePixelDimensions(index, 1));
741
833
  return createComponent(Show, {
742
834
  get when() {
743
835
  return virtualDimensions().size > 0;
@@ -781,100 +873,149 @@ const PianoRollTrackList = () => {
781
873
  };
782
874
  delegateEvents(["click"]);
783
875
 
784
- const _tmpl$$1 = /*#__PURE__*/template(`<div></div>`, 2),
785
- _tmpl$2 = /*#__PURE__*/template(`<div><div></div></div>`, 4);
876
+ const _tmpl$$1 = /*#__PURE__*/template(`<div><div></div></div>`, 4),
877
+ _tmpl$2 = /*#__PURE__*/template(`<div></div>`, 2);
786
878
  const PianoRoll = allProps => {
787
- const [contextProps, divProps] = splitContextProps(allProps);
879
+ const propsWithDefaults = mergeProps({
880
+ showAllTracks: false
881
+ }, allProps);
882
+ const [context, divProps] = splitContextProps(propsWithDefaults);
788
883
  const [scrollerRef, setScrollerRef] = createSignal();
884
+ const [trackScrollerRef, setTrackScrollerRef] = createSignal();
789
885
  const clientRect = useBoundingClientRect(scrollerRef);
886
+ const trackClientRef = useBoundingClientRect(trackScrollerRef);
790
887
  const zoomFactor = 500;
791
888
  const minZoom = createMemo(() => 1 / (zoomFactor / clientRect().width));
792
889
  const maxZoom = createMemo(() => 500 * (zoomFactor / clientRect().width));
793
890
  const minVerticalZoom = createMemo(() => 1 / (zoomFactor / clientRect().height));
794
891
  const maxVerticalZoom = createMemo(() => 10 * (zoomFactor / clientRect().height));
892
+ const minVerticalTracksZoom = createMemo(() => 0.8 / (zoomFactor / trackClientRef().height));
893
+ const maxVerticalTracksZoom = createMemo(() => 3 * (zoomFactor / trackClientRef().height));
795
894
  createEffect(isInitial => {
796
895
  if (isInitial) return false;
797
- if (contextProps.mode === "tracks") {
798
- contextProps.onVerticalPositionChange?.(0);
896
+ if (context.mode === "tracks") {
897
+ context.onVerticalPositionChange?.(0);
799
898
  }
800
899
  }, true);
801
900
  return createComponent(PianoRollContextProvider, {
802
- value: contextProps,
901
+ value: context,
803
902
  get children() {
804
- const _el$ = _tmpl$2.cloneNode(true),
805
- _el$2 = _el$.firstChild;
806
- spread(_el$, mergeProps(divProps, {
903
+ const _el$ = _tmpl$2.cloneNode(true);
904
+ spread(_el$, mergeProps$1(divProps, {
807
905
  get ["class"]() {
808
- return styles$4.PianoRoll;
906
+ return styles$5.PianoRoll;
809
907
  }
810
908
  }), false, true);
811
- insert(_el$2, createComponent(ScrollZoomViewPort, {
909
+ insert(_el$, createComponent(ScrollZoomViewPort, {
812
910
  dimensions: {
813
911
  horizontal: () => ({
814
912
  pixelOffset: clientRect().left,
815
913
  pixelSize: clientRect().width,
816
- position: contextProps.position,
817
- range: contextProps.duration,
818
- zoom: contextProps.zoom * (zoomFactor / clientRect().width),
819
- onPositionChange: contextProps.onPositionChange,
820
- onZoomChange: zoom => contextProps.onZoomChange?.(clamp(zoom / (zoomFactor / clientRect().width), minZoom(), maxZoom()))
914
+ position: context.position,
915
+ range: context.duration,
916
+ zoom: context.zoom * (zoomFactor / clientRect().width),
917
+ onPositionChange: context.onPositionChange,
918
+ onZoomChange: zoom => context.onZoomChange?.(clamp(zoom / (zoomFactor / clientRect().width), minZoom(), maxZoom()))
821
919
  }),
822
920
  vertical: () => ({
823
921
  pixelOffset: clientRect().top,
824
922
  pixelSize: clientRect().height,
825
- position: contextProps.verticalPosition,
826
- range: contextProps.mode === "keys" ? 128 : Math.max(contextProps.tracks.length, 16),
827
- zoom: contextProps.verticalZoom * (zoomFactor / clientRect().height),
828
- onPositionChange: contextProps.onVerticalPositionChange,
829
- onZoomChange: verticalZoom => contextProps.onVerticalZoomChange?.(clamp(verticalZoom / (zoomFactor / clientRect().height), minVerticalZoom(), maxVerticalZoom()))
923
+ position: context.verticalPosition,
924
+ range: 128,
925
+ zoom: context.verticalZoom * (zoomFactor / clientRect().height),
926
+ onPositionChange: context.onVerticalPositionChange,
927
+ onZoomChange: verticalZoom => context.onVerticalZoomChange?.(clamp(verticalZoom / (zoomFactor / clientRect().height), minVerticalZoom(), maxVerticalZoom()))
928
+ }),
929
+ horizontalTracks: () => ({
930
+ pixelOffset: clientRect().left,
931
+ pixelSize: clientRect().width,
932
+ position: 0,
933
+ range: 1,
934
+ zoom: 1
935
+ }),
936
+ verticalTracks: () => ({
937
+ pixelOffset: trackClientRef().top,
938
+ pixelSize: trackClientRef().height,
939
+ position: context.verticalTrackPosition,
940
+ range: context.tracks.length,
941
+ zoom: context.verticalTrackZoom * (zoomFactor / trackClientRef().height),
942
+ onPositionChange: context.onVerticalTrackPositionChange,
943
+ onZoomChange: verticalTrackZoom => context.onVerticalTrackZoomChange?.(clamp(verticalTrackZoom / (zoomFactor / trackClientRef().height), minVerticalTracksZoom(), maxVerticalTracksZoom()))
830
944
  })
831
945
  },
832
946
  get children() {
833
947
  return [(() => {
834
- const _el$3 = _tmpl$$1.cloneNode(true);
948
+ const _el$2 = _tmpl$$1.cloneNode(true),
949
+ _el$3 = _el$2.firstChild;
950
+ insert(_el$2, createComponent(VerticalZoomControl, {
951
+ get min() {
952
+ return minVerticalTracksZoom();
953
+ },
954
+ get max() {
955
+ return maxVerticalTracksZoom();
956
+ },
957
+ dimensionName: "verticalTracks"
958
+ }), _el$3);
835
959
  _el$3.style.setProperty("display", "flex");
836
960
  _el$3.style.setProperty("width", "20%");
837
961
  insert(_el$3, createComponent(Show, {
838
962
  get when() {
839
- return allProps.showTrackList;
963
+ return context.showTrackList;
840
964
  },
841
965
  get children() {
842
- return createComponent(PianoRollTrackList, {});
966
+ return createComponent(ScrollContainer, {
967
+ ref: setTrackScrollerRef,
968
+ horizontalDimensionName: "horizontalTracks",
969
+ verticalDimensionName: "verticalTracks",
970
+ get showScrollbar() {
971
+ return context.mode === "keys";
972
+ },
973
+ get children() {
974
+ return createComponent(PianoRollTrackList, {});
975
+ }
976
+ });
843
977
  }
844
978
  }), null);
845
979
  insert(_el$3, createComponent(Show, {
846
980
  get when() {
847
- return contextProps.mode === "keys";
981
+ return context.mode === "keys";
848
982
  },
849
983
  get children() {
850
984
  return createComponent(PianoRollKeys, {});
851
985
  }
852
986
  }), null);
853
- return _el$3;
854
- })(), createComponent(ScrollContainer, {
855
- ref: setScrollerRef,
856
- get children() {
857
- return [memo(() => allProps.children), createComponent(PianoRollGrid, {}), createComponent(PianoRollNotes, {})];
858
- }
859
- }), createComponent(VerticalZoomControl, {
987
+ insert(_el$2, createComponent(ScrollContainer, {
988
+ ref: setScrollerRef,
989
+ get verticalDimensionName() {
990
+ return context.mode === "keys" ? "vertical" : "verticalTracks";
991
+ },
992
+ get children() {
993
+ return [memo(() => allProps.children), createComponent(PianoRollGrid, {}), createComponent(PianoRollNotes, {})];
994
+ }
995
+ }), null);
996
+ insert(_el$2, createComponent(VerticalZoomControl, {
997
+ get min() {
998
+ return minVerticalZoom();
999
+ },
1000
+ get max() {
1001
+ return maxVerticalZoom();
1002
+ },
1003
+ get disabled() {
1004
+ return context.mode !== "keys";
1005
+ }
1006
+ }), null);
1007
+ effect(() => className(_el$2, styles$5.PianoRollContainer));
1008
+ return _el$2;
1009
+ })(), createComponent(HorizontalZoomControl, {
860
1010
  get min() {
861
- return minVerticalZoom();
1011
+ return minZoom();
862
1012
  },
863
1013
  get max() {
864
- return maxVerticalZoom();
1014
+ return maxZoom();
865
1015
  }
866
1016
  })];
867
1017
  }
868
1018
  }));
869
- insert(_el$, createComponent(HorizontalZoomControl, {
870
- get min() {
871
- return minZoom();
872
- },
873
- get max() {
874
- return maxZoom();
875
- }
876
- }), null);
877
- effect(() => className(_el$2, styles$4.PianoRollContainer));
878
1019
  return _el$;
879
1020
  }
880
1021
  });
@@ -882,7 +1023,7 @@ const PianoRoll = allProps => {
882
1023
 
883
1024
  const _tmpl$ = /*#__PURE__*/template(`<div class="PlayHead"></div>`, 2);
884
1025
  const PlayHead = allProps => {
885
- const propsWithDefauls = mergeProps$1({
1026
+ const propsWithDefauls = mergeProps({
886
1027
  playHeadPosition: 0,
887
1028
  sync: false
888
1029
  }, allProps);
@@ -897,7 +1038,7 @@ const PlayHead = allProps => {
897
1038
  const leftPosition = createMemo(() => viewPort.calculatePixelOffset(props.playHeadPosition));
898
1039
  return (() => {
899
1040
  const _el$ = _tmpl$.cloneNode(true);
900
- spread(_el$, mergeProps(divProps, {
1041
+ spread(_el$, mergeProps$1(divProps, {
901
1042
  get style() {
902
1043
  return {
903
1044
  position: "absolute",
@@ -958,37 +1099,83 @@ const useNotes = () => {
958
1099
  };
959
1100
  };
960
1101
 
961
- const usePianoRoll = () => {
962
- const [ppq, onPpqChange] = createSignal(120);
963
- const [mode, onModeChange] = createSignal("tracks");
964
- const [position, onPositionChange] = createSignal(0);
965
- const [zoom, onZoomChange] = createSignal(10);
966
- const [verticalZoom, onVerticalZoomChange] = createSignal(5);
967
- const [verticalPosition, onVerticalPositionChange] = createSignal(44);
968
- const [gridDivision, onGridDivisionChange] = createSignal(4);
969
- const [snapToGrid, onSnapToGridChange] = createSignal(true);
970
- const [duration, onDurationChange] = createSignal(0);
971
- return {
972
- ppq,
1102
+ const createPianoRollstate = defaultState => {
1103
+ const [state, setState] = createStore({
1104
+ ppq: 0,
1105
+ mode: "keys",
1106
+ position: 0,
1107
+ zoom: 10,
1108
+ verticalZoom: 5,
1109
+ verticalPosition: 44,
1110
+ verticalTrackZoom: 0.5,
1111
+ verticalTrackPosition: 0,
1112
+ gridDivision: 4,
1113
+ snapToGrid: true,
1114
+ duration: 0,
1115
+ tracks: [],
1116
+ selectedTrackIndex: 0,
1117
+ pressedKeys: [],
1118
+ ...defaultState
1119
+ });
1120
+ const updateNotes = async (trackIndex, getNotes) => {
1121
+ const track = state.tracks[trackIndex];
1122
+ if (!track) return;
1123
+ const notes = track.notes;
1124
+ onTracksChange([...state.tracks.slice(0, trackIndex), {
1125
+ ...track,
1126
+ notes: getNotes(notes)
1127
+ }, ...state.tracks.slice(trackIndex + 1)]);
1128
+ };
1129
+ const onNoteChange = (trackIndex, noteIndex, note) => {
1130
+ updateNotes(trackIndex, notes => [...notes.slice(0, noteIndex), note, ...notes.slice(noteIndex + 1)]);
1131
+ };
1132
+ const onInsertNote = (trackIndex, note) => {
1133
+ const track = state.tracks[trackIndex];
1134
+ if (!track) return -1;
1135
+ const notes = track.notes;
1136
+ const newNoteIndex = Math.max(notes.findIndex(({
1137
+ ticks
1138
+ }) => ticks > note.ticks), 0);
1139
+ updateNotes(trackIndex, notes => [...notes.slice(0, newNoteIndex), note, ...notes.slice(newNoteIndex)]);
1140
+ return newNoteIndex;
1141
+ };
1142
+ const onRemoveNote = (trackIndex, noteIndex) => {
1143
+ updateNotes(trackIndex, notes => [...notes.slice(0, noteIndex), ...notes.slice(noteIndex + 1)]);
1144
+ };
1145
+ const onPpqChange = ppq => setState("ppq", ppq);
1146
+ const onModeChange = mode => setState("mode", mode);
1147
+ const onPositionChange = position => setState("position", position);
1148
+ const onZoomChange = zoom => setState("zoom", zoom);
1149
+ const onVerticalZoomChange = verticalZoom => setState("verticalZoom", verticalZoom);
1150
+ const onVerticalPositionChange = verticalPosition => setState("verticalPosition", verticalPosition);
1151
+ const onVerticalTrackZoomChange = verticalTrackZoom => setState("verticalTrackZoom", verticalTrackZoom);
1152
+ const onVerticalTrackPositionChange = verticalTrackPosition => setState("verticalTrackPosition", verticalTrackPosition);
1153
+ const onGridDivisionChange = gridDivision => setState("gridDivision", gridDivision);
1154
+ const onSnapToGridChange = snapToGrid => setState("snapToGrid", snapToGrid);
1155
+ const onDurationChange = duration => setState("duration", duration);
1156
+ const onTracksChange = tracks => setState("tracks", tracks);
1157
+ const onSelectedTrackIndexChange = selectedTrackIndex => setState("selectedTrackIndex", selectedTrackIndex);
1158
+ const onPressedKeysChange = pressedKeys => setState("pressedKeys", pressedKeys);
1159
+ return mergeProps(state, {
973
1160
  onPpqChange,
974
- position,
1161
+ onModeChange,
975
1162
  onPositionChange,
976
- zoom,
977
1163
  onZoomChange,
978
- verticalPosition,
979
- onVerticalPositionChange,
980
- verticalZoom,
981
1164
  onVerticalZoomChange,
982
- gridDivision,
1165
+ onVerticalPositionChange,
1166
+ onVerticalTrackZoomChange,
1167
+ onVerticalTrackPositionChange,
983
1168
  onGridDivisionChange,
984
- snapToGrid,
985
1169
  onSnapToGridChange,
986
- mode,
987
- onModeChange,
988
- duration,
989
- onDurationChange
990
- };
1170
+ onDurationChange,
1171
+ onTracksChange,
1172
+ onNoteChange,
1173
+ onInsertNote,
1174
+ onRemoveNote,
1175
+ onSelectedTrackIndexChange,
1176
+ onPressedKeysChange
1177
+ });
991
1178
  };
992
1179
 
993
- export { PianoRoll, PlayHead, useNotes, usePianoRoll };
1180
+ export { PianoRoll, PlayHead, createPianoRollstate, useNotes };
994
1181
  //# sourceMappingURL=index.js.map