react-headless-dock-layout 0.1.0

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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "editor.defaultFormatter": "biomejs.biome",
3
+ "editor.codeActionsOnSave": {
4
+ "source.organizeImports.biome": "explicit",
5
+ "source.fixAll.biome": "explicit"
6
+ }
7
+ }
package/biome.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/2.3.10/schema.json",
3
+ "vcs": {
4
+ "enabled": true,
5
+ "clientKind": "git",
6
+ "useIgnoreFile": true
7
+ },
8
+ "files": {
9
+ "includes": ["**", "!!**/dist"]
10
+ },
11
+ "formatter": {
12
+ "enabled": true,
13
+ "indentStyle": "space"
14
+ },
15
+ "linter": {
16
+ "enabled": true,
17
+ "rules": {
18
+ "recommended": true
19
+ }
20
+ },
21
+ "javascript": {
22
+ "formatter": {
23
+ "quoteStyle": "double"
24
+ }
25
+ },
26
+ "assist": {
27
+ "enabled": true,
28
+ "actions": {
29
+ "source": {
30
+ "organizeImports": "on"
31
+ }
32
+ }
33
+ }
34
+ }
package/index.html ADDED
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>React Headless Dock Layout</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="dist/bundle.js"></script>
11
+ </body>
12
+ </html>
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "react-headless-dock-layout",
3
+ "version": "0.1.0",
4
+ "description": "A lightweight, headless dock layout library for React.",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "import": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ },
12
+ "require": {
13
+ "types": "./dist/index.d.cts",
14
+ "default": "./dist/index.cjs"
15
+ }
16
+ }
17
+ },
18
+ "keywords": [
19
+ "react",
20
+ "headless",
21
+ "dock layout"
22
+ ],
23
+ "license": "ISC",
24
+ "devDependencies": {
25
+ "@biomejs/biome": "2.3.10",
26
+ "@total-typescript/tsconfig": "^1.0.4",
27
+ "@types/react": "^19.2.2",
28
+ "@types/react-dom": "^19.2.2",
29
+ "react": "^19.2.3",
30
+ "react-dom": "^19.2.3",
31
+ "tsup": "^8.5.1",
32
+ "typescript": "^5.9.3",
33
+ "vitest": "^4.0.14"
34
+ },
35
+ "peerDependencies": {
36
+ "react": ">=17.0.0"
37
+ },
38
+ "scripts": {
39
+ "test": "vitest",
40
+ "build": "tsup",
41
+ "type-check": "tsc --noEmit"
42
+ }
43
+ }
package/src/App.tsx ADDED
@@ -0,0 +1,119 @@
1
+ import type { LayoutNode } from ".";
2
+ import { assertNever } from "./internal/assertNever";
3
+ import { useDockLayout } from "./useDockLayout";
4
+
5
+ const INITIAL_LAYOUT: LayoutNode | null = null;
6
+
7
+ function loadInitialLayout() {
8
+ const savedLayout = localStorage.getItem("layout");
9
+ if (savedLayout === null) {
10
+ return INITIAL_LAYOUT;
11
+ }
12
+ try {
13
+ const parsed = JSON.parse(savedLayout);
14
+ return parsed.root;
15
+ } catch (error) {
16
+ console.error(error);
17
+ return INITIAL_LAYOUT;
18
+ }
19
+ }
20
+
21
+ export function App() {
22
+ const {
23
+ layoutManager,
24
+ containerRef,
25
+ layoutRects,
26
+ draggingRect,
27
+ getRectProps,
28
+ getDropZoneProps,
29
+ getDragHandleProps,
30
+ } = useDockLayout<HTMLDivElement>(loadInitialLayout());
31
+
32
+ return (
33
+ <div>
34
+ <div style={{ height: "10vh", border: "1px solid white" }}>
35
+ <button
36
+ type="button"
37
+ onClick={() => {
38
+ layoutManager.addPanel();
39
+ }}
40
+ >
41
+ Add panel
42
+ </button>
43
+
44
+ <button
45
+ type="button"
46
+ onClick={() => {
47
+ localStorage.setItem("layout", layoutManager.serialize());
48
+ }}
49
+ >
50
+ Save to Local Storage
51
+ </button>
52
+ </div>
53
+ <div
54
+ ref={containerRef}
55
+ style={{
56
+ height: "90vh",
57
+ position: "relative",
58
+ }}
59
+ >
60
+ {layoutRects.map((rect) => {
61
+ if (rect.type === "split") {
62
+ const { style, ...props } = getRectProps(rect);
63
+ return (
64
+ <div
65
+ key={rect.id}
66
+ style={{ ...style, backgroundColor: "white", fontSize: 20 }}
67
+ {...props}
68
+ ></div>
69
+ );
70
+ } else if (rect.type === "panel") {
71
+ const { style, ...props } = getRectProps(rect);
72
+ const dropZoneProps = getDropZoneProps(rect);
73
+
74
+ return (
75
+ <div
76
+ key={rect.id}
77
+ style={{
78
+ ...style,
79
+ fontSize: 20,
80
+ display: "grid",
81
+ placeItems: "center",
82
+ opacity: draggingRect?.id === rect.id ? 0.5 : 1,
83
+ }}
84
+ {...props}
85
+ >
86
+ {dropZoneProps !== null && (
87
+ <div
88
+ style={{
89
+ ...dropZoneProps.style,
90
+ backgroundColor: "#3182f6",
91
+ opacity: 0.5,
92
+ }}
93
+ />
94
+ )}
95
+
96
+ <button type="button" {...getDragHandleProps(rect)}>
97
+ Drag Handle
98
+ </button>
99
+
100
+ <button
101
+ type="button"
102
+ onClick={() => {
103
+ layoutManager.removePanel(rect.id);
104
+ }}
105
+ >
106
+ Close Panel
107
+ </button>
108
+
109
+ {rect.id}
110
+ </div>
111
+ );
112
+ } else {
113
+ assertNever(rect);
114
+ }
115
+ })}
116
+ </div>
117
+ </div>
118
+ );
119
+ }
package/src/global.css ADDED
@@ -0,0 +1,5 @@
1
+ body {
2
+ margin: 0;
3
+ background-color: black;
4
+ color: white;
5
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./strategies";
2
+ export * from "./types";
3
+ export * from "./useDockLayout";
package/src/index.tsx ADDED
@@ -0,0 +1,8 @@
1
+ import { createRoot } from "react-dom/client";
2
+ import "./global.css";
3
+ import { App } from "./App";
4
+
5
+ const domNode = document.getElementById("root")!;
6
+ const root = createRoot(domNode);
7
+
8
+ root.render(<App />);
@@ -0,0 +1,17 @@
1
+ export class EventEmitter {
2
+ private _listeners = new Set<() => void>();
3
+
4
+ subscribe(listener: () => void) {
5
+ this._listeners.add(listener);
6
+
7
+ return () => {
8
+ this._listeners.delete(listener);
9
+ };
10
+ }
11
+
12
+ emit() {
13
+ this._listeners.forEach((listener) => {
14
+ listener();
15
+ });
16
+ }
17
+ }