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