react-dockable-desktop 1.2.0 β 1.3.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.
- package/README.md +385 -365
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +84 -1
- package/dist/index.d.ts +84 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/styles.css +15 -15
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,365 +1,385 @@
|
|
|
1
|
-
# React Dockable Desktop
|
|
2
|
-
|
|
3
|
-
[](#)
|
|
5
|
-
[](https://felipecarrillo100.github.io/react-dockable-desktop/)
|
|
6
|
-
|
|
7
|
-
A beautiful, premium, state-of-the-art React window manager and dockable layout engine. It features fluid split-docking grids, resizable floating windows, dynamic taskbars, and tabbed panels with **zero-unmount DOM persistence** and **built-in internationalization (i18n) support**.
|
|
8
|
-
|
|
9
|
-
[**Live Interactive Demo π**](https://felipecarrillo100.github.io/react-dockable-desktop/)
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## π Key Features
|
|
14
|
-
|
|
15
|
-
* **π¦ Dockable Splits & Tab Grid**: Drag-and-drop panels to split screens or group them together into tabbed containers.
|
|
16
|
-
* **πΊοΈ Workspace Edge Docking**: Drag a tab or floating window to the outer edges of the screen (left, right, top, bottom) to instantly dock it as a full-width or full-height column/row.
|
|
17
|
-
* **πͺ Floating Windows**: Seamlessly pop panels out into floating windows with smooth drag-and-drop movement, custom resizing, maximize, and minimize behaviors.
|
|
18
|
-
* **π¨ Custom Global Style Classes**: Inject custom, global styles at the provider level (`modalClass`, `modalBodyClass`, `sidePanelClass`, `sidePanelBodyClass`, `windowClass`, and `windowBodyClass`) to style widgets uniformly.
|
|
19
|
-
* **π₯ Aspect-Ratio Modal Engine**: Modals automatically wrap and shrink-to-fit content snugly, but cap height and scroll gracefully at visual aspect-ratio thresholds (4:3 for small/medium, 16:10 for large layouts).
|
|
20
|
-
* **π¬ Zero-Unmount DOM Persistence**: Heavy widgets (like Leaflet Maps, WebGL viewports, terminal consoles, or stateful forms) maintain their DOM nodes and state. The engine moves existing DOM structures into a hidden document fragment host rather than unmounting them.
|
|
21
|
-
* **π i18n & Full RTL Support**: Native translation pipeline matching React Intl formats and **Right-to-Left (RTL) language layouts** (Arabic, Hebrew, Persian, etc.). Detection is completely automatic based on surrounding `dir="rtl"` elements, with explicit provider control options.
|
|
22
|
-
* **π οΈ Custom Header Actions**: Inject custom React components directly into tabbed headers and floating window titlebars dynamically using the `renderHeaderActions` registry property.
|
|
23
|
-
* **π Initial Letter Hover Miniatures**: Miniature preview popovers automatically display the uppercase first character of the panel title inside a theme-responsive grayish layout fallback for panels with disabled live previews.
|
|
24
|
-
* **π Glassmorphic & Modern Styling**: Sleek dark mode aesthetics, interactive micro-animations, and fluid transitions.
|
|
25
|
-
* **π Inter-Panel Pub/Sub Event Bus**: A robust messaging system for lightweight, decoupled communication between active panels.
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## π Installation
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
npm install react-dockable-desktop replace-react-contexify
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Ensure the styling for both the layout engine and context menu is imported in your main entry file (e.g., `index.js` or `main.tsx`):
|
|
36
|
-
|
|
37
|
-
```typescript
|
|
38
|
-
import 'replace-react-contexify/styles.css';
|
|
39
|
-
import 'react-dockable-desktop/styles.css';
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
## π» Running the Demo Environments
|
|
45
|
-
|
|
46
|
-
The project includes two built-in demo setups to explore and test the window manager layout features:
|
|
47
|
-
|
|
48
|
-
### 1. Leaflet & Monaco Open Source Demo (Default)
|
|
49
|
-
A clean, lightweight dashboard demo suitable for public deployment. It showcases Leaflet 2D/3D map integration (with dynamic CARTO Light/Dark tile styles mapping to dashboard theme switches) and Monaco Editor panels.
|
|
50
|
-
```bash
|
|
51
|
-
npm run dev
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### 2. LuciadRIA Earth 3D Demo (Isolated)
|
|
55
|
-
An isolated sandbox dashboard demonstrating premium 3D Earth visualizations in the `EPSG:4978` reference reference frame using LuciadRIA. *Note: Requires a valid LuciadRIA developer license locally to run.*
|
|
56
|
-
```bash
|
|
57
|
-
npm run dev:ria
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
---
|
|
61
|
-
|
|
62
|
-
## π οΈ Getting Started
|
|
63
|
-
|
|
64
|
-
### 1.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
import
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
|
236
|
-
|
|
|
237
|
-
| `
|
|
238
|
-
| `
|
|
239
|
-
| `
|
|
240
|
-
| `
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
import
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
<
|
|
279
|
-
<
|
|
280
|
-
<button onClick={
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
import
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
1
|
+
# React Dockable Desktop
|
|
2
|
+
|
|
3
|
+
[](#)
|
|
4
|
+
[](#)
|
|
5
|
+
[](https://felipecarrillo100.github.io/react-dockable-desktop/)
|
|
6
|
+
|
|
7
|
+
A beautiful, premium, state-of-the-art React window manager and dockable layout engine. It features fluid split-docking grids, resizable floating windows, dynamic taskbars, and tabbed panels with **zero-unmount DOM persistence** and **built-in internationalization (i18n) support**.
|
|
8
|
+
|
|
9
|
+
[**Live Interactive Demo π**](https://felipecarrillo100.github.io/react-dockable-desktop/)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## π Key Features
|
|
14
|
+
|
|
15
|
+
* **π¦ Dockable Splits & Tab Grid**: Drag-and-drop panels to split screens or group them together into tabbed containers.
|
|
16
|
+
* **πΊοΈ Workspace Edge Docking**: Drag a tab or floating window to the outer edges of the screen (left, right, top, bottom) to instantly dock it as a full-width or full-height column/row.
|
|
17
|
+
* **πͺ Floating Windows**: Seamlessly pop panels out into floating windows with smooth drag-and-drop movement, custom resizing, maximize, and minimize behaviors.
|
|
18
|
+
* **π¨ Custom Global Style Classes**: Inject custom, global styles at the provider level (`modalClass`, `modalBodyClass`, `sidePanelClass`, `sidePanelBodyClass`, `windowClass`, and `windowBodyClass`) to style widgets uniformly.
|
|
19
|
+
* **π₯ Aspect-Ratio Modal Engine**: Modals automatically wrap and shrink-to-fit content snugly, but cap height and scroll gracefully at visual aspect-ratio thresholds (4:3 for small/medium, 16:10 for large layouts).
|
|
20
|
+
* **π¬ Zero-Unmount DOM Persistence**: Heavy widgets (like Leaflet Maps, WebGL viewports, terminal consoles, or stateful forms) maintain their DOM nodes and state. The engine moves existing DOM structures into a hidden document fragment host rather than unmounting them.
|
|
21
|
+
* **π i18n & Full RTL Support**: Native translation pipeline matching React Intl formats and **Right-to-Left (RTL) language layouts** (Arabic, Hebrew, Persian, etc.). Detection is completely automatic based on surrounding `dir="rtl"` elements, with explicit provider control options.
|
|
22
|
+
* **π οΈ Custom Header Actions**: Inject custom React components directly into tabbed headers and floating window titlebars dynamically using the `renderHeaderActions` registry property.
|
|
23
|
+
* **π Initial Letter Hover Miniatures**: Miniature preview popovers automatically display the uppercase first character of the panel title inside a theme-responsive grayish layout fallback for panels with disabled live previews.
|
|
24
|
+
* **π Glassmorphic & Modern Styling**: Sleek dark mode aesthetics, interactive micro-animations, and fluid transitions.
|
|
25
|
+
* **π Inter-Panel Pub/Sub Event Bus**: A robust messaging system for lightweight, decoupled communication between active panels.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## π Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install react-dockable-desktop replace-react-contexify
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Ensure the styling for both the layout engine and context menu is imported in your main entry file (e.g., `index.js` or `main.tsx`):
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import 'replace-react-contexify/styles.css';
|
|
39
|
+
import 'react-dockable-desktop/styles.css';
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## π» Running the Demo Environments
|
|
45
|
+
|
|
46
|
+
The project includes two built-in demo setups to explore and test the window manager layout features:
|
|
47
|
+
|
|
48
|
+
### 1. Leaflet & Monaco Open Source Demo (Default)
|
|
49
|
+
A clean, lightweight dashboard demo suitable for public deployment. It showcases Leaflet 2D/3D map integration (with dynamic CARTO Light/Dark tile styles mapping to dashboard theme switches) and Monaco Editor panels.
|
|
50
|
+
```bash
|
|
51
|
+
npm run dev
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 2. LuciadRIA Earth 3D Demo (Isolated)
|
|
55
|
+
An isolated sandbox dashboard demonstrating premium 3D Earth visualizations in the `EPSG:4978` reference reference frame using LuciadRIA. *Note: Requires a valid LuciadRIA developer license locally to run.*
|
|
56
|
+
```bash
|
|
57
|
+
npm run dev:ria
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## π οΈ Getting Started
|
|
63
|
+
|
|
64
|
+
### 1. Create a WorkspaceClient
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { WorkspaceClient } from 'react-dockable-desktop';
|
|
68
|
+
import MapView from './panels/MapView';
|
|
69
|
+
import EditorPanel from './panels/EditorPanel';
|
|
70
|
+
|
|
71
|
+
export const client = new WorkspaceClient({
|
|
72
|
+
panels: {
|
|
73
|
+
mainMap: { component: MapView, defaultOptions: { title: 'Satellite Map View', canClose: false, canMinimize: true, canDrag: true } },
|
|
74
|
+
editor: { component: EditorPanel, defaultOptions: { title: 'Editor', canMinimize: true } },
|
|
75
|
+
},
|
|
76
|
+
// Restore last layout, or null for an empty canvas
|
|
77
|
+
initialState: localStorage.getItem('workspace-layout'),
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
> **No module-level side effects.** Panels are registered inside `WorkspaceClient`, not via a global `PanelRegistry.register` call. The client can be exported and used imperatively from anywhere: `client.openPanel(...)`, `client.saveLayout()`.
|
|
82
|
+
|
|
83
|
+
### 2. Set Up the Provider
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import React from 'react';
|
|
87
|
+
import {
|
|
88
|
+
WindowManagerProvider,
|
|
89
|
+
WindowManager,
|
|
90
|
+
PanelProvider,
|
|
91
|
+
ModalStackRenderer,
|
|
92
|
+
SidePanelRenderer,
|
|
93
|
+
} from 'react-dockable-desktop';
|
|
94
|
+
import { client } from './workspaceClient';
|
|
95
|
+
|
|
96
|
+
function App() {
|
|
97
|
+
return (
|
|
98
|
+
<WindowManagerProvider client={client}>
|
|
99
|
+
<PanelProvider>
|
|
100
|
+
<div style={{ width: '100vw', height: '100vh', display: 'flex', flexDirection: 'column' }}>
|
|
101
|
+
<WindowManager />
|
|
102
|
+
</div>
|
|
103
|
+
<ModalStackRenderer />
|
|
104
|
+
<SidePanelRenderer />
|
|
105
|
+
</PanelProvider>
|
|
106
|
+
</WindowManagerProvider>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export default App;
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
> **Backward compatibility:** The `client` prop is optional. The `PanelRegistry` global singleton and the old provider props (`formatMessage`, `predefinedMessages`, `dir`) still work as before.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## π Internationalization (i18n)
|
|
118
|
+
|
|
119
|
+
`react-dockable-desktop` is built with dynamic translation in mind. Titles and context menus can accept either raw `string` values or a structured descriptor object resembling `ContextMenuPredefinedMessage`.
|
|
120
|
+
|
|
121
|
+
### Message Descriptor Format
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
interface ContextMenuPredefinedMessage {
|
|
125
|
+
id: string;
|
|
126
|
+
defaultMessage?: string;
|
|
127
|
+
values?: Record<string, string | number>;
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Integrating custom formatters (e.g., `react-intl`)
|
|
132
|
+
|
|
133
|
+
To route messages through your application's translation engine, pass a `formatMessage` callback to `WorkspaceClient`:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
const client = new WorkspaceClient({
|
|
137
|
+
panels: { ... },
|
|
138
|
+
formatMessage: (msg) => intl.formatMessage({ id: msg.id, defaultMessage: msg.defaultMessage }, msg.values),
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
function App() {
|
|
142
|
+
return (
|
|
143
|
+
<WindowManagerProvider client={client}>
|
|
144
|
+
...
|
|
145
|
+
</WindowManagerProvider>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
> **Backward compatibility:** Passing `formatMessage` directly as a prop to `WindowManagerProvider` is still supported for existing code.
|
|
151
|
+
|
|
152
|
+
*If no `formatMessage` function is provided, the engine defaults to a fallback formatting template parser resolving placeholders like `Hello {user}` using values.*
|
|
153
|
+
|
|
154
|
+
### Right-to-Left (RTL) Layout Support
|
|
155
|
+
|
|
156
|
+
The library features built-in, theme-resilient Right-to-Left (RTL) rendering. It automatically mirrors tab stacking flows, close icon alignments, custom header buttons, side drawers (which slide in from the opposite edge), titlebars, and taskbar icons.
|
|
157
|
+
|
|
158
|
+
#### 1. Automatic Detection (Zero Configuration)
|
|
159
|
+
Simply apply the HTML `dir="rtl"` attribute to the container enclosing your layout, or globally on the `<html>`/`<body>` nodes. The workspace will observe it using a `MutationObserver` and mirror dynamically:
|
|
160
|
+
```html
|
|
161
|
+
<div dir="rtl">
|
|
162
|
+
<Desktop />
|
|
163
|
+
</div>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### 2. Explicit Programmatic Control
|
|
167
|
+
To force a specific direction regardless of the surrounding HTML structure, pass the `dir` option to `WorkspaceClient`:
|
|
168
|
+
```typescript
|
|
169
|
+
const client = new WorkspaceClient({ panels: { ... }, dir: 'rtl' });
|
|
170
|
+
<WindowManagerProvider client={client}>
|
|
171
|
+
<Desktop />
|
|
172
|
+
</WindowManagerProvider>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## ποΈ Programmatic Spawning and Layout API
|
|
178
|
+
|
|
179
|
+
Consume actions from the layout context anywhere inside or outside your panel components using the provided hooks.
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
import { useWindowManagerActions, useWindowManagerState } from 'react-dockable-desktop';
|
|
183
|
+
|
|
184
|
+
const SidebarControls = () => {
|
|
185
|
+
const { openPanel, closePanel, saveLayout, loadLayout } = useWindowManagerActions();
|
|
186
|
+
const state = useWindowManagerState();
|
|
187
|
+
|
|
188
|
+
const handleOpenConsole = () => {
|
|
189
|
+
openPanel('debug-console', 'terminal', {
|
|
190
|
+
title: { id: 'app.console', defaultMessage: 'System Console Log' },
|
|
191
|
+
initialTarget: 'floating' // Options: 'docked' | 'floating' | 'tabbed'
|
|
192
|
+
});
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
return (
|
|
196
|
+
<div>
|
|
197
|
+
<button onClick={handleOpenConsole}>Spawn Console</button>
|
|
198
|
+
<button onClick={() => alert(saveLayout())}>Backup Layout</button>
|
|
199
|
+
</div>
|
|
200
|
+
);
|
|
201
|
+
};
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Hook Reference
|
|
205
|
+
|
|
206
|
+
| Hook Name | Return Type | Description |
|
|
207
|
+
| :--- | :--- | :--- |
|
|
208
|
+
| `useWindowManagerState()` | `WindowState` | Access grid layout trees, list of active floating windows, minimized windows list, and general dragging statuses. |
|
|
209
|
+
| `useWindowManagerActions()` | `WindowActions` | Spawns, minimizes, restores, docks, floats, maximizes, or closes panels. Also handles split sizing, custom locations, and layout serialization (`saveLayout` / `loadLayout`). |
|
|
210
|
+
| `useFormatMessage()` | `(msg: ContextMenuPredefinedMessage) => string` | Returns the translation message formatter hook matching the provider preset configuration. |
|
|
211
|
+
| `usePanelContext()` | `{ publish, subscribe }` | Dynamic decoupled event bus helper for active panels. |
|
|
212
|
+
| `useStyleClasses()` | `StyleClasses` | Returns the custom CSS class overrides configured on the provider (`windowClass`, `windowBodyClass`, etc.). |
|
|
213
|
+
| `useRegistry()` | `PanelRegistryClass` | Returns the scoped panel registry for the current provider. |
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## New Exports (v1.2.0)
|
|
218
|
+
|
|
219
|
+
The following additional exports are available in v1.2.0:
|
|
220
|
+
|
|
221
|
+
- `WorkspaceClient` β class for creating a scoped workspace configuration outside React.
|
|
222
|
+
- `WorkspaceClientConfig` β TypeScript interface for the `WorkspaceClient` constructor options.
|
|
223
|
+
- `PanelDefinition` β TypeScript interface describing a single panel entry in the `panels` map.
|
|
224
|
+
- `PanelRegistryClass` β the class backing both the scoped (per-client) and global registries; useful for typing or extending the registry.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## π‘οΈ Form Container Context (Close Interception & Dirty States)
|
|
229
|
+
|
|
230
|
+
`react-dockable-desktop` provides a context-driven panel container contract to support dirty form tracking, dynamic title overrides, and close action guards. Child elements can access this container context using the `useFormContainer()` hook.
|
|
231
|
+
|
|
232
|
+
### Context Hook Functions
|
|
233
|
+
|
|
234
|
+
| Function / Property | Type | Description |
|
|
235
|
+
| :--- | :--- | :--- |
|
|
236
|
+
| `setDirty(dirty)` | `(dirty: boolean) => void` | Marks the container as dirty. An asterisk `*` will be appended to the panel title (in tabs, minimized taskbars, and floating headers). Attempting to close the panel will trigger a confirmation warning modal. |
|
|
237
|
+
| `onCloseRequested(handler)` | `(handler: () => boolean \| Promise<boolean>) => () => void` | Registers an interception handler. When the panel is closed, this function is triggered. If it returns `false`, the closing is cancelled. Returns an unregister cleanup function. |
|
|
238
|
+
| `setTitle(title)` | `(title: string \| ContextMenuPredefinedMessage) => void` | Overrides the tab / window title dynamically from the child element. |
|
|
239
|
+
| `requestClose(options)` | `(options?: { force?: boolean }) => void` | Request the parent panel container to close programmatically. If `force: true` is passed, it closes immediately, bypassing any dirty checks or guards. |
|
|
240
|
+
| `instanceId` | `string` | The unique ID of the panel. |
|
|
241
|
+
|
|
242
|
+
### Implementation Example
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
import React, { useState, useEffect } from 'react';
|
|
246
|
+
import { useFormContainer } from 'react-dockable-desktop';
|
|
247
|
+
|
|
248
|
+
const EditFormPanel: React.FC = () => {
|
|
249
|
+
const container = useFormContainer();
|
|
250
|
+
const [text, setText] = useState('');
|
|
251
|
+
|
|
252
|
+
// 1. Mark dirty when typing
|
|
253
|
+
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
254
|
+
setText(e.target.value);
|
|
255
|
+
container.setDirty(true); // Appends '*' to tab header and prompts warning on close
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const handleSave = () => {
|
|
259
|
+
container.setDirty(false); // Resets dirty state
|
|
260
|
+
alert('Saved successfully!');
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// 2. Intercept and block closing based on conditions
|
|
264
|
+
useEffect(() => {
|
|
265
|
+
const unregister = container.onCloseRequested(() => {
|
|
266
|
+
if (text.includes('BLOCK')) {
|
|
267
|
+
alert('Cannot close while the word "BLOCK" is present!');
|
|
268
|
+
return false; // Blocks closure
|
|
269
|
+
}
|
|
270
|
+
return true; // Allows closure
|
|
271
|
+
});
|
|
272
|
+
return unregister;
|
|
273
|
+
}, [container, text]);
|
|
274
|
+
|
|
275
|
+
return (
|
|
276
|
+
<div style={{ padding: '1rem', color: '#fff' }}>
|
|
277
|
+
<h5>Dynamic Form Editor</h5>
|
|
278
|
+
<textarea value={text} onChange={handleChange} />
|
|
279
|
+
<button onClick={handleSave}>Save</button>
|
|
280
|
+
<button onClick={() => container.requestClose()}>Cancel & Close</button>
|
|
281
|
+
</div>
|
|
282
|
+
);
|
|
283
|
+
};
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## π Inter-Panel Event Communication Bus
|
|
289
|
+
|
|
290
|
+
Avoid complex state management boilerplate. Active panels can broadcast lightweight messages across the workspace seamlessly:
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
import React, { useEffect } from 'react';
|
|
294
|
+
import { usePanelContext } from 'react-dockable-desktop';
|
|
295
|
+
|
|
296
|
+
// Subscriber Panel (e.g. Console Log Viewer)
|
|
297
|
+
const ConsoleView: React.FC = () => {
|
|
298
|
+
const { subscribe } = usePanelContext();
|
|
299
|
+
|
|
300
|
+
useEffect(() => {
|
|
301
|
+
const unsubscribe = subscribe('CONSOLE_LOG', (payload) => {
|
|
302
|
+
console.log('Received log message: ', payload.message);
|
|
303
|
+
});
|
|
304
|
+
return () => unsubscribe();
|
|
305
|
+
}, [subscribe]);
|
|
306
|
+
|
|
307
|
+
return <div>Console Viewer</div>;
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
// Publisher Panel (e.g. Map View)
|
|
311
|
+
const MapView: React.FC = () => {
|
|
312
|
+
const { publish } = usePanelContext();
|
|
313
|
+
|
|
314
|
+
const handleInteract = () => {
|
|
315
|
+
publish('CONSOLE_LOG', { message: 'User zoomed map viewport.' });
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
return <button onClick={handleInteract}>Click Map</button>;
|
|
319
|
+
};
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## π¨ Layout Presets & Configuration Options
|
|
325
|
+
|
|
326
|
+
You can customize defaults, positioning attributes, and sizes using the `WorkspaceClient` panels configuration:
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
const client = new WorkspaceClient({
|
|
330
|
+
panels: {
|
|
331
|
+
'unique-panel-key': {
|
|
332
|
+
component: PanelComponent,
|
|
333
|
+
defaultOptions: {
|
|
334
|
+
title: 'Default Title String', // fallback
|
|
335
|
+
canMinimize: true,
|
|
336
|
+
canDrag: true,
|
|
337
|
+
canClose: true,
|
|
338
|
+
initialTarget: 'docked', // or 'floating'
|
|
339
|
+
favoritePosition: {
|
|
340
|
+
x: 400,
|
|
341
|
+
y: 200,
|
|
342
|
+
width: 500,
|
|
343
|
+
height: 350,
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
> **Legacy / backward-compatible approach:** You can still use the global `PanelRegistry.register('unique-panel-key', PanelComponent, options)` at module scope. This singleton is fully supported for existing codebases.
|
|
352
|
+
|
|
353
|
+
To customize CSS layout attributes, you can override variables in your stylesheet:
|
|
354
|
+
```css
|
|
355
|
+
:root {
|
|
356
|
+
--accent-color: #00f0ff;
|
|
357
|
+
--bg-dark-color: #12131a;
|
|
358
|
+
--glass-bg: rgba(18, 19, 26, 0.65);
|
|
359
|
+
--border-color: rgba(255, 255, 255, 0.08);
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## π¨ Theming
|
|
366
|
+
|
|
367
|
+
The library's theming attribute is `data-color-scheme`. When switching themes in your app, set this attribute on the workspace root (or a wrapping element):
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
document.documentElement.setAttribute('data-color-scheme', 'dark'); // or 'light'
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
If you are also using Bootstrap, you may set `data-bs-theme` alongside it for Bootstrap component compatibility.
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## π Architecture
|
|
378
|
+
|
|
379
|
+
For a deep-dive into the layout tree model, DOM persistence strategy, overlay system, `WorkspaceClient` and scoped registries, empty canvas default behavior, RTL detection, and build pipeline, see [ARCHITECTURE.md](ARCHITECTURE.md).
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## π License
|
|
384
|
+
|
|
385
|
+
MIT. Free to use, adapt, and build upon.
|