solid-pianoroll 0.0.2 → 0.0.4

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,63 @@
1
- import { delegateEvents, spread, mergeProps, insert, createComponent, effect, setAttribute, memo, use, style, template } from 'solid-js/web';
2
- import { createMemo, splitProps, createSignal, createEffect, onMount, onCleanup, Index, Show } from 'solid-js';
1
+ import { createComponent, delegateEvents, effect, template, insert, className, setAttribute, use, memo, style, spread, mergeProps } from 'solid-js/web';
2
+ import { createSignal, createEffect, createMemo, createContext, splitProps, useContext, Index, Show } from 'solid-js';
3
+ import { createStore } from 'solid-js/store';
4
+
5
+ function styleInject(css, ref) {
6
+ if (ref === void 0) ref = {};
7
+ var insertAt = ref.insertAt;
8
+ if (!css || typeof document === 'undefined') {
9
+ return;
10
+ }
11
+ var head = document.head || document.getElementsByTagName('head')[0];
12
+ var style = document.createElement('style');
13
+ style.type = 'text/css';
14
+ if (insertAt === 'top') {
15
+ if (head.firstChild) {
16
+ head.insertBefore(style, head.firstChild);
17
+ } else {
18
+ head.appendChild(style);
19
+ }
20
+ } else {
21
+ head.appendChild(style);
22
+ }
23
+ if (style.styleSheet) {
24
+ style.styleSheet.cssText = css;
25
+ } else {
26
+ style.appendChild(document.createTextNode(css));
27
+ }
28
+ }
29
+
30
+ var css_248z$1 = ".PianoRoll-module_PianoRoll__rkRYx {\n box-sizing: border-box;\n display: flex;\n overflow: hidden;\n flex-direction: column;\n padding: 16px;\n}\n\n.PianoRoll-module_PianoRollContainer__Oyxr7 {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n\n.PianoRoll-module_PianoRollInnerContainer__9wdIQ {\n position: relative;\n display: flex;\n overflow: hidden;\n flex: 1;\n}\n";
31
+ var styles$1 = {"PianoRoll":"PianoRoll-module_PianoRoll__rkRYx","PianoRollContainer":"PianoRoll-module_PianoRollContainer__Oyxr7","PianoRollInnerContainer":"PianoRoll-module_PianoRollInnerContainer__9wdIQ"};
32
+ styleInject(css_248z$1);
33
+
34
+ const defaultRect = {
35
+ left: 0,
36
+ width: 0,
37
+ top: 0,
38
+ height: 0,
39
+ bottom: 0,
40
+ right: 0,
41
+ x: 0,
42
+ y: 0
43
+ };
44
+ function useBoundingClientRect(containerRef) {
45
+ const [boundingClientRect, setBoundingClientRect] = createSignal(defaultRect);
46
+ createEffect(() => {
47
+ const container = containerRef();
48
+ if (!container) return;
49
+ const updateBoundingClientRect = () => {
50
+ setBoundingClientRect(container.getBoundingClientRect() ?? defaultRect);
51
+ };
52
+ const resizeObserver = new ResizeObserver(updateBoundingClientRect);
53
+ resizeObserver.observe(container);
54
+ updateBoundingClientRect();
55
+ return () => {
56
+ resizeObserver.disconnect();
57
+ };
58
+ });
59
+ return boundingClientRect;
60
+ }
3
61
 
4
62
  const clamp = (value, min, max) => {
5
63
  return Math.max(min, Math.min(max, value));
@@ -38,308 +96,355 @@ function useViewPortScaler(getState) {
38
96
  };
39
97
  }
40
98
 
