solid-pianoroll 0.0.16 → 0.0.18

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.
@@ -1,5 +1,5 @@
1
1
  import styles from "./PianoRoll.module.css";
2
- import { createSignal, createMemo, createEffect, Show } from "solid-js";
2
+ import { createSignal, createMemo, createEffect, Show, mergeProps, } from "solid-js";
3
3
  import { PianoRollContextProvider, splitContextProps } from "./PianoRollContext";
4
4
  import PianoRollKeys from "./PianoRollKeys";
5
5
  import PianoRollNotes from "./PianoRollNotes";
@@ -12,63 +12,87 @@ import VerticalZoomControl from "./viewport/VerticalZoomControl";
12
12
  import HorizontalZoomControl from "./viewport/HorizontalZoomControl";
13
13
  import PianoRollTrackList from "./PianoRollTrackList";
14
14
  const PianoRoll = (allProps) => {
15
- const [contextProps, divProps] = splitContextProps(allProps);
15
+ const propsWithDefaults = mergeProps({ showAllTracks: false }, allProps);
16
+ const [context, divProps] = splitContextProps(propsWithDefaults);
16
17
  const [scrollerRef, setScrollerRef] = createSignal();
18
+ const [trackScrollerRef, setTrackScrollerRef] = createSignal();
17
19
  const clientRect = useBoundingClientRect(scrollerRef);
20
+ const trackClientRef = useBoundingClientRect(trackScrollerRef);
18
21
  const zoomFactor = 500;
19
22
  const minZoom = createMemo(() => 1 / (zoomFactor / clientRect().width));
20
23
  const maxZoom = createMemo(() => 500 * (zoomFactor / clientRect().width));
21
24
  const minVerticalZoom = createMemo(() => 1 / (zoomFactor / clientRect().height));
22
25
  const maxVerticalZoom = createMemo(() => 10 * (zoomFactor / clientRect().height));
26
+ const minVerticalTracksZoom = createMemo(() => 0.8 / (zoomFactor / trackClientRef().height));
27
+ const maxVerticalTracksZoom = createMemo(() => 3 * (zoomFactor / trackClientRef().height));
23
28
  createEffect((isInitial) => {
24
29
  if (isInitial)
25
30
  return false;
26
- if (contextProps.mode === "tracks") {
27
- contextProps.onVerticalPositionChange?.(0);
31
+ if (context.mode === "tracks") {
32
+ context.onVerticalPositionChange?.(0);
28
33
  }
29
34
  }, true);
30
- return (<PianoRollContextProvider value={contextProps}>
35
+ return (<PianoRollContextProvider value={context}>
31
36
  <div {...divProps} class={styles.PianoRoll}>
32
- <div class={styles.PianoRollContainer}>
33
- <ScrollZoomViewPort dimensions={{
37
+ <ScrollZoomViewPort dimensions={{
34
38
  horizontal: () => ({
35
39
  pixelOffset: clientRect().left,
36
40
  pixelSize: clientRect().width,
37
- position: contextProps.position,
38
- range: contextProps.duration,
39
- zoom: contextProps.zoom * (zoomFactor / clientRect().width),
40
- onPositionChange: contextProps.onPositionChange,
41
- onZoomChange: (zoom) => contextProps.onZoomChange?.(clamp(zoom / (zoomFactor / clientRect().width), minZoom(), maxZoom())),
41
+ position: context.position,
42
+ range: context.duration,
43
+ zoom: context.zoom * (zoomFactor / clientRect().width),
44
+ onPositionChange: context.onPositionChange,
45
+ onZoomChange: (zoom) => context.onZoomChange?.(clamp(zoom / (zoomFactor / clientRect().width), minZoom(), maxZoom())),
42
46
  }),
43
47
  vertical: () => ({
44
48
  pixelOffset: clientRect().top,
45
49
  pixelSize: clientRect().height,
46
- position: contextProps.verticalPosition,
47
- range: contextProps.mode === "keys" ? 128 : Math.max(contextProps.tracks.length, 16),
48
- zoom: contextProps.verticalZoom * (zoomFactor / clientRect().height),
49
- onPositionChange: contextProps.onVerticalPositionChange,
50
- onZoomChange: (verticalZoom) => contextProps.onVerticalZoomChange?.(clamp(verticalZoom / (zoomFactor / clientRect().height), minVerticalZoom(), maxVerticalZoom())),
50
+ position: context.verticalPosition,
51
+ range: 128,
52
+ zoom: context.verticalZoom * (zoomFactor / clientRect().height),
53
+ onPositionChange: context.onVerticalPositionChange,
54
+ onZoomChange: (verticalZoom) => context.onVerticalZoomChange?.(clamp(verticalZoom / (zoomFactor / clientRect().height), minVerticalZoom(), maxVerticalZoom())),
55
+ }),
56
+ horizontalTracks: () => ({
57
+ pixelOffset: clientRect().left,
58
+ pixelSize: clientRect().width,
59
+ position: 0,
60
+ range: 1,
61
+ zoom: 1,
62
+ }),
63
+ verticalTracks: () => ({
64
+ pixelOffset: trackClientRef().top,
65
+ pixelSize: trackClientRef().height,
66
+ position: context.verticalTrackPosition,
67
+ range: context.tracks.length,
68
+ zoom: context.verticalTrackZoom * (zoomFactor / trackClientRef().height),
69
+ onPositionChange: context.onVerticalTrackPositionChange,
70
+ onZoomChange: (verticalTrackZoom) => context.onVerticalTrackZoomChange?.(clamp(verticalTrackZoom / (zoomFactor / trackClientRef().height), minVerticalTracksZoom(), maxVerticalTracksZoom())),
51
71
  }),
52
72
  }}>
73
+ <div class={styles.PianoRollContainer}>
74
+ <VerticalZoomControl min={minVerticalTracksZoom()} max={maxVerticalTracksZoom()} dimensionName="verticalTracks"/>
53
75
  <div style={{ display: "flex", width: "20%" }}>
54
- <Show when={allProps.showTrackList}>
55
- <PianoRollTrackList />
76
+ <Show when={context.showTrackList}>
77
+ <ScrollContainer ref={setTrackScrollerRef} horizontalDimensionName="horizontalTracks" verticalDimensionName="verticalTracks" showScrollbar={context.mode === "keys"}>
78
+ <PianoRollTrackList />
79
+ </ScrollContainer>
56
80
  </Show>
57
- <Show when={contextProps.mode === "keys"}>
81
+ <Show when={context.mode === "keys"}>
58
82
  <PianoRollKeys />
59
83
  </Show>
60
84
  </div>
61
85
 
62
- <ScrollContainer ref={setScrollerRef}>
86
+ <ScrollContainer ref={setScrollerRef} verticalDimensionName={context.mode === "keys" ? "vertical" : "verticalTracks"}>
63
87
  {allProps.children}
64
88
  <PianoRollGrid />
65
89
  <PianoRollNotes />
66
90
  </ScrollContainer>
67
91
 
68
- <VerticalZoomControl min={minVerticalZoom()} max={maxVerticalZoom()}/>
69
- </ScrollZoomViewPort>
70
- </div>
71
- <HorizontalZoomControl min={minZoom()} max={maxZoom()}/>
92
+ <VerticalZoomControl min={minVerticalZoom()} max={maxVerticalZoom()} disabled={context.mode !== "keys"}/>
93
+ </div>
94
+ <HorizontalZoomControl min={minZoom()} max={maxZoom()}/>
95
+ </ScrollZoomViewPort>
72
96
  </div>
73
97
  </PianoRollContextProvider>);
74
98
  };
@@ -8,24 +8,37 @@ export const usePianoRollContext = () => {
8
8
  return context;
9
9
  };
10
10
  export const splitContextProps = (allProps) => splitProps(allProps, [
11
+ "ppq",
11
12
  "mode",
12
- "duration",
13
- "gridDivision",
14
- "tracks",
15
13
  "position",
16
- "ppq",
17
- "snapToGrid",
18
- "verticalPosition",
19
- "verticalZoom",
20
14
  "zoom",
21
- "onInsertNote",
22
- "onNoteChange",
15
+ "verticalZoom",
16
+ "verticalPosition",
17
+ "verticalTrackZoom",
18
+ "verticalTrackPosition",
19
+ "gridDivision",
20
+ "snapToGrid",
21
+ "duration",
22
+ "tracks",
23
+ "selectedTrackIndex",
24
+ "pressedKeys",
25
+ "onPpqChange",
26
+ "onModeChange",
23
27
  "onPositionChange",
24
- "onRemoveNote",
25
- "onVerticalPositionChange",
26
- "onVerticalZoomChange",
27
28
  "onZoomChange",
28
- "showAllTracks",
29
- "selectedTrackIndex",
29
+ "onVerticalZoomChange",
30
+ "onVerticalPositionChange",
31
+ "onVerticalTrackZoomChange",
32
+ "onVerticalTrackPositionChange",
33
+ "onGridDivisionChange",
34
+ "onSnapToGridChange",
35
+ "onDurationChange",
36
+ "onTracksChange",
37
+ "onNoteChange",
38
+ "onInsertNote",
39
+ "onRemoveNote",
30
40
  "onSelectedTrackIndexChange",
41
+ "onPressedKeysChange",
42
+ "showAllTracks",
43
+ "showTrackList",
31
44
  ]);
@@ -5,7 +5,7 @@ import { useViewPortDimension } from "./viewport/ScrollZoomViewPort";
5
5
  import styles from "./PianoRollGrid.module.css";
6
6
  const PianoRollGrid = () => {
7
7
  const context = usePianoRollContext();
8
- const verticalViewPort = createMemo(() => useViewPortDimension("vertical"));
8
+ const verticalViewPort = createMemo(() => useViewPortDimension(context.mode === "keys" ? "vertical" : "verticalTracks"));
9
9
  const horizontalViewPort = createMemo(() => useViewPortDimension("horizontal"));
10
10
  const measureTicks = createMemo(() => context.ppq * 4);
11
11
  const selectedGridDivisorTicks = createMemo(() => measureTicks() / context.gridDivision);
@@ -1,12 +1,33 @@
1
1
  import styles from "./PianoRollKeys.module.css";
2
- import { createMemo, createSignal, Index, Show } from "solid-js";
2
+ import { createEffect, createMemo, createSignal, Index, Show } from "solid-js";
3
3
  import { useViewPortDimension } from "./viewport/ScrollZoomViewPort";
4
+ import { usePianoRollContext } from "./PianoRollContext";
5
+ import ScrollContainer from "./viewport/ScrollContainer";
4
6
  const PianoRollKeys = () => {
5
7
  const viewPort = createMemo(() => useViewPortDimension("vertical"));
6
- return (<div class={styles.PianoRollKeys}>
8
+ const [isMouseDown, setIsMouseDown] = createSignal(false);
9
+ const context = usePianoRollContext();
10
+ const handleMouseUp = () => {
11
+ setIsMouseDown(false);
12
+ };
13
+ createEffect(() => {
14
+ if (isMouseDown()) {
15
+ window.addEventListener("mouseup", handleMouseUp);
16
+ }
17
+ else {
18
+ window.removeEventListener("mouseup", handleMouseUp);
19
+ }
20
+ });
21
+ return (<ScrollContainer classList={{ [styles.PianoRollKeys]: true }} showScrollbar={false}>
7
22
  <Index each={keys}>
8
23
  {(key) => {
9
- const [isDown, setIsDown] = createSignal(false);
24
+ const isDown = createMemo(() => context.pressedKeys.includes(key().number));
25
+ const handleKeyDown = () => {
26
+ context.onPressedKeysChange?.([...context.pressedKeys, key().number]);
27
+ };
28
+ const handleKeyUp = () => {
29
+ context.onPressedKeysChange?.([...context.pressedKeys].filter((number) => number !== key().number));
30
+ };
10
31
  const virtualDimensions = createMemo(() => viewPort().calculatePixelDimensions(127 - key().number, 1));
11
32
  const previousIsBlack = blackKeys.includes((key().number - 1) % 12);
12
33
  const nextIsBlack = blackKeys.includes((key().number + 1) % 12);
@@ -16,7 +37,20 @@ const PianoRollKeys = () => {
16
37
  [styles["black"]]: key().isBlack,
17
38
  [styles["white"]]: !key().isBlack,
18
39
  [styles["down"]]: isDown(),
19
- }} onMouseDown={() => setIsDown(true)} onMouseUp={() => setIsDown(false)} title={key().name} data-index={key().number % 12} style={{
40
+ }} onMouseDown={() => {
41
+ setIsMouseDown(true);
42
+ handleKeyDown();
43
+ }} onMouseUp={() => {
44
+ handleKeyUp();
45
+ }} onMouseEnter={() => {
46
+ if (isMouseDown()) {
47
+ handleKeyDown();
48
+ }
49
+ }} onMouseLeave={() => {
50
+ if (isMouseDown()) {
51
+ handleKeyUp();
52
+ }
53
+ }} title={key().name} data-index={key().number % 12} style={{
20
54
  top: `${virtualDimensions().offset -
21
55
  (!key().isBlack && nextIsBlack ? virtualDimensions().size / 2 : 0)}px`,
22
56
  height: `${virtualDimensions().size +
@@ -40,7 +74,7 @@ const PianoRollKeys = () => {
40
74
  </Show>);
41
75
  }}
42
76
  </Index>
43
- </div>);
77
+ </ScrollContainer>);
44
78
  };
45
79
  export const blackKeys = [1, 3, 6, 8, 10];
46
80
  export const keyNames = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
@@ -2,9 +2,10 @@ import { batch, createMemo, createSignal, For, Show } from "solid-js";
2
2
  import { usePianoRollContext } from "./PianoRollContext";
3
3
  import { useViewPortDimension } from "./viewport/ScrollZoomViewPort";
4
4
  import styles from "./PianoRollNotes.module.scss";
5
+ import { clamp } from "./viewport/createViewPortDimension";
5
6
  const PianoRollNotes = (props) => {
6
7
  const context = usePianoRollContext();
7
- const verticalViewPort = createMemo(() => useViewPortDimension("vertical"));
8
+ const verticalViewPort = createMemo(() => useViewPortDimension(context.mode === "keys" ? "vertical" : "verticalTracks"));
8
9
  const horizontalViewPort = createMemo(() => useViewPortDimension("horizontal"));
9
10
  const gridDivisionTicks = createMemo(() => (context.ppq * 4) / context.gridDivision);
10
11
  const snapValueToGridIfEnabled = (value, altKey) => context.snapToGrid && !altKey
@@ -17,7 +18,7 @@ const PianoRollNotes = (props) => {
17
18
  ? 127 - Math.floor(verticalViewPort().calculatePosition(event.clientY))
18
19
  : existingNote?.midi ?? 60;
19
20
  const targetTrackIndex = context.mode === "tracks"
20
- ? Math.floor(verticalViewPort().calculatePosition(event.clientY))
21
+ ? clamp(Math.floor(verticalViewPort().calculatePosition(event.clientY)), 0, context.tracks.length - 1)
21
22
  : context.selectedTrackIndex ?? 0;
22
23
  const eventPositionTicks = Math.floor(position / gridDivisionTicks()) * gridDivisionTicks();
23
24
  const velocity = 127;
@@ -135,7 +136,7 @@ const PianoRollNotes = (props) => {
135
136
  const previousTrackIndex = currentNoteTrackIndex();
136
137
  const previousNoteIndex = currentNoteIndex();
137
138
  const targetTrackIndex = context.mode === "tracks"
138
- ? Math.floor(verticalViewPort().calculatePosition(mouseMoveEvent.clientY))
139
+ ? clamp(Math.floor(verticalViewPort().calculatePosition(mouseMoveEvent.clientY)), 0, context.tracks.length - 1)
139
140
  : previousTrackIndex;
140
141
  if (targetTrackIndex === previousTrackIndex) {
141
142
  context.onNoteChange?.(previousTrackIndex, previousNoteIndex, updatedNote);
@@ -3,12 +3,12 @@ import { createMemo, Index, Show } from "solid-js";
3
3
  import { useViewPortDimension } from "./viewport/ScrollZoomViewPort";
4
4
  import { usePianoRollContext } from "./PianoRollContext";
5
5
  const PianoRollTrackList = () => {
6
- const viewPort = createMemo(() => useViewPortDimension("vertical"));
6
+ const viewPort = createMemo(() => useViewPortDimension("verticalTracks"));
7
7
  const context = usePianoRollContext();
8
8
  return (<div class={styles.PianoRollTrackList}>
9
9
  <Index each={context.tracks}>
10
10
  {(track, index) => {
11
- const virtualDimensions = createMemo(() => viewPort().calculatePixelDimensions(context.mode === "tracks" ? index : index * 8, context.mode === "tracks" ? 1 : 8));
11
+ const virtualDimensions = createMemo(() => viewPort().calculatePixelDimensions(index, 1));
12
12
  return (<Show when={virtualDimensions().size > 0}>
13
13
  <div classList={{
14
14
  [styles["Track"]]: true,
@@ -1,5 +1,5 @@
1
1
  import PianoRoll from "./PianoRoll";
2
2
  import PlayHead from "./PlayHead";
3
3
  import useNotes from "./useNotes";
4
- import usePianoRoll from "./usePianoRoll";
5
- export { PianoRoll, usePianoRoll, useNotes, PlayHead };
4
+ import createPianoRollstate from "./usePianoRollState";
5
+ export { PianoRoll, createPianoRollstate, useNotes, PlayHead };
@@ -0,0 +1,92 @@
1
+ import { mergeProps } from "solid-js";
2
+ import { createStore } from "solid-js/store";
3
+ const createPianoRollstate = (defaultState) => {
4
+ const [state, setState] = createStore({
5
+ ppq: 0,
6
+ mode: "keys",
7
+ position: 0,
8
+ zoom: 10,
9
+ verticalZoom: 5,
10
+ verticalPosition: 44,
11
+ verticalTrackZoom: 0.5,
12
+ verticalTrackPosition: 0,
13
+ gridDivision: 4,
14
+ snapToGrid: true,
15
+ duration: 0,
16
+ tracks: [],
17
+ selectedTrackIndex: 0,
18
+ pressedKeys: [],
19
+ ...defaultState,
20
+ });
21
+ const updateNotes = async (trackIndex, getNotes) => {
22
+ const track = state.tracks[trackIndex];
23
+ if (!track)
24
+ return;
25
+ const notes = track.notes;
26
+ onTracksChange([
27
+ ...state.tracks.slice(0, trackIndex),
28
+ { ...track, notes: getNotes(notes) },
29
+ ...state.tracks.slice(trackIndex + 1),
30
+ ]);
31
+ };
32
+ const onNoteChange = (trackIndex, noteIndex, note) => {
33
+ updateNotes(trackIndex, (notes) => [
34
+ ...notes.slice(0, noteIndex),
35
+ note,
36
+ ...notes.slice(noteIndex + 1),
37
+ ]);
38
+ };
39
+ const onInsertNote = (trackIndex, note) => {
40
+ const track = state.tracks[trackIndex];
41
+ if (!track)
42
+ return -1;
43
+ const notes = track.notes;
44
+ const newNoteIndex = Math.max(notes.findIndex(({ ticks }) => ticks > note.ticks), 0);
45
+ updateNotes(trackIndex, (notes) => [
46
+ ...notes.slice(0, newNoteIndex),
47
+ note,
48
+ ...notes.slice(newNoteIndex),
49
+ ]);
50
+ return newNoteIndex;
51
+ };
52
+ const onRemoveNote = (trackIndex, noteIndex) => {
53
+ updateNotes(trackIndex, (notes) => [
54
+ ...notes.slice(0, noteIndex),
55
+ ...notes.slice(noteIndex + 1),
56
+ ]);
57
+ };
58
+ const onPpqChange = (ppq) => setState("ppq", ppq);
59
+ const onModeChange = (mode) => setState("mode", mode);
60
+ const onPositionChange = (position) => setState("position", position);
61
+ const onZoomChange = (zoom) => setState("zoom", zoom);
62
+ const onVerticalZoomChange = (verticalZoom) => setState("verticalZoom", verticalZoom);
63
+ const onVerticalPositionChange = (verticalPosition) => setState("verticalPosition", verticalPosition);
64
+ const onVerticalTrackZoomChange = (verticalTrackZoom) => setState("verticalTrackZoom", verticalTrackZoom);
65
+ const onVerticalTrackPositionChange = (verticalTrackPosition) => setState("verticalTrackPosition", verticalTrackPosition);
66
+ const onGridDivisionChange = (gridDivision) => setState("gridDivision", gridDivision);
67
+ const onSnapToGridChange = (snapToGrid) => setState("snapToGrid", snapToGrid);
68
+ const onDurationChange = (duration) => setState("duration", duration);
69
+ const onTracksChange = (tracks) => setState("tracks", tracks);
70
+ const onSelectedTrackIndexChange = (selectedTrackIndex) => setState("selectedTrackIndex", selectedTrackIndex);
71
+ const onPressedKeysChange = (pressedKeys) => setState("pressedKeys", pressedKeys);
72
+ return mergeProps(state, {
73
+ onPpqChange,
74
+ onModeChange,
75
+ onPositionChange,
76
+ onZoomChange,
77
+ onVerticalZoomChange,
78
+ onVerticalPositionChange,
79
+ onVerticalTrackZoomChange,
80
+ onVerticalTrackPositionChange,
81
+ onGridDivisionChange,
82
+ onSnapToGridChange,
83
+ onDurationChange,
84
+ onTracksChange,
85
+ onNoteChange,
86
+ onInsertNote,
87
+ onRemoveNote,
88
+ onSelectedTrackIndexChange,
89
+ onPressedKeysChange,
90
+ });
91
+ };
92
+ export default createPianoRollstate;
@@ -1,7 +1,8 @@
1
- import { usePianoRollContext } from "../PianoRollContext";
1
+ import { createMemo } from "solid-js";
2
+ import { useViewPortDimension } from "./ScrollZoomViewPort";
2
3
  const HorizontalZoomControl = (props) => {
3
- const context = usePianoRollContext();
4
- return (<input max="500" min="1" step={0.01} {...props} value={context.zoom} onInput={(event) => context.onZoomChange?.(event.currentTarget.valueAsNumber)} type="range" style={{
4
+ const dimension = createMemo(() => useViewPortDimension(props.dimensionName ?? "horizontal"));
5
+ return (<input max="500" min="1" step={0.01} {...props} value={dimension().zoom} onInput={(event) => dimension().onZoomChange?.(event.currentTarget.valueAsNumber)} type="range" style={{
5
6
  "margin-left": "50px",
6
7
  "margin-right": "16px",
7
8
  ...(typeof props.style === "object" && props.style),
@@ -1,9 +1,21 @@
1
- import { createEffect, createMemo } from "solid-js";
1
+ import { createEffect, createMemo, mergeProps, splitProps } from "solid-js";
2
2
  import { useViewPortDimension } from "./ScrollZoomViewPort";
3
+ import styles from "./ScrollContainer.module.css";
3
4
  const ScrollContainer = (props) => {
4
5
  let scrollContentRef;
5
- const verticalViewPort = createMemo(() => useViewPortDimension("vertical"));
6
- const horizontalViewPort = createMemo(() => useViewPortDimension("horizontal"));
6
+ const [ownProps, divProps] = splitProps(props, [
7
+ "ref",
8
+ "verticalDimensionName",
9
+ "horizontalDimensionName",
10
+ "showScrollbar",
11
+ ]);
12
+ const propsWithDefaults = mergeProps({
13
+ verticalDimensionName: "vertical",
14
+ horizontalDimensionName: "horizontal",
15
+ showScrollbar: true,
16
+ }, ownProps);
17
+ const verticalViewPort = createMemo(() => useViewPortDimension(propsWithDefaults.verticalDimensionName));
18
+ const horizontalViewPort = createMemo(() => useViewPortDimension(propsWithDefaults.horizontalDimensionName));
7
19
  const handleScroll = (event) => {
8
20
  event.preventDefault();
9
21
  if (didUpdateScroll) {
@@ -34,6 +46,11 @@ const ScrollContainer = (props) => {
34
46
  verticalViewPort()?.onPositionChange?.(Math.min(maxVerticalPosition, verticalViewPort()?.position));
35
47
  }
36
48
  }
49
+ else if (!props.showScrollbar) {
50
+ event.preventDefault();
51
+ event.currentTarget.scrollLeft += event.deltaX;
52
+ event.currentTarget.scrollTop += event.deltaY;
53
+ }
37
54
  };
38
55
  let didUpdateScroll = false;
39
56
  createEffect(() => {
@@ -57,13 +74,13 @@ const ScrollContainer = (props) => {
57
74
  top: scrollTop,
58
75
  });
59
76
  });
60
- return (<div ref={props.ref} class="PianoRoll-Scroller" style={{
61
- height: "100%",
62
- width: "100%",
63
- "z-index": 4,
64
- overflow: "scroll",
65
- "pointer-events": "auto",
66
- }} onScroll={handleScroll} onWheel={handleWheel}>
77
+ return (<div ref={props.ref} style={{
78
+ overflow: propsWithDefaults.showScrollbar ? "scroll" : "hidden",
79
+ ...(typeof divProps.style === "object" && divProps.style),
80
+ }} classList={{
81
+ [styles.ScrollContainer]: true,
82
+ ...divProps.classList,
83
+ }} onScroll={handleScroll} onWheel={handleWheel} {...divProps}>
67
84
  <div ref={scrollContentRef}>
68
85
  <div style={{
69
86
  top: 0,
@@ -1,8 +1,10 @@
1
- import { usePianoRollContext } from "../PianoRollContext";
1
+ import { createMemo } from "solid-js";
2
+ import { useViewPortDimension } from "./ScrollZoomViewPort";
2
3
  const VerticalZoomControl = (props) => {
3
- const context = usePianoRollContext();
4
- return (<input min="1" max="11" step={0.01} {...props} value={context.verticalZoom} onInput={(event) => context.onVerticalZoomChange?.(event.currentTarget.valueAsNumber)} type="range" {...{ orient: "vertical" }} style={{
4
+ const dimension = createMemo(() => useViewPortDimension(props.dimensionName ?? "vertical"));
5
+ return (<input min="1" max="11" step={0.01} {...props} value={dimension().zoom} onInput={(event) => dimension().onZoomChange?.(event.currentTarget.valueAsNumber)} type="range" {...{ orient: "vertical" }} style={{
5
6
  width: "16px",
7
+ transform: "rotate(180deg)",
6
8
  ...{
7
9
  "writing-mode": "bt-lr" /* IE */,
8
10
  "-webkit-appearance": "slider-vertical" /* WebKit */,
@@ -1,28 +1,9 @@
1
1
  import { JSX, ParentProps } from "solid-js";
2
- import { Note, Track } from "./types";
2
+ import createPianoRollstate from "./usePianoRollState";
3
3
  export type GridDivision = 1 | 2 | 4 | 8 | 16 | 32 | 64;
4
4
  export type PianoRollProps = {
5
- ppq: number;
6
- tracks: Track[];
7
- gridDivision: GridDivision;
8
- snapToGrid: boolean;
9
- mode: "keys" | "tracks";
10
5
  showAllTracks?: boolean;
11
6
  showTrackList?: boolean;
12
- selectedTrackIndex: number;
13
- verticalPosition: number;
14
- verticalZoom: number;
15
- position: number;
16
- duration: number;
17
- zoom: number;
18
- onVerticalZoomChange?: (zoom: number) => void;
19
- onVerticalPositionChange?: (zoom: number) => void;
20
- onZoomChange?: (zoom: number) => void;
21
- onPositionChange?: (zoom: number) => void;
22
- onNoteChange?: (trackIndex: number, noteIndex: number, note: Note) => void;
23
- onInsertNote?: (trackIndex: number, note: Note) => number;
24
- onRemoveNote?: (trackIndex: number, noteIndex: number) => void;
25
- onSelectedTrackIndexChange?: (trackIndex: number) => void;
26
- };
27
- declare const PianoRoll: (allProps: ParentProps<PianoRollProps & JSX.IntrinsicElements["div"]>) => JSX.Element;
7
+ } & ReturnType<typeof createPianoRollstate> & Omit<JSX.IntrinsicElements["div"], "onDurationChange">;
8
+ declare const PianoRoll: (allProps: ParentProps<PianoRollProps>) => JSX.Element;
28
9
  export default PianoRoll;
@@ -1,5 +1,74 @@
1
- import { JSX } from "solid-js";
2
1
  import { PianoRollProps } from "./PianoRoll";
3
- export declare const PianoRollContextProvider: import("solid-js/types/reactive/signal").ContextProviderComponent<PianoRollProps>;
4
- export declare const usePianoRollContext: () => PianoRollProps;
5
- export declare const splitContextProps: (allProps: PianoRollProps & JSX.IntrinsicElements["div"]) => [Pick<PianoRollProps & JSX.HTMLAttributes<HTMLDivElement>, "position" | "zoom" | "mode" | "duration" | "gridDivision" | "tracks" | "ppq" | "snapToGrid" | "verticalPosition" | "verticalZoom" | "onInsertNote" | "onNoteChange" | "onPositionChange" | "onRemoveNote" | "onVerticalPositionChange" | "onVerticalZoomChange" | "onZoomChange" | "showAllTracks" | "selectedTrackIndex" | "onSelectedTrackIndexChange">, Omit<PianoRollProps & JSX.HTMLAttributes<HTMLDivElement>, "position" | "zoom" | "mode" | "duration" | "gridDivision" | "tracks" | "ppq" | "snapToGrid" | "verticalPosition" | "verticalZoom" | "onInsertNote" | "onNoteChange" | "onPositionChange" | "onRemoveNote" | "onVerticalPositionChange" | "onVerticalZoomChange" | "onZoomChange" | "showAllTracks" | "selectedTrackIndex" | "onSelectedTrackIndexChange">];
2
+ export declare const PianoRollContextProvider: import("solid-js/types/reactive/signal").ContextProviderComponent<{
3
+ showAllTracks?: boolean;
4
+ showTrackList?: boolean;
5
+ } & {
6
+ ppq: number;
7
+ mode: "keys" | "tracks";
8
+ position: number;
9
+ zoom: number;
10
+ verticalZoom: number;
11
+ verticalPosition: number;
12
+ verticalTrackZoom: number;
13
+ verticalTrackPosition: number;
14
+ gridDivision: import("./PianoRoll").GridDivision;
15
+ snapToGrid: boolean;
16
+ duration: number;
17
+ tracks: import("./types").Track[];
18
+ selectedTrackIndex: number;
19
+ pressedKeys: number[];
20
+ onPpqChange: (ppq: number) => void;
21
+ onModeChange: (mode: "keys" | "tracks") => void;
22
+ onPositionChange: (position: number) => void;
23
+ onZoomChange: (zoom: number) => void;
24
+ onVerticalZoomChange: (verticalZoom: number) => void;
25
+ onVerticalPositionChange: (verticalPosition: number) => void;
26
+ onVerticalTrackZoomChange: (verticalTrackZoom: number) => void;
27
+ onVerticalTrackPositionChange: (verticalTrackPosition: number) => void;
28
+ onGridDivisionChange: (gridDivision: import("./PianoRoll").GridDivision) => void;
29
+ onSnapToGridChange: (snapToGrid: boolean) => void;
30
+ onDurationChange: (duration: number) => void;
31
+ onTracksChange: (tracks: import("./types").Track[]) => void;
32
+ onNoteChange: (trackIndex: number, noteIndex: number, note: import("./types").Note) => void;
33
+ onInsertNote: (trackIndex: number, note: import("./types").Note) => number;
34
+ onRemoveNote: (trackIndex: number, noteIndex: number) => void;
35
+ onSelectedTrackIndexChange: (selectedTrackIndex: number) => void;
36
+ onPressedKeysChange: (pressedKeys: number[]) => void;
37
+ }>;
38
+ export declare const usePianoRollContext: () => {
39
+ showAllTracks?: boolean;
40
+ showTrackList?: boolean;
41
+ } & {
42
+ ppq: number;
43
+ mode: "keys" | "tracks";
44
+ position: number;
45
+ zoom: number;
46
+ verticalZoom: number;
47
+ verticalPosition: number;
48
+ verticalTrackZoom: number;
49
+ verticalTrackPosition: number;
50
+ gridDivision: import("./PianoRoll").GridDivision;
51
+ snapToGrid: boolean;
52
+ duration: number;
53
+ tracks: import("./types").Track[];
54
+ selectedTrackIndex: number;
55
+ pressedKeys: number[];
56
+ onPpqChange: (ppq: number) => void;
57
+ onModeChange: (mode: "keys" | "tracks") => void;
58
+ onPositionChange: (position: number) => void;
59
+ onZoomChange: (zoom: number) => void;
60
+ onVerticalZoomChange: (verticalZoom: number) => void;
61
+ onVerticalPositionChange: (verticalPosition: number) => void;
62
+ onVerticalTrackZoomChange: (verticalTrackZoom: number) => void;
63
+ onVerticalTrackPositionChange: (verticalTrackPosition: number) => void;
64
+ onGridDivisionChange: (gridDivision: import("./PianoRoll").GridDivision) => void;
65
+ onSnapToGridChange: (snapToGrid: boolean) => void;
66
+ onDurationChange: (duration: number) => void;
67
+ onTracksChange: (tracks: import("./types").Track[]) => void;
68
+ onNoteChange: (trackIndex: number, noteIndex: number, note: import("./types").Note) => void;
69
+ onInsertNote: (trackIndex: number, note: import("./types").Note) => number;
70
+ onRemoveNote: (trackIndex: number, noteIndex: number) => void;
71
+ onSelectedTrackIndexChange: (selectedTrackIndex: number) => void;
72
+ onPressedKeysChange: (pressedKeys: number[]) => void;
73
+ };
74
+ export declare const splitContextProps: (allProps: PianoRollProps) => [Pick<PianoRollProps, "position" | "zoom" | "onDurationChange" | "mode" | "tracks" | "duration" | "ppq" | "verticalZoom" | "verticalPosition" | "verticalTrackZoom" | "verticalTrackPosition" | "gridDivision" | "snapToGrid" | "selectedTrackIndex" | "pressedKeys" | "onPpqChange" | "onModeChange" | "onPositionChange" | "onZoomChange" | "onVerticalZoomChange" | "onVerticalPositionChange" | "onVerticalTrackZoomChange" | "onVerticalTrackPositionChange" | "onGridDivisionChange" | "onSnapToGridChange" | "onTracksChange" | "onNoteChange" | "onInsertNote" | "onRemoveNote" | "onSelectedTrackIndexChange" | "onPressedKeysChange" | "showAllTracks" | "showTrackList">, Omit<PianoRollProps, "position" | "zoom" | "onDurationChange" | "mode" | "tracks" | "duration" | "ppq" | "verticalZoom" | "verticalPosition" | "verticalTrackZoom" | "verticalTrackPosition" | "gridDivision" | "snapToGrid" | "selectedTrackIndex" | "pressedKeys" | "onPpqChange" | "onModeChange" | "onPositionChange" | "onZoomChange" | "onVerticalZoomChange" | "onVerticalPositionChange" | "onVerticalTrackZoomChange" | "onVerticalTrackPositionChange" | "onGridDivisionChange" | "onSnapToGridChange" | "onTracksChange" | "onNoteChange" | "onInsertNote" | "onRemoveNote" | "onSelectedTrackIndexChange" | "onPressedKeysChange" | "showAllTracks" | "showTrackList">];
@@ -1,5 +1,5 @@
1
1
  import PianoRoll from "./PianoRoll";
2
2
  import PlayHead from "./PlayHead";
3
3
  import useNotes from "./useNotes";
4
- import usePianoRoll from "./usePianoRoll";
5
- export { PianoRoll, usePianoRoll, useNotes, PlayHead };
4
+ import createPianoRollstate from "./usePianoRollState";
5
+ export { PianoRoll, createPianoRollstate, useNotes, PlayHead };