react-dockable-desktop 3.2.0 → 4.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.
- package/README.md +137 -17
- package/dist/index.cjs +5 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +674 -210
- package/dist/index.d.ts +674 -210
- package/dist/index.js +5 -6
- package/dist/index.js.map +1 -1
- package/dist/styles.css +876 -237
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# React Dockable Desktop
|
|
1
|
+
# React Dockable Desktop
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/react-dockable-desktop)
|
|
4
4
|
[](https://www.typescriptlang.org/)
|
|
5
5
|
[](#touch--mobile)
|
|
6
6
|
[](#license)
|
|
@@ -19,7 +19,8 @@ A premium dockable layout engine for React. Build desktop-class applications wit
|
|
|
19
19
|
|
|
20
20
|
- **Split-Docking Grid** — drag panels to split any zone into rows/columns or group into tabbed containers
|
|
21
21
|
- **Workspace Edge Docking** — drag to the outer edges to dock a panel as a full-width or full-height strip
|
|
22
|
-
- **Floating Windows** — pop panels into freely resizable floating windows; 8-direction resize handles (N/NE/E/SE/S/SW/W/NW), maximize, minimize
|
|
22
|
+
- **Floating Windows** — pop panels into freely resizable floating windows; 8-direction resize handles (N/NE/E/SE/S/SW/W/NW), maximize, minimize; drag to a workspace corner to anchor it there — anchored windows stack with 8 px gaps and reposition when the viewport resizes
|
|
23
|
+
- **Panel Overlay** — per-panel overlay layer with anchored toolbars (`PanelToolbar`, `ToolbarButton`, `ToolbarToggle`, async search) and corner-anchored floating windows that stack, drag, and dock; `usePanelFloatingWindowManager()` opens N named windows dynamically from data or event handlers
|
|
23
24
|
- **Touch & Mobile Ready** — full iPad and Android support: long-press to drag tabs, touch resize, 44px coarse-pointer targets throughout
|
|
24
25
|
- **Zero-Unmount DOM Persistence** — panel DOM nodes are moved, never destroyed; WebGL, maps, terminals, and forms retain full state
|
|
25
26
|
- **i18n & RTL** — full Right-to-Left layout support; `dir="rtl"` flips every control, tab order, and drop zone automatically
|
|
@@ -27,6 +28,8 @@ A premium dockable layout engine for React. Build desktop-class applications wit
|
|
|
27
28
|
- **Imperative API** — `WorkspaceClient` opens, closes, focuses, and serializes panels from anywhere — inside or outside React
|
|
28
29
|
- **Layout Serialization** — save and restore the full workspace as a JSON string; survives page reloads
|
|
29
30
|
- **7 Built-in Skins** — VSCode, macOS, Chrome, Slate, Nord, Obsidian, Tokyo — all fully themeable via CSS variables
|
|
31
|
+
- **Toast Notifications** — imperative singleton `toast.info/success/warning/error/promise()` with queue, pause-on-hover, progress bar, and a `ToastAdapter` interface for delegating to a third-party notification library
|
|
32
|
+
- **Zero extra dependencies** — no runtime dependencies beyond React itself; everything is bundled in
|
|
30
33
|
- **TypeScript-first** — complete type definitions included; no separate `@types/` package needed
|
|
31
34
|
|
|
32
35
|
---
|
|
@@ -34,17 +37,16 @@ A premium dockable layout engine for React. Build desktop-class applications wit
|
|
|
34
37
|
## Installation
|
|
35
38
|
|
|
36
39
|
```bash
|
|
37
|
-
npm install react-dockable-desktop
|
|
40
|
+
npm install react-dockable-desktop
|
|
38
41
|
```
|
|
39
42
|
|
|
40
|
-
Import styles in your app entry file
|
|
43
|
+
Import styles in your app entry file:
|
|
41
44
|
|
|
42
45
|
```ts
|
|
43
|
-
import 'replace-react-contexify/styles.css';
|
|
44
46
|
import 'react-dockable-desktop/styles.css';
|
|
45
47
|
```
|
|
46
48
|
|
|
47
|
-
**Requirements:** React ≥ 16.8 · Node ≥ 18
|
|
49
|
+
**Requirements:** React ≥ 16.8 · Node ≥ 18 · No other runtime dependencies
|
|
48
50
|
|
|
49
51
|
---
|
|
50
52
|
|
|
@@ -132,6 +134,50 @@ export default function EditorPanel() {
|
|
|
132
134
|
}
|
|
133
135
|
```
|
|
134
136
|
|
|
137
|
+
### Lifecycle callbacks
|
|
138
|
+
|
|
139
|
+
`useFormContainer()` exposes a full push-based lifecycle API — no subscriptions to global state required:
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
import { useFormContainer } from 'react-dockable-desktop';
|
|
143
|
+
import { useEffect } from 'react';
|
|
144
|
+
|
|
145
|
+
export default function MapPanel() {
|
|
146
|
+
const {
|
|
147
|
+
containerType, // current container type at mount: 'dockable-panel' | 'floating-window'
|
|
148
|
+
onActivate, // fires when this panel becomes the globally active panel
|
|
149
|
+
onDeactivate, // fires when this panel loses active status (or is destroyed)
|
|
150
|
+
onContainerTypeChange, // fires when the panel moves between docked and floating
|
|
151
|
+
onClose, // fires just before the panel is destroyed
|
|
152
|
+
requestMinimize, // imperatively minimize this panel to the taskbar
|
|
153
|
+
getDimensions, // synchronously read current {width, height} — null until first layout
|
|
154
|
+
} = useFormContainer();
|
|
155
|
+
|
|
156
|
+
useEffect(() => {
|
|
157
|
+
const unsub = [
|
|
158
|
+
onActivate?.(() => {
|
|
159
|
+
// e.g. resume animation, reload data
|
|
160
|
+
const dims = getDimensions?.();
|
|
161
|
+
console.log('active, size:', dims);
|
|
162
|
+
}),
|
|
163
|
+
onDeactivate?.(() => {
|
|
164
|
+
// e.g. pause background work
|
|
165
|
+
}),
|
|
166
|
+
onContainerTypeChange?.((type) => {
|
|
167
|
+
// type === 'floating-window' | 'dockable-panel'
|
|
168
|
+
// e.g. trigger map.resize() after layout change
|
|
169
|
+
}),
|
|
170
|
+
onClose?.(() => {
|
|
171
|
+
// final cleanup — unsubscribe from external stores
|
|
172
|
+
}),
|
|
173
|
+
];
|
|
174
|
+
return () => unsub.forEach(fn => fn?.());
|
|
175
|
+
}, []);
|
|
176
|
+
|
|
177
|
+
return <div>Map</div>;
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
135
181
|
---
|
|
136
182
|
|
|
137
183
|
## Hooks
|
|
@@ -144,12 +190,13 @@ Call these inside any component within the `DockableDesktopProvider` tree:
|
|
|
144
190
|
| `useWindowManagerState(selector?)` | `WindowState` or selected slice | Read layout, floating windows, active panel ID |
|
|
145
191
|
| `usePanelActions()` | `PanelActions` | Open modal overlays and left/right side drawers |
|
|
146
192
|
| `usePanelContext()` | `{ publish, subscribe }` | Inter-panel typed event bus |
|
|
147
|
-
| `useFormContainer()` | `FormContainerContract` | Dirty state, close guards, dynamic
|
|
193
|
+
| `useFormContainer()` | `FormContainerContract` | Dirty state, close guards, dynamic title/icon, lifecycle callbacks (activate, deactivate, container-type change), imperative minimize, sync dimensions |
|
|
148
194
|
| `usePanelId()` | `string` | The panel's own instance ID — no prop drilling needed |
|
|
149
195
|
| `useToolbar()` | `ToolbarContextValue` | Read/write Toolbar state (active tool, modifiers) from any panel |
|
|
150
196
|
| `useSidebar()` | `SidebarContextValue` | Open/close Sidebar tabs from any component in the Sidebar tree |
|
|
151
197
|
| `useSidebarTab()` | `SidebarTabContext` | Self-control for content inside a Sidebar tab |
|
|
152
198
|
| `usePanelContextMenu(items)` | `void` | Inject dynamic context menu items into this panel's right-click menu |
|
|
199
|
+
| `usePanelFloatingWindowManager()` | `PanelFloatingWindowManagerHandle` | Open/close N named floating windows inside a panel overlay at runtime; each independently anchored, dockable, and resizable |
|
|
153
200
|
| `useRegistry()` | `PanelRegistryClass` | The scoped panel registry for the current provider |
|
|
154
201
|
| `useFormatMessage()` | `MessageFormatter` | i18n formatter matching the current provider's locale |
|
|
155
202
|
|
|
@@ -169,10 +216,10 @@ const panelCount = useWindowManagerState(s => Object.keys(s.panels).length);
|
|
|
169
216
|
const workspace = new WorkspaceClient({ panels, initialState?, formatMessage?, dir? });
|
|
170
217
|
|
|
171
218
|
// Panel lifecycle
|
|
172
|
-
workspace.openPanel(id, component, options?) // options: title, initialTarget,
|
|
219
|
+
workspace.openPanel(id, component, options?) // options: title, initialTarget, anchor
|
|
173
220
|
workspace.closePanel(id)
|
|
174
221
|
workspace.focusPanel(id) // raises floating / selects tab for docked
|
|
175
|
-
workspace.floatPanel(id, rect?)
|
|
222
|
+
workspace.floatPanel(id, rect?, anchor?) // detach to a floating window; optional corner anchor
|
|
176
223
|
workspace.dockPanel(id) // return floating to the grid
|
|
177
224
|
workspace.minimizePanel(id)
|
|
178
225
|
workspace.restorePanel(id)
|
|
@@ -200,6 +247,47 @@ workspace.setDirection('ltr' | 'rtl')
|
|
|
200
247
|
|
|
201
248
|
---
|
|
202
249
|
|
|
250
|
+
## FormContainerContract Reference
|
|
251
|
+
|
|
252
|
+
`useFormContainer()` returns a `FormContainerContract` with these members:
|
|
253
|
+
|
|
254
|
+
| Member | Type | Description |
|
|
255
|
+
| :--- | :--- | :--- |
|
|
256
|
+
| `requestClose(options?)` | `(options?: CloseOptions) => void` | Request the container to close; respects dirty state and close guards |
|
|
257
|
+
| `setDirty(dirty, options?)` | `(dirty: boolean) => void` | Mark unsaved changes; triggers confirmation dialog on close |
|
|
258
|
+
| `onCloseRequested(handler)` | `(handler) => unsubscribe` | Register a close guard; return `false` to block the close |
|
|
259
|
+
| `setTitle(title)` | `(title) => void` | Change the tab/window title dynamically |
|
|
260
|
+
| `setIcon?(icon)` | `(icon: ReactNode) => void` | Change the tab/window icon dynamically |
|
|
261
|
+
| `containerType?` | `ContainerType` | Container type **at mount time** — see `onContainerTypeChange` for live updates |
|
|
262
|
+
| `instanceId` | `string` | The panel's instance ID |
|
|
263
|
+
| `onClose?(handler)` | `(handler) => unsubscribe` | Subscribe to panel destruction |
|
|
264
|
+
| `onMinimize?(handler)` | `(handler) => unsubscribe` | Subscribe to minimize events |
|
|
265
|
+
| `onRestore?(handler)` | `(handler) => unsubscribe` | Subscribe to restore-from-taskbar events |
|
|
266
|
+
| `onResize?(handler)` | `(handler) => unsubscribe` | Subscribe to resize events; handler receives `(width, height)` |
|
|
267
|
+
| `requestMinimize?()` | `() => void` | Imperatively minimize this panel to the taskbar |
|
|
268
|
+
| `getDimensions?()` | `() => {width, height} \| null` | Synchronously read the current rendered size; `null` until first layout |
|
|
269
|
+
| `onActivate?(handler)` | `(handler) => unsubscribe` | Subscribe to this panel becoming the globally active panel |
|
|
270
|
+
| `onDeactivate?(handler)` | `(handler) => unsubscribe` | Subscribe to this panel losing active status; also fires on destruction |
|
|
271
|
+
| `onContainerTypeChange?(handler)` | `(handler) => unsubscribe` | Subscribe to dock↔float transitions; handler receives the new `ContainerType` |
|
|
272
|
+
|
|
273
|
+
### ContainerType
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
type ContainerType =
|
|
277
|
+
| 'dockable-panel' // panel is docked in the grid
|
|
278
|
+
| 'floating-window' // panel is in a detached floating window
|
|
279
|
+
| 'left-panel' // rendered inside the left side drawer
|
|
280
|
+
| 'right-panel' // rendered inside the right side drawer
|
|
281
|
+
| 'modal' // rendered inside a modal overlay
|
|
282
|
+
| 'standalone'; // rendered outside the Window Manager (default / no context)
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
`containerType` on the contract reflects the state **at mount time**. Subscribe to `onContainerTypeChange` to get notified whenever the panel moves between `'dockable-panel'` and `'floating-window'`. Minimize/restore cycles do **not** fire `onContainerTypeChange`; use `onMinimize`/`onRestore` for those.
|
|
286
|
+
|
|
287
|
+
All `on*` subscribers return an unsubscribe function. Call it (or return it from `useEffect`) to avoid leaks.
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
203
291
|
## Layout Persistence
|
|
204
292
|
|
|
205
293
|
```ts
|
|
@@ -260,19 +348,28 @@ Touch support is built in for v3.1.0+. No extra setup required:
|
|
|
260
348
|
|
|
261
349
|
## i18n & RTL
|
|
262
350
|
|
|
351
|
+
The library does **not** auto-detect direction — the consuming app owns it. Two things must be wired together:
|
|
352
|
+
|
|
263
353
|
```tsx
|
|
354
|
+
// 1. Keep html[dir] in sync for portals (ContextMenu, flyout, Toast)
|
|
355
|
+
// that render into document.body and need CSS direction inheritance.
|
|
356
|
+
useEffect(() => {
|
|
357
|
+
document.documentElement.dir = isRtl ? 'rtl' : 'ltr';
|
|
358
|
+
}, [isRtl]);
|
|
359
|
+
|
|
360
|
+
// 2. Pass dir prop to the provider — controls workspace layout engine.
|
|
264
361
|
<DockableDesktopProvider
|
|
265
|
-
dir=
|
|
362
|
+
dir={isRtl ? 'rtl' : 'ltr'}
|
|
266
363
|
formatMessage={(msg) => intl.formatMessage({ id: msg.id, defaultMessage: msg.defaultMessage })}
|
|
267
364
|
predefinedMessages={customMessages}
|
|
268
365
|
>
|
|
269
366
|
```
|
|
270
367
|
|
|
271
|
-
`dir` can be `'ltr'` (default) or `'rtl'`. All layout, split directions, tab ordering, drop zones, and context menus flip automatically.
|
|
368
|
+
`dir` can be `'ltr'` (default) or `'rtl'`. All layout, split directions, tab ordering, floating window controls, drop zones, sidebars, and context menus flip automatically.
|
|
272
369
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
370
|
+
Direction is **independent of locale** — you can have Arabic translations with LTR layout, or RTL without locale changes.
|
|
371
|
+
|
|
372
|
+
See the [RTL Support guide](https://felipecarrillo100.github.io/react-dockable-desktop/guide/rtl) for the complete wiring pattern and macOS skin notes.
|
|
276
373
|
|
|
277
374
|
---
|
|
278
375
|
|
|
@@ -301,6 +398,27 @@ All built-in skins include dark and light variants. Create your own skin by over
|
|
|
301
398
|
|
|
302
399
|
## What's New
|
|
303
400
|
|
|
401
|
+
### v4.1.0
|
|
402
|
+
- **`onActivate` / `onDeactivate` on `FormContainerContract`** — push-based callbacks fired when a panel gains or loses globally active status; no need to subscribe to `useWindowManagerState` and diff `activePanelId` inside each panel.
|
|
403
|
+
- **`onContainerTypeChange` on `FormContainerContract`** — fires with the new `ContainerType` (`'dockable-panel'` | `'floating-window'`) whenever the panel transitions between docked and floating; does not fire during minimize/restore cycles.
|
|
404
|
+
- **`getDimensions()` on `FormContainerContract`** — synchronous getter returning the current `{width, height}` of the panel (or `null` before first layout); reads from the same `ResizeObserver` that drives `onResize`.
|
|
405
|
+
- **`requestMinimize()` on `FormContainerContract`** — imperative counterpart to `requestClose()`; minimizes the panel without needing `useWindowManagerActions`.
|
|
406
|
+
- **`'floating-window'` ContainerType value** — `containerType` now correctly reports `'dockable-panel'` for docked panels and `'floating-window'` for panels in detached floating windows.
|
|
407
|
+
|
|
408
|
+
### v4.0.0
|
|
409
|
+
- **Built-in `<ContextMenu>`** — zero-dependency context menu, portal-rendered and styled with design tokens. No extra package or CSS import needed.
|
|
410
|
+
- **`ContextMenuAdapter` interface** — swap the default menu for your own design-system implementation via `<WindowManager contextMenuAdapter={...} />`.
|
|
411
|
+
- **New exports** — `ContextMenu`, `DefaultContextMenuAdapter`, `ContextMenuHandle`, `ContextMenuAdapter`, `ShowContextMenuOptions`.
|
|
412
|
+
- **Panel Overlay system** — `PanelOverlayRoot`, `PanelToolbar`, `PanelFloatingWindow`, and `usePanelFloatingWindowManager` bring per-panel toolbars and dynamically-spawned corner-anchored floating info windows to any panel.
|
|
413
|
+
- **`usePanelFloatingWindowManager()`** — imperative hook for opening N named floating windows from data or event handlers; all windows share z-ordering, drag, and corner-docking infrastructure.
|
|
414
|
+
- **Toolbar primitives** — `ToolbarButton`, `ToolbarToggle`, `PanelToolbarSeparator`, `ToolbarSpacer`, `ToolbarCenter`, `ToolbarSearchInput` compose panel toolbar content.
|
|
415
|
+
- **Toast Notifications** — zero-dependency `toast.info/success/warning/error/promise()` singleton. `<ToastContainer>` renders via `createPortal`; supports configurable position, width, max-visible queue, pause-on-hover, opt-in progress bar, and auto-dismiss. `ToastAdapter` lets you delegate to Ant Design, MUI, Sonner, or any other notification library without changing call sites.
|
|
416
|
+
- **Workspace corner anchor zones** — drag any panel (floating or docked tab) to a workspace corner to pin it there. Four 80×80 px snap zones appear during drag; anchored windows stack with 8 px gaps and reposition automatically on viewport resize.
|
|
417
|
+
- **`anchor` option on `openPanel` and `floatPanel`** — spawn a new floating window pre-anchored: `openPanel('id', 'comp', { initialTarget: 'floating', anchor: 'top-right' })` or `floatPanel('id', undefined, 'bottom-left')`.
|
|
418
|
+
- **`defaultAnchor` in panel registry** — set `defaultAnchor: 'top-left'` in `PanelRegistryEntry.defaultOptions` so every instance of that component opens anchored.
|
|
419
|
+
- **Full RTL support** — floating window drop zones, edge triggers, and corner snap zones all mirror correctly when `dir="rtl"`.
|
|
420
|
+
- **Removed:** `openPanel` options `stickyRight` / `stickyBottom` (replaced by `anchor`). Saved layouts are automatically migrated.
|
|
421
|
+
|
|
304
422
|
### v3.2.0
|
|
305
423
|
- **Per-skin active state design language** — Sidebar tabs and Toolbar buttons now use a per-skin visual pattern (transparent bar, floating chip, pill, line, neon glow), driven by new CSS design tokens — fully overridable in custom skins. CSS-only, no API changes.
|
|
306
424
|
- **Documentation overhaul** — All guides updated to cover the full v3.1.0 API surface.
|
|
@@ -331,7 +449,7 @@ All built-in skins include dark and light variants. Create your own skin by over
|
|
|
331
449
|
| `bringToFront(id)` | `focusPanel(id)` — works for both floating and docked panels |
|
|
332
450
|
| `setActivePanel(id)` | `focusPanel(id)` |
|
|
333
451
|
|
|
334
|
-
Full details in the [Migration Guide](https://felipecarrillo100.github.io/react-dockable-desktop/guide/migration).
|
|
452
|
+
Full details in the [Migration Guide](https://felipecarrillo100.github.io/react-dockable-desktop/guide/migration) and the [Changelog](https://github.com/felipecarrillo100/react-dockable-desktop/releases).
|
|
335
453
|
|
|
336
454
|
---
|
|
337
455
|
|
|
@@ -354,7 +472,9 @@ Complete guides, API reference, and interactive demo at:
|
|
|
354
472
|
| [Theming](https://felipecarrillo100.github.io/react-dockable-desktop/guide/theming) | CSS variables, custom skins, dark/light modes |
|
|
355
473
|
| [Advanced Topics](https://felipecarrillo100.github.io/react-dockable-desktop/guide/advanced) | RTL, multiple workspaces, custom header actions |
|
|
356
474
|
| [Best Practices](https://felipecarrillo100.github.io/react-dockable-desktop/guide/best-practices) | Patterns for production-ready implementations |
|
|
357
|
-
| [
|
|
475
|
+
| [Panel Overlay](https://felipecarrillo100.github.io/react-dockable-desktop/guide/panel-overlay) | `PanelOverlayRoot`, panel toolbars, `PanelFloatingWindow`, `usePanelFloatingWindowManager` |
|
|
476
|
+
| [Toast Notifications](https://felipecarrillo100.github.io/react-dockable-desktop/guide/toast) | `toast` singleton, `<ToastContainer>`, queue behaviour, theming, `ToastAdapter` |
|
|
477
|
+
| [Migration Guide](https://felipecarrillo100.github.io/react-dockable-desktop/guide/migration) | Upgrade from v1 → v2 → v3 → v4 |
|
|
358
478
|
| [API Reference](https://felipecarrillo100.github.io/react-dockable-desktop/api/) | Full type-level reference for all exports |
|
|
359
479
|
|
|
360
480
|
---
|