41
- const _tmpl$ = /*#__PURE__*/template(`<div><div><div class="PianoRoll-Keys"></div><div><div class="PianoRoll-Notes-Container"><div class="PianoRoll-Notes"></div></div><div class="PianoRoll-Vertical-Scroller"><div></div></div></div><input type="range" min="1" max="11" step="0.01"></div><input type="range" max="500" min="1" step="0.01"></div>`, 18),
42
- _tmpl$2 = /*#__PURE__*/template(`<div class="PianoRoll-Key"></div>`, 2),
43
- _tmpl$3 = /*#__PURE__*/template(`<div class="PianoRoll-Note"></div>`, 2);
44
- const PianoRoll = allProps => {
45
- let scrollContainerRef;
46
- let scrollContentRef;
47
- let notesContainerRef;
48
- const [props, divProps] = splitProps(allProps, ["ppq", "notes", "position", "duration", "zoom", "verticalPosition", "verticalZoom", "onVerticalZoomChange", "onVerticalPositionChange", "onZoomChange", "onPositionChange", "onNoteChange"]);
49
- const [dimensions, setDimensions] = createSignal({
50
- left: 0,
51
- width: 0,
52
- top: 0,
53
- height: 0
54
- });
55
- const resizeObserver = new ResizeObserver(event => {
56
- const {
57
- left,
58
- top,
59
- width,
60
- height
61
- } = scrollContainerRef?.getBoundingClientRect() ?? {
62
- left: 0,
63
- width: 0,
64
- top: 0,
65
- height: 0
66
- };
67
- setDimensions({
68
- left,
69
- top,
70
- width,
71
- height
72
- });
73
- });
74
- createEffect(() => {
75
- console.log(props.notes);
76
- });
77
- onMount(() => {
78
- if (!scrollContainerRef) return;
79
- resizeObserver.observe(scrollContainerRef);
80
- });
81
- onCleanup(() => {
82
- resizeObserver.disconnect();
83
- });
99
+ const PianoRollContext = createContext(undefined);
100
+ const PianoRollContextProvider = props => {
101
+ const [noteDragMode, setNoteDragMode] = createSignal();
102
+ const [isDragging, setIsDragging] = createSignal(false);
103
+ const [_ownProps, contextProps] = splitProps(props, ["children", "scrollContainer"]);
104
+ const clientRect = useBoundingClientRect(() => props.scrollContainer);
84
105
  const horizontalViewPort = useViewPortScaler(() => ({
85
- viewPortOffset: dimensions().left,
86
- viewPortSize: dimensions().width,
106
+ viewPortOffset: clientRect().left,
107
+ viewPortSize: clientRect().width,
87
108
  virtualPosition: props.position,
88
109
  virtualRange: props.duration,
89
110
  zoom: props.zoom
90
111
  }));
91
112
  const verticalViewPort = useViewPortScaler(() => ({
92
- viewPortOffset: dimensions().top,
93
- viewPortSize: dimensions().height,
113
+ viewPortOffset: clientRect().top,
114
+ viewPortSize: clientRect().height,
94
115
  virtualPosition: props.verticalPosition,
95
- virtualRange: 128,
96
- zoom: props.verticalZoom
116
+ zoom: props.verticalZoom,
117
+ virtualRange: 128
97
118
  }));
98
- const handleScroll = event => {
99
- event.preventDefault();
100
- if (didUpdateScroll) {
101
- didUpdateScroll = false;
102
- return;
103
- }
104
- const maxVerticalPosition = 128 - 128 / props.verticalZoom;
105
- const maxPosition = props.duration - props.duration / props.zoom;
106
- const {
107
- width,
108
- height
109
- } = dimensions();
110
- const {
111
- scrollTop,
112
- scrollLeft,
113
- scrollWidth,
114
- scrollHeight
115
- } = event.currentTarget;
116
- const scrollTopAmount = scrollTop / (scrollHeight - height);
117
- const scrollLeftAmount = scrollLeft / (scrollWidth - width);
118
- props.onVerticalPositionChange?.(maxVerticalPosition * scrollTopAmount);
119
- props.onPositionChange?.(maxPosition * scrollLeftAmount);
119
+ const getContextValue = () => {
120
+ return {
121
+ ...contextProps,
122
+ horizontalViewPort,
123
+ verticalViewPort,
124
+ clientRect: clientRect(),
125
+ isDragging: isDragging(),
126
+ onIsDraggingChange: setIsDragging,
127
+ noteDragMode: noteDragMode(),
128
+ onNoteDragModeChange: setNoteDragMode
129
+ };
120
130
  };
121
- let didUpdateScroll = false;
131
+ const [contextValue, setContextValue] = createStore(getContextValue());
122
132
  createEffect(() => {
123
- const maxVerticalPosition = 128 - 128 / props.verticalZoom;
124
- const maxPosition = props.duration - props.duration / props.zoom;
125
- const scrollTopAmount = maxVerticalPosition > 0 ? props.verticalPosition / maxVerticalPosition : 0;
126
- const scrollLeftAmount = maxPosition > 0 ? props.position / maxPosition : 0;
127
- const {
128
- height,
129
- width
130
- } = dimensions();
131
- if (!scrollContentRef?.parentElement) return;
132
- const scrollDivHeight = clamp(props.verticalZoom * height, height, 10000);
133
- const scrollTop = scrollTopAmount * (scrollDivHeight - height);
134
- const scrollDivWidth = clamp(props.zoom * width, width, 10000);
135
- const scrollLeft = scrollLeftAmount * (scrollDivWidth - width);
136
- didUpdateScroll = true;
137
- scrollContentRef.style.height = `${scrollDivHeight}px`;
138
- scrollContentRef.style.width = `${scrollDivWidth}px`;
139
- scrollContentRef.parentElement.scrollTo({
140
- left: scrollLeft,
141
- top: scrollTop
142
- });
133
+ setContextValue(getContextValue());
143
134
  });
144
- const forwardEventToNote = event => {
145
- const x = "clientX" in event ? event.clientX : event.touches[0]?.clientX;
146
- const y = "clientY" in event ? event.clientY : event.touches[0]?.clientY;
147
- const elementUnderMouse = [...(notesContainerRef?.querySelectorAll?.("div") ?? [])].find(element => {
148
- const rect = element.getBoundingClientRect();
149
- return x >= rect.left && rect.left + rect.width > x && y >= rect.top && rect.top + rect.height > y;
150
- });
151
- if (elementUnderMouse) {
152
- return elementUnderMouse.dispatchEvent(new MouseEvent(event.type, event));
135
+ return createComponent(PianoRollContext.Provider, {
136
+ value: contextValue,
137
+ get children() {
138
+ return props.children;
153
139
  }
154
- };
155
- const [noteDragMode, setNoteDragMode] = createSignal();
156
- const [isDragging, setIsDragging] = createSignal(false);
140
+ });
141
+ };
142
+ const usePianoRollContext = () => {
143
+ const context = useContext(PianoRollContext);
144
+ if (!context) throw new Error("No PianoRollContext found");
145
+ return context;
146
+ };
147
+
148
+ const _tmpl$$6 = /*#__PURE__*/template(`<input type="range" max="500" min="1" step="0.01">`, 1);
149
+ const HorizontalZoomControl = () => {
150
+ const context = usePianoRollContext();
157
151
  return (() => {
158
- const _el$ = _tmpl$.cloneNode(true),
159
- _el$2 = _el$.firstChild,
160
- _el$3 = _el$2.firstChild,
161
- _el$4 = _el$3.nextSibling,
162
- _el$5 = _el$4.firstChild,
163
- _el$6 = _el$5.firstChild,
164
- _el$7 = _el$5.nextSibling,
165
- _el$8 = _el$7.firstChild,
166
- _el$9 = _el$4.nextSibling,
167
- _el$10 = _el$2.nextSibling;
168
- spread(_el$, mergeProps(divProps, {
169
- "class": "PianoRoll",
170
- get style() {
171
- return {
172
- "box-sizing": "border-box",
173
- display: "flex",
174
- overflow: "hidden",
175
- "flex-direction": "column",
176
- padding: "16px",
177
- ...(typeof divProps.style === "object" && divProps.style)
178
- };
179
- }
180
- }), false, true);
181
- _el$2.style.setProperty("overflow", "hidden");
182
- _el$2.style.setProperty("height", "100%");
183
- _el$2.style.setProperty("display", "flex");
184
- _el$2.style.setProperty("flex-direction", "row");
185
- _el$3.style.setProperty("position", "relative");
186
- _el$3.style.setProperty("height", "100%");
187
- _el$3.style.setProperty("width", "50px");
188
- insert(_el$3, createComponent(Index, {
152
+ const _el$ = _tmpl$$6.cloneNode(true);
153
+ _el$.$$input = event => context.onZoomChange?.(event.currentTarget.valueAsNumber);
154
+ _el$.style.setProperty("margin-left", "50px");
155
+ _el$.style.setProperty("margin-right", "16px");
156
+ effect(() => _el$.value = context.zoom);
157
+ return _el$;
158
+ })();
159
+ };
160
+ delegateEvents(["input"]);
161
+
162
+ var css_248z = ".PianoRollKeys-module_PianoRollKeys__5vnQ0 {\n position: relative;\n height: 100%;\n width: 50px;\n}\n\n.PianoRollKeys-module_PianoRollKeys-Key__xunQt {\n position: absolute;\n box-sizing: border-box;\n left: 0;\n width: 100%;\n border-color: #000;\n border-style: solid;\n}\n";
163
+ var styles = {"PianoRollKeys":"PianoRollKeys-module_PianoRollKeys__5vnQ0","PianoRollKeys-Key":"PianoRollKeys-module_PianoRollKeys-Key__xunQt"};
164
+ styleInject(css_248z);
165
+
166
+ const _tmpl$$5 = /*#__PURE__*/template(`<div></div>`, 2);
167
+ const PianoRollKeys = () => {
168
+ const context = usePianoRollContext();
169
+ return (() => {
170
+ const _el$ = _tmpl$$5.cloneNode(true);
171
+ insert(_el$, createComponent(Index, {
189
172
  each: keys,
190
173
  children: key => {
191
- const virtualDimensions = createMemo(() => verticalViewPort.getVirtualDimensions(127 - key().number, 1));
174
+ const virtualDimensions = createMemo(() => context.verticalViewPort.getVirtualDimensions(127 - key().number, 1));
192
175
  return createComponent(Show, {
193
176
  get when() {
194
177
  return virtualDimensions().size > 0;
195
178
  },
196
179
  get children() {
197
- const _el$11 = _tmpl$2.cloneNode(true);
198
- _el$11.style.setProperty("position", "absolute");
199
- _el$11.style.setProperty("box-sizing", "border-box");
200
- _el$11.style.setProperty("left", "0");
201
- _el$11.style.setProperty("width", "100%");
202
- _el$11.style.setProperty("border-color", "#000");
203
- _el$11.style.setProperty("border-style", "solid");
180
+ const _el$2 = _tmpl$$5.cloneNode(true);
204
181
  effect(_p$ => {
205
- const _v$5 = key().name,
206
- _v$6 = key().number % 12,
207
- _v$7 = `${virtualDimensions().offset}px`,
208
- _v$8 = `${virtualDimensions().size}px`,
209
- _v$9 = key().isBlack ? "#000" : "#fff",
210
- _v$10 = `${!key().isBlack && !blackKeys.includes((key().number + 1) % 12) ? "0.1px" : 0} 1px ${!key().isBlack && !blackKeys.includes((key().number - 1) % 12) ? "0.1px" : 0} 0`;
211
- _v$5 !== _p$._v$5 && setAttribute(_el$11, "title", _p$._v$5 = _v$5);
212
- _v$6 !== _p$._v$6 && setAttribute(_el$11, "data-index", _p$._v$6 = _v$6);
213
- _v$7 !== _p$._v$7 && _el$11.style.setProperty("top", _p$._v$7 = _v$7);
214
- _v$8 !== _p$._v$8 && _el$11.style.setProperty("height", _p$._v$8 = _v$8);
215
- _v$9 !== _p$._v$9 && _el$11.style.setProperty("background-color", _p$._v$9 = _v$9);
216
- _v$10 !== _p$._v$10 && _el$11.style.setProperty("border-width", _p$._v$10 = _v$10);
182
+ const _v$ = styles["PianoRollKeys-Key"],
183
+ _v$2 = key().name,
184
+ _v$3 = key().number % 12,
185
+ _v$4 = `${virtualDimensions().offset}px`,
186
+ _v$5 = `${virtualDimensions().size}px`,
187
+ _v$6 = key().isBlack ? "#000" : "#fff",
188
+ _v$7 = `${!key().isBlack && !blackKeys.includes((key().number + 1) % 12) ? "0.1px" : 0} 1px ${!key().isBlack && !blackKeys.includes((key().number - 1) % 12) ? "0.1px" : 0} 0`;
189
+ _v$ !== _p$._v$ && className(_el$2, _p$._v$ = _v$);
190
+ _v$2 !== _p$._v$2 && setAttribute(_el$2, "title", _p$._v$2 = _v$2);
191
+ _v$3 !== _p$._v$3 && setAttribute(_el$2, "data-index", _p$._v$3 = _v$3);
192
+ _v$4 !== _p$._v$4 && _el$2.style.setProperty("top", _p$._v$4 = _v$4);
193
+ _v$5 !== _p$._v$5 && _el$2.style.setProperty("height", _p$._v$5 = _v$5);
194
+ _v$6 !== _p$._v$6 && _el$2.style.setProperty("background-color", _p$._v$6 = _v$6);
195
+ _v$7 !== _p$._v$7 && _el$2.style.setProperty("border-width", _p$._v$7 = _v$7);
217
196
  return _p$;
218
197
  }, {
198
+ _v$: undefined,
199
+ _v$2: undefined,
200
+ _v$3: undefined,
201
+ _v$4: undefined,
219
202
  _v$5: undefined,
220
203
  _v$6: undefined,
221
- _v$7: undefined,
222
- _v$8: undefined,
223
- _v$9: undefined,
224
- _v$10: undefined
204
+ _v$7: undefined
225
205
  });
226
- return _el$11;
206
+ return _el$2;
227
207
  }
228
208
  });
229
209
  }
230
210
  }));
231
- _el$4.style.setProperty("position", "relative");
232
- _el$4.style.setProperty("display", "flex");
233
- _el$4.style.setProperty("overflow", "hidden");
234
- _el$4.style.setProperty("flex", "1");
235
- _el$5.style.setProperty("position", "absolute");
236
- const _ref$ = notesContainerRef;
237
- typeof _ref$ === "function" ? use(_ref$, _el$6) : notesContainerRef = _el$6;
238
- _el$6.style.setProperty("position", "relative");
239
- _el$6.style.setProperty("height", "100%");
240
- insert(_el$6, createComponent(Index, {
211
+ effect(() => className(_el$, styles.PianoRollKeys));
212
+ return _el$;
213
+ })();
214
+ };
215
+ const blackKeys = [1, 3, 6, 8, 10];
216
+ const keyNames = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
217
+ const keys = Array.from({
218
+ length: 128
219
+ }).map((_, index) => ({
220
+ number: index,
221
+ name: `${keyNames[index % 12]} ${Math.floor(index / 12) - 2}`,
222
+ isBlack: blackKeys.includes(index % 12)
223
+ }));
224
+
225
+ const _tmpl$$4 = /*#__PURE__*/template(`<div class="PianoRoll-Notes-Container"><div class="PianoRoll-Notes"></div></div>`, 4),
226
+ _tmpl$2$1 = /*#__PURE__*/template(`<div class="PianoRoll-Note"></div>`, 2);
227
+ const PianoRollNotes = props => {
228
+ const context = usePianoRollContext();
229
+ const gridDivisionTicks = createMemo(() => context.ppq * 4 / context.gridDivision);
230
+ const snapValueToGridIfEnabled = (value, altKey) => context.snapToGrid && !altKey ? Math.round(value / gridDivisionTicks()) * gridDivisionTicks() : value;
231
+ return (() => {
232
+ const _el$ = _tmpl$$4.cloneNode(true),
233
+ _el$2 = _el$.firstChild;
234
+ _el$.style.setProperty("position", "absolute");
235
+ const _ref$ = props.ref;
236
+ typeof _ref$ === "function" ? use(_ref$, _el$2) : props.ref = _el$2;
237
+ _el$2.style.setProperty("position", "relative");
238
+ _el$2.style.setProperty("height", "100%");
239
+ insert(_el$2, createComponent(Index, {
241
240
  get each() {
242
- return props.notes;
241
+ return context.notes;
243
242
  },
244
243
  children: (note, index) => {
245
- const horizontalVirtualDimensions = createMemo(() => verticalViewPort.getVirtualDimensions(127 - note().midi, 1));
246
- const verticalVirtualDimensions = createMemo(() => horizontalViewPort.getVirtualDimensions(note().ticks, note().durationTicks));
244
+ const horizontalVirtualDimensions = createMemo(() => context.verticalViewPort.getVirtualDimensions(127 - note().midi, 1));
245
+ const verticalVirtualDimensions = createMemo(() => context.horizontalViewPort.getVirtualDimensions(note().ticks, note().durationTicks));
247
246
  return createComponent(Show, {
248
247
  get when() {
249
248
  return memo(() => !!!!horizontalVirtualDimensions().size)() && !!verticalVirtualDimensions().size;
250
249
  },
251
250
  get children() {
252
- const _el$12 = _tmpl$3.cloneNode(true);
253
- _el$12.$$mousedown = event => {
254
- setIsDragging(true);
255
- const initialPosition = horizontalViewPort.getPosition(event.clientX);
251
+ const _el$3 = _tmpl$2$1.cloneNode(true);
252
+ _el$3.$$mousedown = event => {
253
+ context.onIsDraggingChange(true);
254
+ const initialPosition = context.horizontalViewPort.getPosition(event.clientX);
256
255
  const diffPosition = initialPosition - note().ticks;
257
256
  const handleMouseMove = mouseMoveEvent => {
258
- const ticks = Math.max(horizontalViewPort.getPosition(mouseMoveEvent.clientX) - diffPosition, 0);
259
- props.onNoteChange?.(index, {
257
+ const ticks = snapValueToGridIfEnabled(Math.max(context.horizontalViewPort.getPosition(mouseMoveEvent.clientX) - diffPosition, 0), mouseMoveEvent.altKey);
258
+ context.onNoteChange?.(index, {
260
259
  ...note(),
261
- midi: Math.round(127 - verticalViewPort.getPosition(mouseMoveEvent.clientY)),
262
- ...((noteDragMode() === "move" || noteDragMode() === "trimStart") && {
260
+ ...(context.noteDragMode === "move" && {
261
+ midi: Math.round(127 - context.verticalViewPort.getPosition(mouseMoveEvent.clientY))
262
+ }),
263
+ ...((context.noteDragMode === "move" || context.noteDragMode === "trimStart") && {
263
264
  ticks
264
265
  }),
265
- ...(noteDragMode() === "trimStart" && {
266
+ ...(context.noteDragMode === "trimStart" && {
266
267
  durationTicks: note().durationTicks + note().ticks - ticks
267
268
  }),
268
- ...(noteDragMode() === "trimEnd" && {
269
- durationTicks: horizontalViewPort.getPosition(mouseMoveEvent.clientX) - note().ticks
269
+ ...(context.noteDragMode === "trimEnd" && {
270
+ durationTicks: snapValueToGridIfEnabled(context.horizontalViewPort.getPosition(mouseMoveEvent.clientX) - note().ticks, mouseMoveEvent.altKey)
270
271
  })
271
272
  });
272
273
  };
273
274
  const handleMouseUp = () => {
274
- setIsDragging(false);
275
+ context.onIsDraggingChange(false);
275
276
  window.removeEventListener("mousemove", handleMouseMove);
276
277
  window.removeEventListener("mouseup", handleMouseUp);
277
278
  };
278
279
  window.addEventListener("mousemove", handleMouseMove);
279
280
  window.addEventListener("mouseup", handleMouseUp);
280
281
  };
281
- _el$12.$$mousemove = event => {
282
- if (isDragging()) return;
283
- const relativeX = horizontalViewPort.getScaledValue(horizontalViewPort.getPosition(event.clientX));
284
- const noteStartX = horizontalViewPort.getScaledValue(note().ticks);
285
- const noteEndX = horizontalViewPort.getScaledValue(note().ticks + note().durationTicks);
286
- setNoteDragMode(relativeX - noteStartX < 3 ? "trimStart" : noteEndX - relativeX < 3 ? "trimEnd" : "move");
282
+ _el$3.$$mousemove = event => {
283
+ if (context.isDragging) return;
284
+ const relativeX = context.horizontalViewPort.getScaledValue(context.horizontalViewPort.getPosition(event.clientX));
285
+ const noteStartX = context.horizontalViewPort.getScaledValue(note().ticks);
286
+ const noteEndX = context.horizontalViewPort.getScaledValue(note().ticks + note().durationTicks);
287
+ context.onNoteDragModeChange(relativeX - noteStartX < 3 ? "trimStart" : noteEndX - relativeX < 3 ? "trimEnd" : "move");
287
288
  };
288
- _el$12.style.setProperty("z-index", "1");
289
- _el$12.style.setProperty("position", "absolute");
290
- _el$12.style.setProperty("box-sizing", "border-box");
291
- _el$12.style.setProperty("border-width", "0.5px");
292
- _el$12.style.setProperty("border-style", "solid");
293
- _el$12.style.setProperty("background-color", "#f00");
294
- _el$12.style.setProperty("border-color", "#a00");
289
+ _el$3.style.setProperty("z-index", "2");
290
+ _el$3.style.setProperty("position", "absolute");
291
+ _el$3.style.setProperty("box-sizing", "border-box");
292
+ _el$3.style.setProperty("border-width", "0.5px");
293
+ _el$3.style.setProperty("border-style", "solid");
294
+ _el$3.style.setProperty("border-color", "#a00");
295
295
  effect(_p$ => {
296
- const _v$11 = `${horizontalVirtualDimensions().offset}px`,
297
- _v$12 = `${horizontalVirtualDimensions().size}px`,
298
- _v$13 = `${verticalVirtualDimensions().offset}px`,
299
- _v$14 = `${verticalVirtualDimensions().size}px`;
300
- _v$11 !== _p$._v$11 && _el$12.style.setProperty("top", _p$._v$11 = _v$11);
301
- _v$12 !== _p$._v$12 && _el$12.style.setProperty("height", _p$._v$12 = _v$12);
302
- _v$13 !== _p$._v$13 && _el$12.style.setProperty("left", _p$._v$13 = _v$13);
303
- _v$14 !== _p$._v$14 && _el$12.style.setProperty("width", _p$._v$14 = _v$14);
296
+ const _v$4 = `${horizontalVirtualDimensions().offset}px`,
297
+ _v$5 = `${horizontalVirtualDimensions().size}px`,
298
+ _v$6 = `${verticalVirtualDimensions().offset}px`,
299
+ _v$7 = `${verticalVirtualDimensions().size}px`,
300
+ _v$8 = `rgba(255,0,0, ${(128 + note().velocity) / 256})`;
301
+ _v$4 !== _p$._v$4 && _el$3.style.setProperty("top", _p$._v$4 = _v$4);
302
+ _v$5 !== _p$._v$5 && _el$3.style.setProperty("height", _p$._v$5 = _v$5);
303
+ _v$6 !== _p$._v$6 && _el$3.style.setProperty("left", _p$._v$6 = _v$6);
304
+ _v$7 !== _p$._v$7 && _el$3.style.setProperty("width", _p$._v$7 = _v$7);
305
+ _v$8 !== _p$._v$8 && _el$3.style.setProperty("background-color", _p$._v$8 = _v$8);
304
306
  return _p$;
305
307
  }, {
306
- _v$11: undefined,
307
- _v$12: undefined,
308
- _v$13: undefined,
309
- _v$14: undefined
308
+ _v$4: undefined,
309
+ _v$5: undefined,
310
+ _v$6: undefined,
311
+ _v$7: undefined,
312
+ _v$8: undefined
310
313
  });
311
- return _el$12;
314
+ return _el$3;
312
315
  }
313
316
  });
314
317
  }
315
318
  }));
316
- _el$7.addEventListener("dragend", forwardEventToNote);
317
- _el$7.addEventListener("drag", forwardEventToNote);
318
- _el$7.addEventListener("dragstart", forwardEventToNote);
319
- _el$7.addEventListener("touchcancel", forwardEventToNote);
320
- _el$7.$$touchend = forwardEventToNote;
321
- _el$7.$$touchmove = forwardEventToNote;
322
- _el$7.$$touchstart = forwardEventToNote;
323
- _el$7.$$mouseup = forwardEventToNote;
324
- _el$7.$$dblclick = event => {
319
+ effect(_p$ => {
320
+ const _v$ = `${context.clientRect.width}px`,
321
+ _v$2 = `${context.clientRect.height}px`,
322
+ _v$3 = `${context.clientRect.width}px`;
323
+ _v$ !== _p$._v$ && _el$.style.setProperty("width", _p$._v$ = _v$);
324
+ _v$2 !== _p$._v$2 && _el$.style.setProperty("height", _p$._v$2 = _v$2);
325
+ _v$3 !== _p$._v$3 && _el$2.style.setProperty("width", _p$._v$3 = _v$3);
326
+ return _p$;
327
+ }, {
328
+ _v$: undefined,
329
+ _v$2: undefined,
330
+ _v$3: undefined
331
+ });
332
+ return _el$;
333
+ })();
334
+ };
335
+ delegateEvents(["mousemove", "mousedown"]);
336
+
337
+ const _tmpl$$3 = /*#__PURE__*/template(`<div class="PianoRoll-Vertical-Scroller"><div></div></div>`, 4);
338
+ const ScrollContainer = props => {
339
+ let scrollContentRef;
340
+ const context = usePianoRollContext();
341
+ const forwardEventToNote = event => {
342
+ const x = "clientX" in event ? event.clientX : event.touches[0]?.clientX;
343
+ const y = "clientY" in event ? event.clientY : event.touches[0]?.clientY;
344
+ const elementUnderMouse = [...(context.notesContainer?.querySelectorAll?.("div") ?? [])].find(element => {
345
+ const rect = element.getBoundingClientRect();
346
+ return x >= rect.left && rect.left + rect.width > x && y >= rect.top && rect.top + rect.height > y;
347
+ });
348
+ if (elementUnderMouse) {
349
+ return elementUnderMouse.dispatchEvent(new MouseEvent(event.type, event));
350
+ }
351
+ };
352
+ const handleScroll = event => {
353
+ event.preventDefault();
354
+ if (didUpdateScroll) {
355
+ didUpdateScroll = false;
356
+ return;
357
+ }
358
+ const maxVerticalPosition = 128 - 128 / context.verticalZoom;
359
+ const maxPosition = context.duration - context.duration / context.zoom;
360
+ const {
361
+ width,
362
+ height
363
+ } = context.clientRect;
364
+ const {
365
+ scrollTop,
366
+ scrollLeft,
367
+ scrollWidth,
368
+ scrollHeight
369
+ } = event.currentTarget;
370
+ const scrollTopAmount = scrollTop / (scrollHeight - height);
371
+ const scrollLeftAmount = scrollLeft / (scrollWidth - width);
372
+ context.onVerticalPositionChange?.(maxVerticalPosition * scrollTopAmount);
373
+ context.onPositionChange?.(maxPosition * scrollLeftAmount);
374
+ };
375
+ let didUpdateScroll = false;
376
+ createEffect(() => {
377
+ const maxVerticalPosition = 128 - 128 / context.verticalZoom;
378
+ const maxPosition = context.duration - context.duration / context.zoom;
379
+ const scrollTopAmount = maxVerticalPosition > 0 ? context.verticalPosition / maxVerticalPosition : 0;
380
+ const scrollLeftAmount = maxPosition > 0 ? context.position / maxPosition : 0;
381
+ const {
382
+ height,
383
+ width
384
+ } = context.clientRect;
385
+ if (!scrollContentRef?.parentElement) return;
386
+ const scrollDivHeight = clamp(context.verticalZoom * height, height, 10000);
387
+ const scrollTop = scrollTopAmount * (scrollDivHeight - height);
388
+ const scrollDivWidth = clamp(context.zoom * width, width, 10000);
389
+ const scrollLeft = scrollLeftAmount * (scrollDivWidth - width);
390
+ didUpdateScroll = true;
391
+ scrollContentRef.style.height = `${scrollDivHeight}px`;
392
+ scrollContentRef.style.width = `${scrollDivWidth}px`;
393
+ scrollContentRef.parentElement.scrollTo({
394
+ left: scrollLeft,
395
+ top: scrollTop
396
+ });
397
+ });
398
+ return (() => {
399
+ const _el$ = _tmpl$$3.cloneNode(true),
400
+ _el$2 = _el$.firstChild;
401
+ _el$.addEventListener("dragend", forwardEventToNote);
402
+ _el$.addEventListener("drag", forwardEventToNote);
403
+ _el$.addEventListener("dragstart", forwardEventToNote);
404
+ _el$.addEventListener("touchcancel", forwardEventToNote);
405
+ _el$.$$touchend = forwardEventToNote;
406
+ _el$.$$touchmove = forwardEventToNote;
407
+ _el$.$$touchstart = forwardEventToNote;
408
+ _el$.$$mouseup = forwardEventToNote;
409
+ _el$.$$dblclick = event => {
325
410
  if (!forwardEventToNote(event)) ;
326
411
  };
327
- _el$7.$$click = forwardEventToNote;
328
- _el$7.$$mousemove = event => {
412
+ _el$.$$click = forwardEventToNote;
413
+ _el$.$$mousemove = event => {
329
414
  if (forwardEventToNote(event)) return;
330
- if (isDragging()) return;
331
- setNoteDragMode(undefined);
415
+ if (context.isDragging) return;
416
+ context.onNoteDragModeChange(undefined);
332
417
  };
333
- _el$7.$$mousedown = forwardEventToNote;
334
- _el$7.addEventListener("scroll", handleScroll);
335
- const _ref$2 = scrollContainerRef;
336
- typeof _ref$2 === "function" ? use(_ref$2, _el$7) : scrollContainerRef = _el$7;
337
- const _ref$3 = scrollContentRef;
338
- typeof _ref$3 === "function" ? use(_ref$3, _el$8) : scrollContentRef = _el$8;
339
- _el$9.$$input = event => props.onVerticalZoomChange?.(event.currentTarget.valueAsNumber);
340
- spread(_el$9, mergeProps({
418
+ _el$.$$mousedown = forwardEventToNote;
419
+ _el$.addEventListener("scroll", handleScroll);
420
+ const _ref$ = props.ref;
421
+ typeof _ref$ === "function" ? use(_ref$, _el$) : props.ref = _el$;
422
+ const _ref$2 = scrollContentRef;
423
+ typeof _ref$2 === "function" ? use(_ref$2, _el$2) : scrollContentRef = _el$2;
424
+ effect(_$p => style(_el$, {
425
+ flex: 1,
426
+ ...(context.noteDragMode && {
427
+ cursor: context.noteDragMode === "trimStart" ? "w-resize" : context.noteDragMode === "trimEnd" ? "e-resize" : "pointer"
428
+ }),
429
+ //"box-sizing": "border-box",
430
+ "z-index": 4,
431
+ overflow: "scroll",
432
+ "pointer-events": "auto"
433
+ }, _$p));
434
+ return _el$;
435
+ })();
436
+ };
437
+ delegateEvents(["mousedown", "mousemove", "click", "dblclick", "mouseup", "touchstart", "touchmove", "touchend"]);
438
+
439
+ const _tmpl$$2 = /*#__PURE__*/template(`<input type="range" min="1" max="11" step="0.01">`, 1);
440
+ const VerticalZoomControl = () => {
441
+ const context = usePianoRollContext();
442
+ return (() => {
443
+ const _el$ = _tmpl$$2.cloneNode(true);
444
+ _el$.$$input = event => context.onVerticalZoomChange?.(event.currentTarget.valueAsNumber);
445
+ spread(_el$, mergeProps({
341
446
  get value() {
342
- return props.verticalZoom;
447
+ return context.verticalZoom;
343
448
  }
344
449
  }, {
345
450
  orient: "vertical"
@@ -354,48 +459,161 @@ const PianoRoll = allProps => {
354
459
  };
355
460
  }
356
461
  }), false, false);
357
- _el$10.$$input = event => props.onZoomChange?.(event.currentTarget.valueAsNumber);
358
- _el$10.style.setProperty("margin-left", "50px");
359
- _el$10.style.setProperty("margin-right", "16px");
462
+ return _el$;
463
+ })();
464
+ };
465
+ delegateEvents(["input"]);
466
+
467
+ const _tmpl$$1 = /*#__PURE__*/template(`<div class="PianoRoll-Grid-Container"><div class="PianoRoll-Grid"></div></div>`, 4),
468
+ _tmpl$2 = /*#__PURE__*/template(`<div class="PianoRoll-Grid-Time"></div>`, 2),
469
+ _tmpl$3 = /*#__PURE__*/template(`<div class="PianoRoll-Grid-Key"></div>`, 2);
470
+ const PianoRollGrid = () => {
471
+ const context = usePianoRollContext();
472
+ const gridDivisorTicks = createMemo(() => context.ppq * 4 / context.gridDivision);
473
+ const gridArray = createMemo(() => {
474
+ const numberOfLines = context.duration / gridDivisorTicks();
475
+ return Array.from({
476
+ length: numberOfLines
477
+ }).map((_, index) => index * gridDivisorTicks());
478
+ });
479
+ return (() => {
480
+ const _el$ = _tmpl$$1.cloneNode(true),
481
+ _el$2 = _el$.firstChild;
482
+ _el$.style.setProperty("position", "absolute");
483
+ _el$2.style.setProperty("position", "relative");
484
+ _el$2.style.setProperty("height", "100%");
485
+ insert(_el$2, createComponent(Index, {
486
+ get each() {
487
+ return gridArray();
488
+ },
489
+ children: (tick, index) => {
490
+ const virtualDimensions = createMemo(() => context.horizontalViewPort.getVirtualDimensions(tick(), gridDivisorTicks()));
491
+ return createComponent(Show, {
492
+ get when() {
493
+ return virtualDimensions().size > 0;
494
+ },
495
+ get children() {
496
+ const _el$3 = _tmpl$2.cloneNode(true);
497
+ _el$3.style.setProperty("z-index", "1");
498
+ _el$3.style.setProperty("position", "absolute");
499
+ _el$3.style.setProperty("box-sizing", "border-box");
500
+ _el$3.style.setProperty("background", index % 4 === 0 ? "#ccc" : "#ddd");
501
+ _el$3.style.setProperty("top", "0px");
502
+ _el$3.style.setProperty("height", "100%");
503
+ _el$3.style.setProperty("border-left", "1px gray solid");
504
+ effect(_p$ => {
505
+ const _v$4 = `${virtualDimensions().offset}px`,
506
+ _v$5 = `${virtualDimensions().size}px`;
507
+ _v$4 !== _p$._v$4 && _el$3.style.setProperty("left", _p$._v$4 = _v$4);
508
+ _v$5 !== _p$._v$5 && _el$3.style.setProperty("width", _p$._v$5 = _v$5);
509
+ return _p$;
510
+ }, {
511
+ _v$4: undefined,
512
+ _v$5: undefined
513
+ });
514
+ return _el$3;
515
+ }
516
+ });
517
+ }
518
+ }), null);
519
+ insert(_el$2, createComponent(Index, {
520
+ each: keys,
521
+ children: key => {
522
+ const virtualDimensions = createMemo(() => context.verticalViewPort.getVirtualDimensions(127 - key().number, 1));
523
+ return createComponent(Show, {
524
+ get when() {
525
+ return virtualDimensions().size > 0;
526
+ },
527
+ get children() {
528
+ const _el$4 = _tmpl$3.cloneNode(true);
529
+ _el$4.style.setProperty("position", "absolute");
530
+ _el$4.style.setProperty("width", "100%");
531
+ _el$4.style.setProperty("border-style", "solid");
532
+ _el$4.style.setProperty("border-color", "gray");
533
+ _el$4.style.setProperty("z-index", "1");
534
+ effect(_p$ => {
535
+ const _v$6 = `${virtualDimensions().offset}px`,
536
+ _v$7 = `${virtualDimensions().size}px`,
537
+ _v$8 = key().isBlack ? "rgba(0,0,0,0.2)" : "none",
538
+ _v$9 = `${!key().isBlack && !blackKeys.includes((key().number + 1) % 12) ? "0.1px" : 0} 1px ${!key().isBlack && !blackKeys.includes((key().number - 1) % 12) ? "0.1px" : 0} 0`;
539
+ _v$6 !== _p$._v$6 && _el$4.style.setProperty("top", _p$._v$6 = _v$6);
540
+ _v$7 !== _p$._v$7 && _el$4.style.setProperty("height", _p$._v$7 = _v$7);
541
+ _v$8 !== _p$._v$8 && _el$4.style.setProperty("background-color", _p$._v$8 = _v$8);
542
+ _v$9 !== _p$._v$9 && _el$4.style.setProperty("border-width", _p$._v$9 = _v$9);
543
+ return _p$;
544
+ }, {
545
+ _v$6: undefined,
546
+ _v$7: undefined,
547
+ _v$8: undefined,
548
+ _v$9: undefined
549
+ });
550
+ return _el$4;
551
+ }
552
+ });
553
+ }
554
+ }), null);
360
555
  effect(_p$ => {
361
- const _v$ = `${dimensions().width}px`,
362
- _v$2 = `${dimensions().height}px`,
363
- _v$3 = `${dimensions().width}px`,
364
- _v$4 = {
365
- flex: 1,
366
- ...(noteDragMode() && {
367
- cursor: noteDragMode() === "trimStart" ? "w-resize" : noteDragMode() === "trimEnd" ? "e-resize" : "pointer"
368
- }),
369
- //"box-sizing": "border-box",
370
- "z-index": 1,
371
- overflow: "scroll",
372
- "pointer-events": "auto"
373
- };
374
- _v$ !== _p$._v$ && _el$5.style.setProperty("width", _p$._v$ = _v$);
375
- _v$2 !== _p$._v$2 && _el$5.style.setProperty("height", _p$._v$2 = _v$2);
376
- _v$3 !== _p$._v$3 && _el$6.style.setProperty("width", _p$._v$3 = _v$3);
377
- _p$._v$4 = style(_el$7, _v$4, _p$._v$4);
556
+ const _v$ = `${context.clientRect.width}px`,
557
+ _v$2 = `${context.clientRect.height}px`,
558
+ _v$3 = `${context.clientRect.width}px`;
559
+ _v$ !== _p$._v$ && _el$.style.setProperty("width", _p$._v$ = _v$);
560
+ _v$2 !== _p$._v$2 && _el$.style.setProperty("height", _p$._v$2 = _v$2);
561
+ _v$3 !== _p$._v$3 && _el$2.style.setProperty("width", _p$._v$3 = _v$3);
378
562
  return _p$;
379
563
  }, {
380
564
  _v$: undefined,
381
565
  _v$2: undefined,
382
- _v$3: undefined,
383
- _v$4: undefined
566
+ _v$3: undefined
384
567
  });
385
- effect(() => _el$10.value = props.zoom);
386
568
  return _el$;
387
569
  })();
388
570
  };
389
- const blackKeys = [1, 3, 6, 8, 10];
390
- const keyNames = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
391
- const keys = Array.from({
392
- length: 128
393
- }).map((_, index) => ({
394
- number: index,
395
- name: `${keyNames[index % 12]} ${Math.floor(index / 12) - 2}`,
396
- isBlack: blackKeys.includes(index % 12)
397
- }));
398
- delegateEvents(["mousedown", "mousemove", "click", "dblclick", "mouseup", "touchstart", "touchmove", "touchend", "input"]);
571
+
572
+ const _tmpl$ = /*#__PURE__*/template(`<div><div><div></div></div></div>`, 6);
573
+ const PianoRoll = allProps => {
574
+ const [contextProps, divProps] = splitProps(allProps, ["ppq", "notes", "position", "duration", "zoom", "verticalPosition", "verticalZoom", "onVerticalZoomChange", "onVerticalPositionChange", "onZoomChange", "onPositionChange", "onNoteChange", "gridDivision", "snapToGrid"]);
575
+ const [scrollContainer, setScrollContainer] = createSignal();
576
+ const [notesContainer, setNotesContainer] = createSignal();
577
+ return createComponent(PianoRollContextProvider, mergeProps(contextProps, {
578
+ get scrollContainer() {
579
+ return scrollContainer();
580
+ },
581
+ get notesContainer() {
582
+ return notesContainer();
583
+ },
584
+ get children() {
585
+ const _el$ = _tmpl$.cloneNode(true),
586
+ _el$2 = _el$.firstChild,
587
+ _el$3 = _el$2.firstChild;
588
+ spread(_el$, mergeProps(divProps, {
589
+ get ["class"]() {
590
+ return styles$1.PianoRoll;
591
+ }
592
+ }), false, true);
593
+ insert(_el$2, createComponent(PianoRollKeys, {}), _el$3);
594
+ insert(_el$3, createComponent(PianoRollNotes, {
595
+ ref: setNotesContainer
596
+ }), null);
597
+ insert(_el$3, createComponent(PianoRollGrid, {}), null);
598
+ insert(_el$3, createComponent(ScrollContainer, {
599
+ ref: setScrollContainer
600
+ }), null);
601
+ insert(_el$2, createComponent(VerticalZoomControl, {}), null);
602
+ insert(_el$, createComponent(HorizontalZoomControl, {}), null);
603
+ effect(_p$ => {
604
+ const _v$ = styles$1.PianoRollContainer,
605
+ _v$2 = styles$1.PianoRollInnerContainer;
606
+ _v$ !== _p$._v$ && className(_el$2, _p$._v$ = _v$);
607
+ _v$2 !== _p$._v$2 && className(_el$3, _p$._v$2 = _v$2);
608
+ return _p$;
609
+ }, {
610
+ _v$: undefined,
611
+ _v$2: undefined
612
+ });
613
+ return _el$;
614
+ }
615
+ }));
616
+ };
399
617
 
400
618
  export { PianoRoll };
401
619
  //# sourceMappingURL=index.js.map