solid-pianoroll 0.0.8 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/cjs/index.js +457 -451
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/esm/index.js +459 -453
  4. package/dist/esm/index.js.map +1 -1
  5. package/dist/source/PianoRoll.jsx +43 -16
  6. package/dist/source/PianoRollContext.jsx +3 -42
  7. package/dist/source/PianoRollGrid.jsx +22 -33
  8. package/dist/source/PianoRollKeys.jsx +10 -6
  9. package/dist/source/PianoRollNotes.jsx +91 -46
  10. package/dist/source/usePianoRoll.js +6 -3
  11. package/dist/source/viewport/HorizontalZoomControl.jsx +9 -0
  12. package/dist/source/viewport/ScrollContainer.jsx +87 -0
  13. package/dist/source/viewport/ScrollZoomViewPort.jsx +19 -0
  14. package/dist/source/viewport/VerticalZoomControl.jsx +12 -0
  15. package/dist/source/viewport/createViewPortDimension.js +39 -0
  16. package/dist/types/PianoRoll.d.ts +1 -0
  17. package/dist/types/PianoRollContext.d.ts +2 -22
  18. package/dist/types/PianoRollNotes.d.ts +1 -1
  19. package/dist/types/usePianoRoll.d.ts +1 -0
  20. package/dist/types/viewport/HorizontalZoomControl.d.ts +3 -0
  21. package/dist/types/viewport/ScrollContainer.d.ts +5 -0
  22. package/dist/types/viewport/ScrollZoomViewPort.d.ts +23 -0
  23. package/dist/types/viewport/VerticalZoomControl.d.ts +3 -0
  24. package/dist/types/viewport/createViewPortDimension.d.ts +31 -0
  25. package/package.json +7 -1
  26. package/dist/source/HorizontalZoomControl.jsx +0 -9
  27. package/dist/source/ScrollContainer.jsx +0 -138
  28. package/dist/source/VerticalZoomControl.jsx +0 -12
  29. package/dist/source/useViewPortScaler.js +0 -44
  30. package/dist/types/HorizontalZoomControl.d.ts +0 -2
  31. package/dist/types/ScrollContainer.d.ts +0 -5
  32. package/dist/types/VerticalZoomControl.d.ts +0 -2
  33. package/dist/types/useViewPortScaler.d.ts +0 -20
@@ -1,88 +1,133 @@
1
- import { createMemo, Index, Show } from "solid-js";
1
+ import { createMemo, createSignal, Index, Show } from "solid-js";
2
2
  import { usePianoRollContext } from "./PianoRollContext";
