Skip to content

Commit

Permalink
Massive work on Splittable / Panel resizing!
Browse files Browse the repository at this point in the history
Co-authored-by: JackDotJS <jackdotbusiness@proton.me>
  • Loading branch information
bdotsamir and JackDotJS committed Apr 16, 2024
1 parent 94bc1fe commit 1db2a76
Show file tree
Hide file tree
Showing 11 changed files with 206 additions and 69 deletions.
26 changes: 13 additions & 13 deletions src/MemoryContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ export interface IMemory {
[x: string]: any // TODO: **REMOVE THIS LINE WHEN PROJECT IS DONE!**
}

type PanelFunction = () => JSX.Element;
type SplitableFunction = () => JSX.Element;

export const MemoryContext = createContext<{
masterAnalyser: AnalyserNode,
audioContext: AudioContext,

readonly panels: (PanelFunction)[],
addPanel(NewPanel: PanelFunction): void, // eslint-disable-line no-unused-vars
removePanel(OldPanel: PanelFunction): void, // eslint-disable-line no-unused-vars
readonly panels: (SplitableFunction)[],
addPanel(NewPanel: SplitableFunction): void, // eslint-disable-line no-unused-vars
removePanel(OldPanel: SplitableFunction): void, // eslint-disable-line no-unused-vars

readonly isEditingPanels: boolean,
toggleEditingPanels(): void,
Expand All @@ -52,27 +52,27 @@ export function MemoryProvider(props: { children: JSX.Element }) {
const audioContext = new window.AudioContext();
const masterAnalyser = audioContext.createAnalyser();

const [panelsStore, setPanelsStore] = createStore({
panels: [] as PanelFunction[],
const [memoryStore, setMemoryStore] = createStore({
panels: [] as SplitableFunction[],
isEditingPanels: false
});

const memory = {
audioContext,
masterAnalyser,

get panels() { return panelsStore.panels; },
addPanel(NewPanel: PanelFunction) {
get panels() { return memoryStore.panels; },
addPanel(NewPanel: SplitableFunction) {
console.log("Adding a panel...");
return setPanelsStore("panels", panelsStore.panels.length, () => NewPanel);
return setMemoryStore("panels", memoryStore.panels.length, () => NewPanel);
},
removePanel(OldPanel: PanelFunction) {
removePanel(OldPanel: SplitableFunction) {
console.log("Removing a panel...");
return setPanelsStore("panels", panelsStore.panels.filter((panel) => panel !== OldPanel));
return setMemoryStore("panels", memoryStore.panels.filter((panel) => panel !== OldPanel));
},
get isEditingPanels() { return panelsStore.isEditingPanels; },
get isEditingPanels() { return memoryStore.isEditingPanels; },
toggleEditingPanels() {
setPanelsStore("isEditingPanels", (e) => !e);
setMemoryStore("isEditingPanels", (e) => !e);
},

mousePosition: useMousePosition()
Expand Down
19 changes: 12 additions & 7 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,20 @@ export default function Header() {
<button>Track</button>
<button>Help</button>
<button onClick={() => {
memoryContext.addPanel(() => (
<Splitable type="horizontal">
<Panel />
<Panel />
</Splitable>
));
memoryContext.addPanel(() => {
return (
<Splitable type="horizontal">
<Panel />
<Splitable type="vertical">
<Panel />
<Panel />
</Splitable>
</Splitable>
);
});
// memoryContext.setPanels([...memoryContext.panels, <Panel />]);
console.log(memoryContext.panels);
}}>+ Panel</button>
}}>+ 🍌</button>

<input
style={{ flex: 1 }}
Expand Down
48 changes: 23 additions & 25 deletions src/components/Panel.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,53 @@
import { JSX, Show, createEffect } from "solid-js";
import panelStyles from "../css/Panel.module.css";
import clsx from "clsx";
import { getPositionToElement } from "@solid-primitives/mouse";
import { useMemoryContext } from "../MemoryContext";
import { useSplitableContext } from "./Splitable";

type PanelProps = {
// height?: number,
// width?: number,
children?: JSX.Element,
headerPosition?: "top" | "bottom" | "left" | "right"
headerPosition?: "top" | "bottom" | "left" | "right",
// panelID: string
}
export default function Panel(props: PanelProps) {

const { mousePosition } = useMemoryContext();
let panelRef!: HTMLDivElement;

let ref!: HTMLDivElement;
const ctx = useSplitableContext();

createEffect(() => {
const relative = getPositionToElement(mousePosition.x, mousePosition.y, ref);
// console.log(relative.x, relative.y);
const sibling = panelRef.nextElementSibling;
// console.log("sibling", sibling?.attributes);

// Calculate which cursor to show based on the position of the mouse
// based on the relative positions
if (relative.x < 10) {
ref.style.cursor = "col-resize";
if (!sibling) {
// We can reasonably assume we are panel 2
console.log("I am element 2", panelRef);
ctx.setElementTwo(panelRef);
return;
}
else if (relative.x > ref.offsetWidth - 10) {
ref.style.cursor = "col-resize";
}
else if (relative.y < 10) {
ref.style.cursor = "row-resize";
}
else if (relative.y > ref.offsetHeight - 10) {
ref.style.cursor = "row-resize";
}
else {
ref.style.cursor = "default";

// If there is a sibling, check if it is the split line
if(sibling.getAttribute("data-split-line")) {
console.log("I am element 1", panelRef);
ctx.setElementOne(panelRef);
}
});

return (
<Show when={props.children} fallback={<FallbackComponent ref={ref} />}>
<div ref={ref}>
<Show when={props.children} fallback={<FallbackComponent ref={panelRef} />}>
<div ref={panelRef}>
{props.children}
</div>
</Show>
);
}

function FallbackComponent(props: { ref: HTMLDivElement }) {
return <div ref={props.ref} class={clsx(panelStyles.panel, panelStyles.centeredContent)}>
return <div
ref={props.ref}
class={clsx(panelStyles.panel, panelStyles.centeredContent)}
>
<h2>Select a panel</h2>
<button>+ Panel</button>
</div>;
Expand Down
5 changes: 3 additions & 2 deletions src/components/PanelHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ export default function PanelHandler() {
<div
ref={ref}
style={{
"flex": 1,
"flex": "1",
"flex-direction": "row",
"display": "flex"
"display": "flex",
"align-items": "center"
}}
>
<For each={memoryContext.panels}>
Expand Down
2 changes: 1 addition & 1 deletion src/components/SpectrumGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,5 @@ export default function SpectrumGraph() {
ref={spectrumRef}
class={styles.waveform}
/>
)
);
}
11 changes: 0 additions & 11 deletions src/components/SplitLine.tsx

This file was deleted.

89 changes: 82 additions & 7 deletions src/components/Splitable.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { JSX, Match, Switch, createContext, createSignal, useContext } from "solid-js";
import SplitLine from "./SplitLine";
import { JSX, Match, Switch, createContext, createSignal, onMount, useContext } from "solid-js";

import styles from "../css/Splitable.module.css";

export const SplitableContext = createContext<{
elementOne: HTMLDivElement,
elementTwo: HTMLDivElement,

setElementOne: (element: HTMLDivElement) => void, // eslint-disable-line no-unused-vars
setElementTwo: (element: HTMLDivElement) => void, // eslint-disable-line no-unused-vars

splitDirection: "horizontal" | "vertical"
}>();

export function useSplitableContext() {
Expand All @@ -31,25 +34,97 @@ export default function Splitable(props: SplitableProps) {
const [elementOne, setElementOne] = createSignal<HTMLDivElement>();
const [elementTwo, setElementTwo] = createSignal<HTMLDivElement>();

let splitline!: HTMLDivElement;
let splitlineWidth: number = 0;

let isDragging = false;

const startDrag = () => {
isDragging = true;
};

const drag = (e: PointerEvent) => {
const e1 = elementOne();

if (e1 == null) return;

if (props.type === `horizontal`) {
const relativePosition = (e.clientX - e1.getBoundingClientRect().left) - (splitlineWidth / 2);
e1.style.width = `${relativePosition}px`;
e1.style.flex = `unset`;
} else {
const relativePosition = (e.clientY - e1.getBoundingClientRect().top) - (splitlineWidth / 2);
e1.style.height = `${relativePosition}px`;
e1.style.flex = `unset`;
}
};

const stopDrag = () => {
isDragging = false;
};

onMount(() => {
if (props.type === `horizontal`) {
splitlineWidth = splitline.getBoundingClientRect().width;
} else {
splitlineWidth = splitline.getBoundingClientRect().height;
}

splitline.addEventListener(`pointerdown`, () => {
console.debug(`start drag`);
startDrag();
});

window.addEventListener(`pointermove`, (e) => {
if (isDragging) {
drag(e);
}
});

window.addEventListener(`pointerup`, stopDrag);
window.addEventListener(`pointercancel`, stopDrag);
});

return (
<SplitableContext.Provider value={{
get elementOne() { return elementOne()!; },
get elementTwo() { return elementTwo()!; },
setElementOne,
setElementTwo
setElementTwo,
get splitDirection() { return props.type; }
}}>
<Switch fallback={<FallbackComponent />}>
<Match when={props.type === "vertical"}>
<div style={{ display: "flex", "flex-direction": "column", flex: 1 }}>
<div style={{
display: "flex",
"flex-direction": "column",
"flex": "1",
"height": "100%"
}}>
{props.children[0]}
<SplitLine type="vertical" />
{/* <SplitLine type="vertical" /> */}
<div
data-split-line="true"
ref={splitline}
class={`${styles.vertical} ${styles.splitLine}`}
/>
{props.children[1]}
</div>
</Match>
<Match when={props.type === "horizontal"}>
<div style={{ display: "flex", "flex-direction": "row", flex: 1 }}>
<div style={{
display: "flex",
"flex-direction": "row",
"flex": "1",
"height": "100%"
}}>
{props.children[0]}
<SplitLine type="horizontal" />
{/* <SplitLine type="horizontal" /> */}
<div
data-split-line="true"
ref={splitline}
class={`${styles.horizontal} ${styles.splitLine}`}
/>
{props.children[1]}
</div>
</Match>
Expand Down
4 changes: 2 additions & 2 deletions src/css/Panel.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
flex: 1;
border-radius: 0.25rem;
background-color: var(--generic-module-frame);
padding: 1rem;
margin: 0.5rem;
/* padding: 1rem; */
/* margin: 0.5rem; */
}
66 changes: 66 additions & 0 deletions src/css/Splitable.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
.splitLine {
--gap: 0.25rem;
--lowerWidth: 45%;
--upperWidth: 55%;
position: relative;
}

.vertical {
height: 1rem;
cursor: ns-resize;
}

.horizontal {
width: 1rem;
cursor: ew-resize;
}

.vertical:after, .vertical:before {
content: '';
display: block;
position: absolute;
top: 50%;
transform: translateY(-50%);
box-sizing: border-box;
}

.vertical:before {
height: 6px;
width: 100%;
border: 2px dotted currentColor;
mask-image: linear-gradient(to right, transparent calc(var(--lowerWidth) + var(--gap)), #FFF calc(var(--lowerWidth) + var(--gap)), #FFF calc(var(--upperWidth) - var(--gap)), transparent calc(var(--upperWidth) - var(--gap)));
}

.vertical:after {
height: 2px;
width: calc(100% - 2rem);
left: 50%;
transform: translate(-50%, -50%);
background-color: currentColor;
mask-image: linear-gradient(to right, #FFF calc(var(--lowerWidth) - var(--gap)), transparent calc(var(--lowerWidth) - var(--gap)), transparent calc(var(--upperWidth) + var(--gap)), #FFF calc(var(--upperWidth) + var(--gap)));
}

.horizontal:after, .horizontal:before {
content: '';
display: block;
position: absolute;
left: 50%;
transform: translateX(-50%);
box-sizing: border-box;
}

.horizontal:before {
width: 6px;
height: 100%;
border: 2px dotted currentColor;
mask-image: linear-gradient(to bottom, transparent calc(var(--lowerWidth) + var(--gap)), #FFF calc(var(--lowerWidth) + var(--gap)), #FFF calc(var(--upperWidth) - var(--gap)), transparent calc(var(--upperWidth) - var(--gap)));
}

.horizontal:after {
width: 2px;
height: calc(100% - 2rem);
top: 50%;
transform: translate(-50%, -50%);
background-color: currentColor;
mask-image: linear-gradient(to bottom, #FFF calc(var(--lowerWidth) - var(--gap)), transparent calc(var(--lowerWidth) - var(--gap)), transparent calc(var(--upperWidth) + var(--gap)), #FFF calc(var(--upperWidth) + var(--gap)));
}
Loading

0 comments on commit 1db2a76

Please sign in to comment.