3
+ import { useViewPortDimension } from "./viewport/ScrollZoomViewPort";
4
+ import styles from "./PianoRollNotes.module.scss";
3
5
  const PianoRollNotes = (props) => {
4
6
  const context = usePianoRollContext();
7
+ const verticalViewPort = createMemo(() => useViewPortDimension("vertical"));
8
+ const horizontalViewPort = createMemo(() => useViewPortDimension("horizontal"));
5
9
  const gridDivisionTicks = createMemo(() => (context.ppq * 4) / context.gridDivision);
6
10
  const snapValueToGridIfEnabled = (value, altKey) => context.snapToGrid && !altKey
7
11
  ? Math.round(value / gridDivisionTicks()) * gridDivisionTicks()
8
12
  : value;
9
- return (<div class="PianoRoll-Notes-Container" style={{
10
- position: "absolute",
11
- width: `${context.clientRect.width}px`,
12
- height: `${context.clientRect.height}px`,
13
+ const insertOrUpdateNote = (event) => {
14
+ const position = horizontalViewPort().calculatePosition(event.clientX);
15
+ const midi = 127 - Math.floor(verticalViewPort().calculatePosition(event.clientY));
16
+ const eventPositionTicks = Math.floor(position / gridDivisionTicks()) * gridDivisionTicks();
17
+ const velocity = 127;
18
+ const existingNote = newNote();
19
+ const ticks = existingNote?.ticks ?? eventPositionTicks;
20
+ const durationTicks = existingNote?.ticks
21
+ ? eventPositionTicks - existingNote?.ticks
22
+ : gridDivisionTicks();
23
+ const note = { midi, ticks, durationTicks, velocity };
24
+ if (existingNote) {
25
+ context.onNoteChange?.(newNoteIndex(), note);
26
+ return newNoteIndex();
27
+ }
28
+ else {
29
+ return context.onInsertNote?.(note) ?? -1;
30
+ }
31
+ };
32
+ const [isDragging, setIsDragging] = createSignal(false);
33
+ const [noteDragMode, setNoteDragMode] = createSignal();
34
+ const [newNoteIndex, setNewNoteIndex] = createSignal(-1);
35
+ const [isMouseDown, setIsMouseDown] = createSignal(false);
36
+ const newNote = createMemo(() => context.notes[newNoteIndex()]);
37
+ const getClasses = (noteDragMode) => {
38
+ return noteDragMode ? [styles.Note, styles[noteDragMode]] : [styles.Note];
39
+ };
40
+ return (<div class={styles.PianoRollNotes} ref={props.ref} onMouseDown={() => {
41
+ console.log("down");
42
+ setIsMouseDown(true);
43
+ }} onMouseMove={(event) => {
44
+ if (isDragging())
45
+ return;
46
+ if (!isMouseDown()) {
47
+ setNoteDragMode(undefined);
48
+ return;
49
+ }
50
+ const index = insertOrUpdateNote(event);
51
+ console.log({ index });
52
+ setNewNoteIndex(index);
53
+ }} onMouseUp={(event) => {
54
+ setIsMouseDown(false);
55
+ if (!event.altKey)
56
+ return;
57
+ insertOrUpdateNote(event);
58
+ }} onDblClick={(event) => {
59
+ insertOrUpdateNote(event);
60
+ }} onClick={(event) => {
61
+ if (newNote()) {
62
+ setNewNoteIndex(-1);
63
+ return;
64
+ }
65
+ if (!event.altKey)
66
+ return;
67
+ insertOrUpdateNote(event);
13
68
  }}>
14
- <div class="PianoRoll-Notes" ref={props.ref} style={{
15
- position: "relative",
16
- width: `${context.clientRect.width}px`,
17
- height: "100%",
18
- }}>
19
- <Index each={context.notes}>
20
- {(note, index) => {
21
- const horizontalVirtualDimensions = createMemo(() => context.verticalViewPort.getVirtualDimensions(127 - note().midi, 1));
22
- const verticalVirtualDimensions = createMemo(() => context.horizontalViewPort.getVirtualDimensions(note().ticks, note().durationTicks));
23
- return (<Show when={!!horizontalVirtualDimensions().size && !!verticalVirtualDimensions().size}>
24
- <div class="PianoRoll-Note" onMouseMove={(event) => {
25
- if (context.isDragging)
69
+ <Index each={context.notes}>
70
+ {(note, index) => {
71
+ const verticalVirtualDimensionss = createMemo(() => verticalViewPort().calculatePixelDimensions(127 - note().midi, 1));
72
+ const horizontalDimensions = createMemo(() => horizontalViewPort().calculatePixelDimensions(note().ticks, note().durationTicks));
73
+ return (<Show when={!!verticalVirtualDimensionss().size && !!horizontalDimensions().size}>
74
+ <div class={getClasses(noteDragMode()).join(" ")} onMouseMove={(event) => {
75
+ if (isDragging())
26
76
  return;
27
- const relativeX = context.horizontalViewPort.getScaledValue(context.horizontalViewPort.getPosition(event.clientX));
28
- const noteStartX = context.horizontalViewPort.getScaledValue(note().ticks);
29
- const noteEndX = context.horizontalViewPort.getScaledValue(note().ticks + note().durationTicks);
30
- context.onNoteDragModeChange(relativeX - noteStartX < 3
77
+ event.stopPropagation();
78
+ const relativeX = horizontalViewPort().calculatePixelValue(horizontalViewPort().calculatePosition(event.clientX));
79
+ const noteStartX = horizontalViewPort().calculatePixelValue(note().ticks);
80
+ const noteEndX = horizontalViewPort().calculatePixelValue(note().ticks + note().durationTicks);
81
+ setNoteDragMode(relativeX - noteStartX < 3
31
82
  ? "trimStart"
32
83
  : noteEndX - relativeX < 3
33
84
  ? "trimEnd"
34
85
  : "move");
35
- }} onDblClick={() => {
86
+ }} onDblClick={(event) => {
87
+ event.stopPropagation();
36
88
  context.onRemoveNote?.(index);
37
89
  }} onMouseDown={(event) => {
38
- context.onIsDraggingChange(true);
39
- const initialPosition = context.horizontalViewPort.getPosition(event.clientX);
90
+ event.stopPropagation();
91
+ setIsDragging(true);
92
+ const initialPosition = horizontalViewPort().calculatePosition(event.clientX);
40
93
  const diffPosition = initialPosition - note().ticks;
41
94
  const handleMouseMove = (mouseMoveEvent) => {
42
- const ticks = snapValueToGridIfEnabled(Math.max(context.horizontalViewPort.getPosition(mouseMoveEvent.clientX) -
95
+ const ticks = snapValueToGridIfEnabled(Math.max(horizontalViewPort().calculatePosition(mouseMoveEvent.clientX) -
43
96
  diffPosition, 0), mouseMoveEvent.altKey);
44
97
  context.onNoteChange?.(index, {
45
98
  ...note(),
46
- ...(context.noteDragMode === "move" && {
47
- midi: Math.round(127 - context.verticalViewPort.getPosition(mouseMoveEvent.clientY)),
99
+ ...(noteDragMode() === "move" && {
100
+ midi: Math.round(127 - verticalViewPort().calculatePosition(mouseMoveEvent.clientY)),
48
101
  }),
49
- ...((context.noteDragMode === "move" ||
50
- context.noteDragMode === "trimStart") && {
102
+ ...((noteDragMode() === "move" || noteDragMode() === "trimStart") && {
51
103
  ticks,
52
104
  }),
53
- ...(context.noteDragMode === "trimStart" && {
105
+ ...(noteDragMode() === "trimStart" && {
54
106
  durationTicks: note().durationTicks + note().ticks - ticks,
55
107
  }),
56
- ...(context.noteDragMode === "trimEnd" && {
57
- durationTicks: snapValueToGridIfEnabled(context.horizontalViewPort.getPosition(mouseMoveEvent.clientX) -
108
+ ...(noteDragMode() === "trimEnd" && {
109
+ durationTicks: snapValueToGridIfEnabled(horizontalViewPort().calculatePosition(mouseMoveEvent.clientX) -
58
110
  note().ticks, mouseMoveEvent.altKey),
59
111
  }),
60
112
  });
61
113
  };
62
114
  const handleMouseUp = () => {
63
- context.onIsDraggingChange(false);
115
+ setIsDragging(false);
64
116
  window.removeEventListener("mousemove", handleMouseMove);
65
117
  window.removeEventListener("mouseup", handleMouseUp);
66
118
  };
67
119
  window.addEventListener("mousemove", handleMouseMove);
68
120
  window.addEventListener("mouseup", handleMouseUp);
69
121
  }} style={{
70
- "z-index": 2,
71
- position: "absolute",
72
- "box-sizing": "border-box",
73
- top: `${horizontalVirtualDimensions().offset}px`,
74
- height: `${horizontalVirtualDimensions().size}px`,
75
- left: `${verticalVirtualDimensions().offset}px`,
76
- width: `${verticalVirtualDimensions().size}px`,
77
- "border-width": "0.5px",
78
- "border-style": "solid",
79
122
  "background-color": `rgba(255,0,0, ${(128 + note().velocity) / 256})`,
80
- "border-color": "#a00",
123
+ top: `${verticalVirtualDimensionss().offset}px`,
124
+ height: `${verticalVirtualDimensionss().size}px`,
125
+ left: `${horizontalDimensions().offset}px`,
126
+ width: `${horizontalDimensions().size}px`,
81
127
  }}></div>
82
- </Show>);
128
+ </Show>);
83
129
  }}
84
- </Index>
85
- </div>
130
+ </Index>
86
131
  </div>);
87
132
  };
88
133
  export default PianoRollNotes;
@@ -8,7 +8,7 @@ const usePianoRoll = () => {
8
8
  const [gridDivision, onGridDivisionChange] = createSignal(16);
9
9
  const [snapToGrid, onSnapToGridChange] = createSignal(true);
10
10
  const [notes, onNotesChange] = createSignal([]);
11
- const [duration, setDuration] = createSignal(0);
11
+ const [duration, onDurationChange] = createSignal(0);
12
12
  const onNoteChange = (index, note) => {
13
13
  onNotesChange([...notes().slice(0, index), note, ...notes().splice(index + 1)]);
14
14
  };
@@ -21,8 +21,10 @@ const usePianoRoll = () => {
21
21
  onNotesChange([...notes().slice(0, index), ...notes().splice(index + 1)]);
22
22
  };
23
23
  createEffect(() => {
24
- setDuration(Math.max((notes()[notes().length - 1]?.ticks ?? 0) +
25
- (notes()[notes().length - 1]?.durationTicks ?? 0), ppq() * 4 * 4));
24
+ const minDuration = Math.max((notes()[notes().length - 1]?.ticks ?? 0) + (notes()[notes().length - 1]?.durationTicks ?? 0), ppq() * 4 * 4);
25
+ if (duration() < minDuration) {
26
+ onDurationChange(minDuration);
27
+ }
26
28
  });
27
29
  return {
28
30
  ppq,
@@ -45,6 +47,7 @@ const usePianoRoll = () => {
45
47
  onInsertNote,
46
48
  onRemoveNote,
47
49
  duration,
50
+ onDurationChange,
48
51
  };
49
52
  };
50
53
  export default usePianoRoll;
@@ -0,0 +1,9 @@
1
+ import { usePianoRollContext } from "../PianoRollContext";
2
+ 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={{
5
+ "margin-left": "50px",
6
+ "margin-right": "16px",
7
+ }}/>);
8
+ };
9
+ export default HorizontalZoomControl;
@@ -0,0 +1,87 @@
1
+ import { createEffect, createMemo } from "solid-js";
2
+ import { useViewPortDimension } from "./ScrollZoomViewPort";
3
+ const ScrollContainer = (props) => {
4
+ let scrollContentRef;
5
+ const verticalViewPort = createMemo(() => useViewPortDimension("vertical"));
6
+ const horizontalViewPort = createMemo(() => useViewPortDimension("horizontal"));
7
+ const handleScroll = (event) => {
8
+ event.preventDefault();
9
+ if (didUpdateScroll) {
10
+ didUpdateScroll = false;
11
+ return;
12
+ }
13
+ const maxVerticalPosition = verticalViewPort().calculateMaxPosition();
14
+ const maxPosition = horizontalViewPort().calculateMaxPosition();
15
+ const height = verticalViewPort().pixelSize;
16
+ const width = horizontalViewPort().pixelSize;
17
+ const { scrollTop, scrollLeft, scrollWidth, scrollHeight } = event.currentTarget;
18
+ const scrollTopAmount = scrollTop / (scrollHeight - height);
19
+ const scrollLeftAmount = scrollLeft / (scrollWidth - width);
20
+ verticalViewPort().onPositionChange?.(maxVerticalPosition * scrollTopAmount);
21
+ horizontalViewPort().onPositionChange?.(maxPosition * scrollLeftAmount);
22
+ };
23
+ const handleWheel = (event) => {
24
+ if (event.altKey) {
25
+ event.preventDefault();
26
+ if (Math.abs(event.deltaX) > Math.abs(event.deltaY)) {
27
+ horizontalViewPort()?.onZoomChange?.(horizontalViewPort().zoom * (1 + event.deltaX / horizontalViewPort().pixelSize));
28
+ const maxPosition = horizontalViewPort().calculateMaxPosition();
29
+ horizontalViewPort()?.onPositionChange?.(Math.min(maxPosition, horizontalViewPort()?.position));
30
+ }
31
+ else {
32
+ verticalViewPort()?.onZoomChange?.(verticalViewPort().zoom * (1 + event.deltaY / verticalViewPort().pixelSize));
33
+ const maxVerticalPosition = verticalViewPort().calculateMaxPosition();
34
+ verticalViewPort()?.onPositionChange?.(Math.min(maxVerticalPosition, verticalViewPort()?.position));
35
+ }
36
+ }
37
+ };
38
+ let didUpdateScroll = false;
39
+ createEffect(() => {
40
+ const maxVerticalPosition = verticalViewPort().calculateMaxPosition();
41
+ const maxPosition = horizontalViewPort().calculateMaxPosition();
42
+ const scrollTopAmount = maxVerticalPosition > 0 ? verticalViewPort().position / maxVerticalPosition : 0;
43
+ const scrollLeftAmount = maxPosition > 0 ? horizontalViewPort().position / maxPosition : 0;
44
+ const height = verticalViewPort().pixelSize;
45
+ const width = horizontalViewPort().pixelSize;
46
+ if (!scrollContentRef?.parentElement)
47
+ return;
48
+ const scrollDivHeight = verticalViewPort().zoom * verticalViewPort().pixelSize;
49
+ const scrollTop = scrollTopAmount * (scrollDivHeight - height);
50
+ const scrollDivWidth = horizontalViewPort().zoom * horizontalViewPort().pixelSize;
51
+ const scrollLeft = scrollLeftAmount * (scrollDivWidth - width);
52
+ didUpdateScroll = true;
53
+ scrollContentRef.style.height = `${scrollDivHeight}px`;
54
+ scrollContentRef.style.width = `${scrollDivWidth}px`;
55
+ scrollContentRef.parentElement.scrollTo({
56
+ left: scrollLeft,
57
+ top: scrollTop,
58
+ });
59
+ });
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}>
67
+ <div ref={scrollContentRef}>
68
+ <div style={{
69
+ top: 0,
70
+ left: 0,
71
+ height: `${verticalViewPort().pixelSize}px`,
72
+ width: `${horizontalViewPort().pixelSize}px`,
73
+ position: "sticky",
74
+ display: "flex",
75
+ }}>
76
+ <div style={{
77
+ position: "relative",
78
+ height: `${verticalViewPort().pixelSize}px`,
79
+ width: `${horizontalViewPort().pixelSize}px`,
80
+ }}>
81
+ {props.children}
82
+ </div>
83
+ </div>
84
+ </div>
85
+ </div>);
86
+ };
87
+ export default ScrollContainer;
@@ -0,0 +1,19 @@
1
+ import { createContext, useContext } from "solid-js";
2
+ import createViewPortDimension from "./createViewPortDimension";
3
+ export const ScrollZoomViewPort = (props) => {
4
+ const viewPorts = Object.entries(props.dimensions).map(([name, viewPortProps]) => createViewPortDimension(() => ({ name: name, ...viewPortProps() })));
5
+ const parentViewPorts = useContext(ScrollZoomViewPortContext);
6
+ return (<ScrollZoomViewPortContext.Provider value={[...parentViewPorts, ...viewPorts]}>
7
+ {props.children}
8
+ </ScrollZoomViewPortContext.Provider>);
9
+ };
10
+ const ScrollZoomViewPortContext = createContext([]);
11
+ export const useViewPortDimension = (name) => {
12
+ const viewPorts = useContext(ScrollZoomViewPortContext);
13
+ const viewPort = name
14
+ ? viewPorts.find((viewPort) => viewPort.name === name)
15
+ : viewPorts[viewPorts.length - 1];
16
+ if (!viewPort)
17
+ throw new Error(`ViewPort dimension "${name}" not found.`);
18
+ return viewPort;
19
+ };
@@ -0,0 +1,12 @@
1
+ import { usePianoRollContext } from "../PianoRollContext";
2
+ 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={{
5
+ width: "16px",
6
+ ...{
7
+ "writing-mode": "bt-lr" /* IE */,
8
+ "-webkit-appearance": "slider-vertical" /* WebKit */,
9
+ },
10
+ }}/>);
11
+ };
12
+ export default VerticalZoomControl;
@@ -0,0 +1,39 @@
1
+ import { createEffect } from "solid-js";
2
+ import { createStore } from "solid-js/store";
3
+ export default function createViewPortDimension(getState) {
4
+ const getStateWithFunctions = () => ({
5
+ ...getState(),
6
+ calculatePixelValue,
7
+ calculatePosition,
8
+ calculatePixelDimensions,
9
+ calculateVisibleRange,
10
+ calculateMaxPosition,
11
+ });
12
+ const [state, setState] = createStore(getStateWithFunctions());
13
+ createEffect(() => setState(getStateWithFunctions()));
14
+ function calculatePixelValue(position = state.position) {
15
+ const virtualSize = state.pixelSize * state.zoom;
16
+ return (position / state.range) * virtualSize;
17
+ }
18
+ function calculatePixelOffset(position) {
19
+ return calculatePixelValue(position) - calculatePixelValue(state.position);
20
+ }
21
+ function calculatePosition(offset) {
22
+ const percentX = (offset - state.pixelOffset) / state.pixelSize;
23
+ const position = state.position + percentX * calculateVisibleRange();
24
+ return position;
25
+ }
26
+ function calculateVisibleRange() {
27
+ return state.range / state.zoom;
28
+ }
29
+ function calculateMaxPosition() {
30
+ return state.range - state.range / state.zoom;
31
+ }
32
+ function calculatePixelDimensions(position, length) {
33
+ const offset = calculatePixelOffset(position);
34
+ const size = calculatePixelValue(length);
35
+ return { offset, size };
36
+ }
37
+ return state;
38
+ }
39
+ export const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
@@ -6,6 +6,7 @@ export type PianoRollProps = {
6
6
  notes: Note[];
7
7
  gridDivision: GridDivision;
8
8
  snapToGrid: boolean;
9
+ condensed?: boolean;
9
10
  verticalPosition: number;
10
11
  verticalZoom: number;
11
12
  position: number;
@@ -1,23 +1,3 @@
1
- import { JSX } from "solid-js";
2
1
  import { PianoRollProps } from "./PianoRoll";
3
- import { ClientRect } from "./useBoundingClientRect";
4
- import { ViewPortScaler } from "./useViewPortScaler";
5
- export type NoteDragMode = "trimStart" | "move" | "trimEnd" | undefined;
6
- type PianoRollContext = {
7
- horizontalViewPort: ViewPortScaler;
8
- verticalViewPort: ViewPortScaler;
9
- clientRect: ClientRect;
10
- notesContainer?: HTMLDivElement;
11
- isDragging: boolean;
12
- onIsDraggingChange: (isDragging: boolean) => void;
13
- noteDragMode: NoteDragMode;
14
- onNoteDragModeChange: (isDragging: NoteDragMode) => void;
15
- } & PianoRollProps;
16
- declare const PianoRollContext: import("solid-js").Context<PianoRollContext>;
17
- export declare const PianoRollContextProvider: (props: {
18
- scrollContainer?: HTMLDivElement;
19
- notesContainer?: HTMLDivElement;
20
- children: JSX.Element;
21
- } & PianoRollProps) => JSX.Element;
22
- export declare const usePianoRollContext: () => PianoRollContext;
23
- export {};
2
+ export declare const PianoRollContextProvider: import("solid-js/types/reactive/signal").ContextProviderComponent<PianoRollProps>;
3
+ export declare const usePianoRollContext: () => PianoRollProps;
@@ -1,5 +1,5 @@
1
1
  import { Ref } from "solid-js";
2
2
  declare const PianoRollNotes: (props: {
3
- ref: Ref<HTMLDivElement | undefined>;
3
+ ref?: Ref<HTMLDivElement | undefined>;
4
4
  }) => import("solid-js").JSX.Element;
5
5
  export default PianoRollNotes;
@@ -21,5 +21,6 @@ declare const usePianoRoll: () => {
21
21
  onInsertNote: (note: Note) => number;
22
22
  onRemoveNote: (index: number) => void;
23
23
  duration: import("solid-js").Accessor<number>;
24
+ onDurationChange: import("solid-js").Setter<number>;
24
25
  };
25
26
  export default usePianoRoll;
@@ -0,0 +1,3 @@
1
+ import { JSX } from "solid-js/jsx-runtime";
2
+ declare const HorizontalZoomControl: (props: JSX.IntrinsicElements["input"]) => JSX.Element;
3
+ export default HorizontalZoomControl;
@@ -0,0 +1,5 @@
1
+ import { ParentProps, Ref } from "solid-js";
2
+ declare const ScrollContainer: (props: ParentProps<{
3
+ ref?: Ref<HTMLDivElement>;
4
+ }>) => import("solid-js").JSX.Element;
5
+ export default ScrollContainer;
@@ -0,0 +1,23 @@
1
+ import { ParentProps } from "solid-js";
2
+ import { ViewPortDimensionName, ViewPortDimensionState } from "./createViewPortDimension";
3
+ export declare const ScrollZoomViewPort: (props: ParentProps<{
4
+ dimensions: Record<ViewPortDimensionName, () => Omit<ViewPortDimensionState, "name">>;
5
+ }>) => import("solid-js").JSX.Element;
6
+ export declare const useViewPortDimension: (name: ViewPortDimensionName) => {
7
+ calculatePixelValue: (position?: number) => number;
8
+ calculatePosition: (offset: number) => number;
9
+ calculatePixelDimensions: (position: number, length: number) => {
10
+ offset: number;
11
+ size: number;
12
+ };
13
+ calculateVisibleRange: () => number;
14
+ calculateMaxPosition: () => number;
15
+ position: number;
16
+ range: number;
17
+ pixelOffset: number;
18
+ pixelSize: number;
19
+ name: ViewPortDimensionName;
20
+ onZoomChange?: (zoom: number) => void;
21
+ onPositionChange?: (zoom: number) => void;
22
+ zoom: number;
23
+ };
@@ -0,0 +1,3 @@
1
+ import { JSX } from "solid-js/jsx-runtime";
2
+ declare const VerticalZoomControl: (props: JSX.IntrinsicElements["input"]) => JSX.Element;
3
+ export default VerticalZoomControl;
@@ -0,0 +1,31 @@
1
+ export type ViewPortDimension = ReturnType<typeof createViewPortDimension>;
2
+ export type ViewPortDimensionName = "horizontal" | "vertical";
3
+ export type ViewPortDimensionState = {
4
+ position: number;
5
+ range: number;
6
+ pixelOffset: number;
7
+ pixelSize: number;
8
+ name: ViewPortDimensionName;
9
+ onZoomChange?: (zoom: number) => void;
10
+ onPositionChange?: (zoom: number) => void;
11
+ zoom: number;
12
+ };
13
+ export default function createViewPortDimension(getState: () => ViewPortDimensionState): {
14
+ calculatePixelValue: (position?: number) => number;
15
+ calculatePosition: (offset: number) => number;
16
+ calculatePixelDimensions: (position: number, length: number) => {
17
+ offset: number;
18
+ size: number;
19
+ };
20
+ calculateVisibleRange: () => number;
21
+ calculateMaxPosition: () => number;
22
+ position: number;
23
+ range: number;
24
+ pixelOffset: number;
25
+ pixelSize: number;
26
+ name: ViewPortDimensionName;
27
+ onZoomChange?: (zoom: number) => void;
28
+ onPositionChange?: (zoom: number) => void;
29
+ zoom: number;
30
+ };
31
+ export declare const clamp: (value: number, min: number, max: number) => number;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.0.8",
2
+ "version": "0.0.10",
3
3
  "name": "solid-pianoroll",
4
4
  "description": "Pianoroll UI Control for Solid JS apps",
5
5
  "license": "MIT",
@@ -50,6 +50,12 @@
50
50
  "solid-js": ">=1.0.0"
51
51
  },
52
52
  "devDependencies": {
53
+ "@typescript-eslint/eslint-plugin": "^5.47.1",
54
+ "@typescript-eslint/parser": "^5.47.1",
55
+ "eslint": "^8.31.0",
56
+ "eslint-config-prettier": "^8.5.0",
57
+ "eslint-plugin-prettier": "^4.2.1",
58
+ "eslint-plugin-unused-imports": "^2.0.0",
53
59
  "postcss": "^8.4.20",
54
60
  "prettier": "2.8.1",
55
61
  "rollup": "^3.7.2",
@@ -1,9 +0,0 @@
1
- import { usePianoRollContext } from "./PianoRollContext";
2
- const HorizontalZoomControl = () => {
3
- const context = usePianoRollContext();
4
- return (<input value={context.zoom} onInput={(event) => context.onZoomChange?.(event.currentTarget.valueAsNumber)} type="range" max="500" min="1" step={0.01} style={{
5
- "margin-left": "50px",
6
- "margin-right": "16px",
7
- }}/>);
8
- };
9
- export default HorizontalZoomControl;