quilt-react 0.1.1 → 0.2.1
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 +4 -5
- package/dist/index.d.ts +18 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +158 -54
- package/dist/index.js.map +1 -1
- package/dist/styles.css +9 -9
- package/package.json +3 -3
- package/src/index.tsx +229 -80
package/README.md
CHANGED
|
@@ -27,8 +27,7 @@ const store = new LayoutStore(
|
|
|
27
27
|
);
|
|
28
28
|
const data = { text: 'Hello' };
|
|
29
29
|
const getPaneState = () => data;
|
|
30
|
-
function Notes({ state }: PaneProps) {
|
|
31
|
-
const model = state as typeof data;
|
|
30
|
+
function Notes({ state: model }: PaneProps<typeof data>) {
|
|
32
31
|
return (
|
|
33
32
|
<textarea
|
|
34
33
|
defaultValue={model.text}
|
|
@@ -41,7 +40,7 @@ function Notes({ state }: PaneProps) {
|
|
|
41
40
|
const components = { notes: Notes };
|
|
42
41
|
const root = createRoot(document.getElementById('app')!);
|
|
43
42
|
root.render(
|
|
44
|
-
<Layout
|
|
43
|
+
<Layout<typeof data>
|
|
45
44
|
store={store}
|
|
46
45
|
components={components}
|
|
47
46
|
getPaneState={getPaneState}
|
|
@@ -56,8 +55,8 @@ function disposeWorkspace() {
|
|
|
56
55
|
}
|
|
57
56
|
```
|
|
58
57
|
|
|
59
|
-
Keep the store
|
|
58
|
+
Keep the store stable. Component maps and adapter callbacks can change identity without rebuilding the workspace. Declarative panes inherit application providers through portals. `useLayoutSnapshot(store)` observes layout changes. The model type is `LayoutSnapshot`; `Layout` is the component. `MountedLayout`, `TabRegistry`, theme presets, and DOM configuration types are available here too. Attach a `MountedLayout` ref to call `popout` from a user gesture; before mounting completes, popout/requestClose resolve false; workspace configuration methods throw a clear not-mounted error.
|
|
60
59
|
|
|
61
|
-
|
|
60
|
+
Only standalone `reactRenderer` creates separate roots requiring explicit provider wrappers. React-local state does not survive crossing documents: retain data in an application-owned store, and subscribe inside each view when live synchronization is needed. Companion windows are same-origin and depend on the main session.
|
|
62
61
|
|
|
63
62
|
The stylesheet is identical to `quilt-vanilla/styles.css`; import either once. See [API](https://github.com/niko-dellic/quilt/blob/main/docs/api.md), [lifecycle](https://github.com/niko-dellic/quilt/blob/main/docs/lifecycle.md), and [migration](https://github.com/niko-dellic/quilt/blob/main/docs/migration.md).
|
package/dist/index.d.ts
CHANGED
|
@@ -1,20 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
1
|
+
import type { ComponentType, CSSProperties, RefAttributes, ReactElement } from 'react';
|
|
2
|
+
import { PaneRegistry } from 'quilt-vanilla';
|
|
3
3
|
import type { LayoutOptions, MountedLayout, PaneContext, PaneRenderer } from 'quilt-vanilla';
|
|
4
4
|
import type { Layout as LayoutSnapshot, LayoutStore } from 'quilt-core';
|
|
5
|
-
export type PaneProps = Omit<PaneContext
|
|
5
|
+
export type PaneProps<State = unknown> = Omit<PaneContext<State>, 'element'>;
|
|
6
6
|
/** A separate root is created for each pane view, including companion documents. */
|
|
7
|
-
export declare function reactRenderer(component: ComponentType<PaneProps
|
|
8
|
-
export
|
|
9
|
-
components: Record<string, ComponentType<PaneProps>>;
|
|
7
|
+
export declare function reactRenderer<State = unknown>(component: ComponentType<PaneProps<State>>): PaneRenderer<State>;
|
|
8
|
+
export type LayoutProps<State = unknown> = Omit<LayoutOptions<State>, 'renderers' | 'registry' | 'tabs'> & {
|
|
10
9
|
className?: string;
|
|
11
10
|
style?: CSSProperties;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
} & ({
|
|
12
|
+
registry: PaneRegistry<ComponentType<PaneProps<State>>>;
|
|
13
|
+
components?: never;
|
|
14
|
+
tabs?: never;
|
|
15
|
+
} | {
|
|
16
|
+
registry?: undefined;
|
|
17
|
+
components?: Record<string, ComponentType<PaneProps<State>>>;
|
|
18
|
+
tabs?: import('quilt-vanilla').TabRegistry;
|
|
19
|
+
});
|
|
20
|
+
/** State is application-owned and shared by every registered pane in this layout. */
|
|
21
|
+
export declare const Layout: <State = unknown>(props: LayoutProps<State> & RefAttributes<MountedLayout<State>>) => ReactElement | null;
|
|
15
22
|
export declare function useLayoutSnapshot(store: LayoutStore): LayoutSnapshot;
|
|
16
23
|
export { LayoutStore, LayoutError, createLayout, parseLayout, validate } from 'quilt-core';
|
|
17
24
|
export type { AutoCollapse, LayoutStoreOptions, Json, Capability, Axis, Pane, Group, Split, Node, WindowPlacement, Popout, Layout as LayoutSnapshot, Issue, CommandOptions, Change, Bounds, JoinOptions, } from 'quilt-core';
|
|
18
25
|
export { TabRegistry, themes, themeFamilies } from 'quilt-vanilla';
|
|
19
26
|
export type { TabRegistration, LayoutTheme, LayoutOptions, MountedLayout, PaneContext, PaneView, PaneRenderer, } from 'quilt-vanilla';
|
|
27
|
+
export { PaneRegistry, parseWorkspace, dockLayout, themeProperties, defaultMessages, } from 'quilt-vanilla';
|
|
28
|
+
export type { PaneRegistration, WorkspacePreset, Messages, CloseRequest, KeyBinding, TabBarOptions, TabBarStyle, } from 'quilt-vanilla';
|
|
20
29
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAa,aAAa,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAGlG,OAAO,EAAe,YAAY,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7F,OAAO,KAAK,EAAE,MAAM,IAAI,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACxE,MAAM,MAAM,SAAS,CAAC,KAAK,GAAG,OAAO,IAAI,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,CAAC;AAsB7E,oFAAoF;AACpF,wBAAgB,aAAa,CAAC,KAAK,GAAG,OAAO,EAC3C,SAAS,EAAE,aAAa,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,GACzC,YAAY,CAAC,KAAK,CAAC,CAqCrB;AAmED,MAAM,MAAM,WAAW,CAAC,KAAK,GAAG,OAAO,IAAI,IAAI,CAC7C,aAAa,CAAC,KAAK,CAAC,EACpB,WAAW,GAAG,UAAU,GAAG,MAAM,CAClC,GAAG;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB,GAAG,CACE;IAAE,QAAQ,EAAE,YAAY,CAAC,aAAa,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAAC,UAAU,CAAC,EAAE,KAAK,CAAC;IAAC,IAAI,CAAC,EAAE,KAAK,CAAA;CAAE,GAC7F;IACE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7D,IAAI,CAAC,EAAE,OAAO,eAAe,EAAE,WAAW,CAAC;CAC5C,CACJ,CAAC;AAqIJ,qFAAqF;AACrF,eAAO,MAAM,MAAM,EAAiB,CAAC,KAAK,GAAG,OAAO,EAClD,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,KAC5D,YAAY,GAAG,IAAI,CAAC;AACzB,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,cAAc,CAEpE;AACD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3F,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,IAAI,EACJ,UAAU,EACV,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,KAAK,EACL,IAAI,EACJ,eAAe,EACf,MAAM,EACN,MAAM,IAAI,cAAc,EACxB,KAAK,EACL,cAAc,EACd,MAAM,EACN,MAAM,EACN,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnE,YAAY,EACV,eAAe,EACf,WAAW,EACX,aAAa,EACb,aAAa,EACb,WAAW,EACX,QAAQ,EACR,YAAY,GACb,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,YAAY,EACZ,cAAc,EACd,UAAU,EACV,eAAe,EACf,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,aAAa,EACb,WAAW,GACZ,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Component, createElement, forwardRef, useImperativeHandle, useLayoutEffect, useMemo, useRef, useSyncExternalStore, } from 'react';
|
|
3
3
|
import { createRoot } from 'react-dom/client';
|
|
4
|
-
import { flushSync } from 'react-dom';
|
|
5
|
-
import { mountLayout } from 'quilt-vanilla';
|
|
4
|
+
import { createPortal, flushSync } from 'react-dom';
|
|
5
|
+
import { mountLayout, PaneRegistry } from 'quilt-vanilla';
|
|
6
6
|
class Boundary extends Component {
|
|
7
7
|
state = { failed: false };
|
|
8
8
|
static getDerivedStateFromError() {
|
|
@@ -12,7 +12,9 @@ class Boundary extends Component {
|
|
|
12
12
|
this.props.onError(error);
|
|
13
13
|
}
|
|
14
14
|
render() {
|
|
15
|
-
return this.state.failed
|
|
15
|
+
return this.state.failed
|
|
16
|
+
? createElement('div', { role: 'alert' }, this.props.fallback ?? 'Pane could not be rendered.')
|
|
17
|
+
: this.props.children;
|
|
16
18
|
}
|
|
17
19
|
}
|
|
18
20
|
/** A separate root is created for each pane view, including companion documents. */
|
|
@@ -42,7 +44,7 @@ export function reactRenderer(component) {
|
|
|
42
44
|
update(pane) {
|
|
43
45
|
root.render(createElement(Boundary, {
|
|
44
46
|
onError: (error) => {
|
|
45
|
-
context.
|
|
47
|
+
context.reportError(error);
|
|
46
48
|
},
|
|
47
49
|
children: createElement(component, { ...props, pane }),
|
|
48
50
|
}));
|
|
@@ -50,73 +52,175 @@ export function reactRenderer(component) {
|
|
|
50
52
|
};
|
|
51
53
|
};
|
|
52
54
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
55
|
+
function Ready({ children, onReady }) {
|
|
56
|
+
useLayoutEffect(onReady, [onReady]);
|
|
57
|
+
return children;
|
|
58
|
+
}
|
|
59
|
+
function createBridge() {
|
|
60
|
+
let sequence = 0;
|
|
61
|
+
let entries = [];
|
|
62
|
+
const listeners = new Set();
|
|
63
|
+
const emit = () => {
|
|
64
|
+
for (const listener of listeners)
|
|
65
|
+
listener();
|
|
66
|
+
};
|
|
67
|
+
const cache = new WeakMap();
|
|
68
|
+
return {
|
|
69
|
+
subscribe: (listener) => {
|
|
70
|
+
listeners.add(listener);
|
|
71
|
+
return () => {
|
|
72
|
+
listeners.delete(listener);
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
snapshot: () => entries,
|
|
76
|
+
renderer(component) {
|
|
77
|
+
let renderer = cache.get(component);
|
|
78
|
+
if (renderer)
|
|
79
|
+
return renderer;
|
|
80
|
+
renderer = (context) => {
|
|
81
|
+
let resolve, reject;
|
|
82
|
+
const ready = new Promise((yes, no) => {
|
|
83
|
+
resolve = yes;
|
|
84
|
+
reject = no;
|
|
85
|
+
});
|
|
86
|
+
void ready.catch(() => { });
|
|
87
|
+
const entry = {
|
|
88
|
+
id: ++sequence,
|
|
89
|
+
context,
|
|
90
|
+
component,
|
|
91
|
+
ready: resolve,
|
|
92
|
+
fail: reject,
|
|
93
|
+
};
|
|
94
|
+
entries = [...entries, entry];
|
|
95
|
+
emit();
|
|
96
|
+
return {
|
|
97
|
+
ready,
|
|
98
|
+
update(pane) {
|
|
99
|
+
entry.context = { ...entry.context, pane };
|
|
100
|
+
entries = [...entries];
|
|
101
|
+
emit();
|
|
102
|
+
},
|
|
103
|
+
dispose() {
|
|
104
|
+
entries = entries.filter((value) => value !== entry);
|
|
105
|
+
emit();
|
|
106
|
+
reject(new Error('Pane disposed before readiness'));
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
cache.set(component, renderer);
|
|
111
|
+
return renderer;
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const LayoutImpl = forwardRef(function Layout(props, ref) {
|
|
116
|
+
const { store, components, registry, className, style, ...rest } = props;
|
|
117
|
+
if (registry && (components || props.tabs))
|
|
118
|
+
throw new Error('registry cannot be combined with components or tabs');
|
|
119
|
+
const bridge = useMemo(createBridge, [store]);
|
|
120
|
+
const entries = useSyncExternalStore(bridge.subscribe, bridge.snapshot, bridge.snapshot);
|
|
121
|
+
const host = useRef(null);
|
|
122
|
+
const mounted = useRef(null);
|
|
123
|
+
const latest = useRef(props);
|
|
124
|
+
latest.current = props;
|
|
125
|
+
const adapt = () => {
|
|
126
|
+
const current = latest.current;
|
|
127
|
+
const { store: _store, components: map, registry: source, className: _className, style: _style, ...options } = current;
|
|
128
|
+
const complete = Object.fromEntries([
|
|
129
|
+
'tabs',
|
|
130
|
+
'theme',
|
|
131
|
+
'tabBar',
|
|
132
|
+
'getPaneState',
|
|
133
|
+
'onError',
|
|
134
|
+
'prepareWindow',
|
|
135
|
+
'openWindow',
|
|
136
|
+
'renderIcon',
|
|
137
|
+
'shortcuts',
|
|
138
|
+
'messages',
|
|
139
|
+
'popouts',
|
|
140
|
+
'confirmClose',
|
|
141
|
+
'registry',
|
|
142
|
+
'renderers',
|
|
143
|
+
].map((key) => [key, undefined]));
|
|
144
|
+
if (source) {
|
|
145
|
+
const adapted = new PaneRegistry(source.list().map((entry) => ({ ...entry, view: bridge.renderer(entry.view) })));
|
|
146
|
+
return { ...complete, ...options, registry: adapted };
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
...complete,
|
|
150
|
+
...options,
|
|
151
|
+
renderers: Object.fromEntries(Object.entries(map ?? {}).map(([key, component]) => [key, bridge.renderer(component)])),
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
useImperativeHandle(ref, () => {
|
|
155
|
+
const instance = () => {
|
|
156
|
+
if (!mounted.current)
|
|
157
|
+
throw new Error('Layout is not mounted');
|
|
158
|
+
return mounted.current;
|
|
159
|
+
};
|
|
160
|
+
return {
|
|
161
|
+
setTheme: (theme) => instance().setTheme(theme),
|
|
162
|
+
setTabBar: (options) => instance().setTabBar(options),
|
|
163
|
+
updateOptions: (options) => instance().updateOptions(options),
|
|
164
|
+
refreshTheme: () => instance().refreshTheme(),
|
|
165
|
+
retryPane: (id) => instance().retryPane(id),
|
|
166
|
+
exportWorkspace: () => instance().exportWorkspace(),
|
|
167
|
+
loadWorkspace: (input) => instance().loadWorkspace(input),
|
|
168
|
+
requestClose: (id, kind) => mounted.current?.requestClose(id, kind) ?? Promise.resolve(false),
|
|
169
|
+
popout: (id, placement) => mounted.current?.popout(id, placement) ?? Promise.resolve(false),
|
|
170
|
+
returnPane: (id) => mounted.current?.returnPane(id),
|
|
171
|
+
dispose: () => mounted.current?.dispose(),
|
|
172
|
+
};
|
|
173
|
+
}, []);
|
|
75
174
|
useLayoutEffect(() => {
|
|
76
175
|
let cancelled = false;
|
|
77
176
|
let instance;
|
|
78
|
-
// Pane roots must mount outside the parent React commit (flushSync is forbidden there).
|
|
79
177
|
queueMicrotask(() => {
|
|
80
178
|
if (cancelled || !host.current)
|
|
81
179
|
return;
|
|
82
|
-
instance = mountLayout(host.current, {
|
|
83
|
-
store,
|
|
84
|
-
renderers,
|
|
85
|
-
...(currentTabBar.current ? { tabBar: currentTabBar.current } : {}),
|
|
86
|
-
...(tabs ? { tabs } : {}),
|
|
87
|
-
...(currentTheme.current ? { theme: currentTheme.current } : {}),
|
|
88
|
-
...(getPaneState ? { getPaneState } : {}),
|
|
89
|
-
...(onError ? { onError } : {}),
|
|
90
|
-
...(prepareWindow ? { prepareWindow } : {}),
|
|
91
|
-
...(openWindow ? { openWindow } : {}),
|
|
92
|
-
...(renderIcon ? { renderIcon } : {}),
|
|
93
|
-
...(shortcuts !== undefined ? { shortcuts } : {}),
|
|
94
|
-
});
|
|
180
|
+
instance = mountLayout(host.current, { store, ...adapt() });
|
|
95
181
|
mounted.current = instance;
|
|
96
182
|
});
|
|
97
183
|
return () => {
|
|
98
184
|
cancelled = true;
|
|
99
|
-
|
|
100
|
-
queueMicrotask(() => previous?.dispose());
|
|
101
|
-
if (mounted.current === previous)
|
|
185
|
+
if (mounted.current === instance)
|
|
102
186
|
mounted.current = null;
|
|
187
|
+
queueMicrotask(() => instance?.dispose());
|
|
103
188
|
};
|
|
189
|
+
}, [store, bridge]);
|
|
190
|
+
// Only changes to public options update the DOM adapter; bridge renders don't rebuild chrome.
|
|
191
|
+
useLayoutEffect(() => {
|
|
192
|
+
mounted.current?.updateOptions(adapt());
|
|
104
193
|
}, [
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
tabs,
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
194
|
+
components,
|
|
195
|
+
registry,
|
|
196
|
+
rest.tabs,
|
|
197
|
+
rest.theme,
|
|
198
|
+
rest.tabBar,
|
|
199
|
+
rest.getPaneState,
|
|
200
|
+
rest.onError,
|
|
201
|
+
rest.prepareWindow,
|
|
202
|
+
rest.openWindow,
|
|
203
|
+
rest.renderIcon,
|
|
204
|
+
rest.shortcuts,
|
|
205
|
+
rest.messages,
|
|
206
|
+
rest.popouts,
|
|
207
|
+
rest.confirmClose,
|
|
114
208
|
]);
|
|
115
|
-
|
|
209
|
+
useLayoutEffect(() => registry?.subscribe(() => mounted.current?.updateOptions(adapt())), [registry, bridge]);
|
|
210
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { ref: host, className: className, style: { width: '100%', height: '100%', ...style } }), entries.map((entry) => {
|
|
211
|
+
const { element, ...context } = entry.context;
|
|
212
|
+
return createPortal(_jsx(Boundary, { fallback: props.messages?.['Pane could not be rendered.'] ?? 'Pane could not be rendered.', onError: (error) => {
|
|
213
|
+
entry.fail(error);
|
|
214
|
+
entry.context.reportError(error);
|
|
215
|
+
}, children: _jsx(Ready, { onReady: entry.ready, children: createElement(entry.component, context) }) }), element, String(entry.id));
|
|
216
|
+
})] }));
|
|
116
217
|
});
|
|
218
|
+
/** State is application-owned and shared by every registered pane in this layout. */
|
|
219
|
+
export const Layout = LayoutImpl;
|
|
117
220
|
export function useLayoutSnapshot(store) {
|
|
118
221
|
return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
119
222
|
}
|
|
120
223
|
export { LayoutStore, LayoutError, createLayout, parseLayout, validate } from 'quilt-core';
|
|
121
224
|
export { TabRegistry, themes, themeFamilies } from 'quilt-vanilla';
|
|
225
|
+
export { PaneRegistry, parseWorkspace, dockLayout, themeProperties, defaultMessages, } from 'quilt-vanilla';
|
|
122
226
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"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","sourcesContent":["export type { TabBarOptions, TabBarStyle } from 'quilt-vanilla';\nimport {\n Component,\n createElement,\n forwardRef,\n useImperativeHandle,\n useLayoutEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from 'react';\nimport type { ComponentType, CSSProperties, ReactNode } from 'react';\nimport { createRoot } from 'react-dom/client';\nimport { flushSync } from 'react-dom';\nimport { mountLayout } from 'quilt-vanilla';\nimport type { LayoutOptions, MountedLayout, PaneContext, PaneRenderer } from 'quilt-vanilla';\nimport type { Layout as LayoutSnapshot, LayoutStore } from 'quilt-core';\nexport type PaneProps = Omit<PaneContext, 'element'>;\nclass Boundary extends Component<\n { children: ReactNode; onError: (error: unknown) => void },\n { failed: boolean }\n> {\n override state = { failed: false };\n static getDerivedStateFromError() {\n return { failed: true };\n }\n override componentDidCatch(error: unknown) {\n this.props.onError(error);\n }\n override render() {\n return this.state.failed ? null : this.props.children;\n }\n}\n/** A separate root is created for each pane view, including companion documents. */\nexport function reactRenderer(component: ComponentType<PaneProps>): PaneRenderer {\n return (context) => {\n const root = createRoot(context.element);\n let failure: unknown;\n const { element: _element, ...props } = context;\n try {\n flushSync(() =>\n root.render(\n createElement(Boundary, {\n onError: (error) => {\n failure = error;\n },\n children: createElement(component, props),\n }),\n ),\n );\n if (failure) throw failure;\n } catch (error) {\n root.unmount();\n throw error;\n }\n return {\n dispose() {\n root.unmount();\n },\n update(pane) {\n root.render(\n createElement(Boundary, {\n onError: (error) => {\n context.element.textContent = error instanceof Error ? error.message : String(error);\n },\n children: createElement(component, { ...props, pane }),\n }),\n );\n },\n };\n };\n}\nexport interface LayoutProps extends Omit<LayoutOptions, 'renderers'> {\n components: Record<string, ComponentType<PaneProps>>;\n className?: string;\n style?: CSSProperties;\n}\n/** Keep store, components, and adapter callbacks stable to avoid remounting the workspace. */\nexport const Layout = forwardRef<MountedLayout, LayoutProps>(function Layout(props, ref) {\n const {\n store,\n tabs,\n theme,\n tabBar,\n components,\n getPaneState,\n onError,\n prepareWindow,\n openWindow,\n renderIcon,\n shortcuts,\n className,\n style,\n } = props;\n const host = useRef<HTMLDivElement>(null),\n mounted = useRef<MountedLayout | null>(null);\n const currentTabBar = useRef(tabBar);\n currentTabBar.current = tabBar;\n useLayoutEffect(() => {\n mounted.current?.setTabBar(tabBar ?? {});\n }, [tabBar]);\n const currentTheme = useRef(theme);\n currentTheme.current = theme;\n useLayoutEffect(() => {\n mounted.current?.setTheme(theme ?? {});\n }, [theme]);\n const renderers = useMemo(\n () => Object.fromEntries(Object.entries(components).map(([id, c]) => [id, reactRenderer(c)])),\n [components],\n );\n useImperativeHandle(\n ref,\n () => ({\n setTabBar: (options) => mounted.current?.setTabBar(options),\n setTheme: (theme) => mounted.current?.setTheme(theme),\n popout: (...args) => mounted.current?.popout(...args) ?? false,\n returnPane: (id) => mounted.current?.returnPane(id),\n dispose: () => mounted.current?.dispose(),\n }),\n [],\n );\n useLayoutEffect(() => {\n let cancelled = false;\n let instance: MountedLayout | undefined;\n // Pane roots must mount outside the parent React commit (flushSync is forbidden there).\n queueMicrotask(() => {\n if (cancelled || !host.current) return;\n instance = mountLayout(host.current, {\n store,\n renderers,\n ...(currentTabBar.current ? { tabBar: currentTabBar.current } : {}),\n ...(tabs ? { tabs } : {}),\n ...(currentTheme.current ? { theme: currentTheme.current } : {}),\n ...(getPaneState ? { getPaneState } : {}),\n ...(onError ? { onError } : {}),\n ...(prepareWindow ? { prepareWindow } : {}),\n ...(openWindow ? { openWindow } : {}),\n ...(renderIcon ? { renderIcon } : {}),\n ...(shortcuts !== undefined ? { shortcuts } : {}),\n });\n mounted.current = instance;\n });\n return () => {\n cancelled = true;\n const previous = instance;\n queueMicrotask(() => previous?.dispose());\n if (mounted.current === previous) mounted.current = null;\n };\n }, [\n store,\n renderers,\n tabs,\n getPaneState,\n onError,\n prepareWindow,\n openWindow,\n renderIcon,\n shortcuts,\n ]);\n return (\n <div ref={host} className={className} style={{ width: '100%', height: '100%', ...style }} />\n );\n});\nexport function useLayoutSnapshot(store: LayoutStore): LayoutSnapshot {\n return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);\n}\nexport { LayoutStore, LayoutError, createLayout, parseLayout, validate } from 'quilt-core';\nexport type {\n AutoCollapse,\n LayoutStoreOptions,\n Json,\n Capability,\n Axis,\n Pane,\n Group,\n Split,\n Node,\n WindowPlacement,\n Popout,\n Layout as LayoutSnapshot,\n Issue,\n CommandOptions,\n Change,\n Bounds,\n JoinOptions,\n} from 'quilt-core';\nexport { TabRegistry, themes, themeFamilies } from 'quilt-vanilla';\nexport type {\n TabRegistration,\n LayoutTheme,\n LayoutOptions,\n MountedLayout,\n PaneContext,\n PaneView,\n PaneRenderer,\n} from 'quilt-vanilla';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,SAAS,EACT,aAAa,EACb,UAAU,EACV,mBAAmB,EACnB,eAAe,EACf,OAAO,EACP,MAAM,EACN,oBAAoB,GACrB,MAAM,OAAO,CAAC;AAEf,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAI1D,MAAM,QAAS,SAAQ,SAGtB;IACU,KAAK,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IACnC,MAAM,CAAC,wBAAwB;QAC7B,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC1B,CAAC;IACQ,iBAAiB,CAAC,KAAc;QACvC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;IACQ,MAAM;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM;YACtB,CAAC,CAAC,aAAa,CACX,KAAK,EACL,EAAE,IAAI,EAAE,OAAO,EAAE,EACjB,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,6BAA6B,CACrD;YACH,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC1B,CAAC;CACF;AACD,oFAAoF;AACpF,MAAM,UAAU,aAAa,CAC3B,SAA0C;IAE1C,OAAO,CAAC,OAAO,EAAE,EAAE;QACjB,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACzC,IAAI,OAAgB,CAAC;QACrB,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;QAChD,IAAI,CAAC;YACH,SAAS,CAAC,GAAG,EAAE,CACb,IAAI,CAAC,MAAM,CACT,aAAa,CAAC,QAAQ,EAAE;gBACtB,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;oBACjB,OAAO,GAAG,KAAK,CAAC;gBAClB,CAAC;gBACD,QAAQ,EAAE,aAAa,CAAC,SAAS,EAAE,KAAK,CAAC;aAC1C,CAAC,CACH,CACF,CAAC;YACF,IAAI,OAAO;gBAAE,MAAM,OAAO,CAAC;QAC7B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,MAAM,KAAK,CAAC;QACd,CAAC;QACD,OAAO;YACL,OAAO;gBACL,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,CAAC;YACD,MAAM,CAAC,IAAI;gBACT,IAAI,CAAC,MAAM,CACT,aAAa,CAAC,QAAQ,EAAE;oBACtB,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;wBACjB,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;oBAC7B,CAAC;oBACD,QAAQ,EAAE,aAAa,CAAC,SAAS,EAAE,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC;iBACvD,CAAC,CACH,CAAC;YACJ,CAAC;SACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AASD,SAAS,KAAK,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAgD;IAChF,eAAe,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACpC,OAAO,QAAQ,CAAC;AAClB,CAAC;AACD,SAAS,YAAY;IACnB,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,OAAO,GAAkB,EAAE,CAAC;IAChC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAC;IACxC,MAAM,IAAI,GAAG,GAAG,EAAE;QAChB,KAAK,MAAM,QAAQ,IAAI,SAAS;YAAE,QAAQ,EAAE,CAAC;IAC/C,CAAC,CAAC;IACF,MAAM,KAAK,GAAG,IAAI,OAAO,EAA0C,CAAC;IACpE,OAAO;QACL,SAAS,EAAE,CAAC,QAAoB,EAAE,EAAE;YAClC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACxB,OAAO,GAAG,EAAE;gBACV,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC7B,CAAC,CAAC;QACJ,CAAC;QACD,QAAQ,EAAE,GAAG,EAAE,CAAC,OAAO;QACvB,QAAQ,CAAC,SAAmC;YAC1C,IAAI,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACpC,IAAI,QAAQ;gBAAE,OAAO,QAAQ,CAAC;YAC9B,QAAQ,GAAG,CAAC,OAAO,EAAE,EAAE;gBACrB,IAAI,OAAoB,EAAE,MAAiC,CAAC;gBAC5D,MAAM,KAAK,GAAG,IAAI,OAAO,CAAO,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE;oBAC1C,OAAO,GAAG,GAAG,CAAC;oBACd,MAAM,GAAG,EAAE,CAAC;gBACd,CAAC,CAAC,CAAC;gBACH,KAAK,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;gBAC3B,MAAM,KAAK,GAAgB;oBACzB,EAAE,EAAE,EAAE,QAAQ;oBACd,OAAO;oBACP,SAAS;oBACT,KAAK,EAAE,OAAO;oBACd,IAAI,EAAE,MAAM;iBACb,CAAC;gBACF,OAAO,GAAG,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,CAAC;gBAC9B,IAAI,EAAE,CAAC;gBACP,OAAO;oBACL,KAAK;oBACL,MAAM,CAAC,IAAI;wBACT,KAAK,CAAC,OAAO,GAAG,EAAE,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;wBAC3C,OAAO,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC;wBACvB,IAAI,EAAE,CAAC;oBACT,CAAC;oBACD,OAAO;wBACL,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;wBACrD,IAAI,EAAE,CAAC;wBACP,MAAM,CAAC,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC,CAAC;oBACtD,CAAC;iBACF,CAAC;YACJ,CAAC,CAAC;YACF,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YAC/B,OAAO,QAAQ,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC;AAeD,MAAM,UAAU,GAAG,UAAU,CAA6B,SAAS,MAAM,CAAC,KAAK,EAAE,GAAG;IAClF,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC;IACzE,IAAI,QAAQ,IAAI,CAAC,UAAU,IAAI,KAAK,CAAC,IAAI,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,oBAAoB,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzF,MAAM,IAAI,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,MAAM,CAAuB,IAAI,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7B,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC;IACvB,MAAM,KAAK,GAAG,GAAG,EAAE;QACjB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;QAC/B,MAAM,EACJ,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,GAAG,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,UAAU,EACrB,KAAK,EAAE,MAAM,EACb,GAAG,OAAO,EACX,GAAG,OAAO,CAAC;QACZ,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CACjC;YACE,MAAM;YACN,OAAO;YACP,QAAQ;YACR,cAAc;YACd,SAAS;YACT,eAAe;YACf,YAAY;YACZ,YAAY;YACZ,WAAW;YACX,UAAU;YACV,SAAS;YACT,cAAc;YACd,UAAU;YACV,WAAW;SACZ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CACjC,CAAC;QACF,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,OAAO,GAAG,IAAI,YAAY,CAC9B,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAChF,CAAC;YACF,OAAO,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;QACxD,CAAC;QACD,OAAO;YACL,GAAG,QAAQ;YACX,GAAG,OAAO;YACV,SAAS,EAAE,MAAM,CAAC,WAAW,CAC3B,MAAM,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CACvF;SACF,CAAC;IACJ,CAAC,CAAC;IACF,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE;QAC5B,MAAM,QAAQ,GAAG,GAAG,EAAE;YACpB,IAAI,CAAC,OAAO,CAAC,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;YAC/D,OAAO,OAAO,CAAC,OAAO,CAAC;QACzB,CAAC,CAAC;QACF,OAAO;YACL,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC;YAC/C,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC;YACrD,aAAa,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC;YAC7D,YAAY,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,YAAY,EAAE;YAC7C,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3C,eAAe,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,eAAe,EAAE;YACnD,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC;YACzD,YAAY,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC;YAC7F,MAAM,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC;YAC3F,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC;YACnD,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE;SAC1C,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IACP,eAAe,CAAC,GAAG,EAAE;QACnB,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,IAAI,QAAmC,CAAC;QACxC,cAAc,CAAC,GAAG,EAAE;YAClB,IAAI,SAAS,IAAI,CAAC,IAAI,CAAC,OAAO;gBAAE,OAAO;YACvC,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAmB,CAAC,CAAC;YAC7E,OAAO,CAAC,OAAO,GAAG,QAAQ,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,EAAE;YACV,SAAS,GAAG,IAAI,CAAC;YACjB,IAAI,OAAO,CAAC,OAAO,KAAK,QAAQ;gBAAE,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;YACzD,cAAc,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QAC5C,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;IACpB,8FAA8F;IAC9F,eAAe,CAAC,GAAG,EAAE;QACnB,OAAO,CAAC,OAAO,EAAE,aAAa,CAAC,KAAK,EAAE,CAAC,CAAC;IAC1C,CAAC,EAAE;QACD,UAAU;QACV,QAAQ;QACR,IAAI,CAAC,IAAI;QACT,IAAI,CAAC,KAAK;QACV,IAAI,CAAC,MAAM;QACX,IAAI,CAAC,YAAY;QACjB,IAAI,CAAC,OAAO;QACZ,IAAI,CAAC,aAAa;QAClB,IAAI,CAAC,UAAU;QACf,IAAI,CAAC,UAAU;QACf,IAAI,CAAC,SAAS;QACd,IAAI,CAAC,QAAQ;QACb,IAAI,CAAC,OAAO;QACZ,IAAI,CAAC,YAAY;KAClB,CAAC,CAAC;IACH,eAAe,CACb,GAAG,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,aAAa,CAAC,KAAK,EAAE,CAAC,CAAC,EACxE,CAAC,QAAQ,EAAE,MAAM,CAAC,CACnB,CAAC;IACF,OAAO,CACL,8BACE,cAAK,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,GAAI,EAC3F,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;gBACrB,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC;gBAC9C,OAAO,YAAY,CACjB,KAAC,QAAQ,IACP,QAAQ,EACN,KAAK,CAAC,QAAQ,EAAE,CAAC,6BAA6B,CAAC,IAAI,6BAA6B,EAElF,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;wBACjB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;wBAClB,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;oBACnC,CAAC,YAED,KAAC,KAAK,IAAC,OAAO,EAAE,KAAK,CAAC,KAAK,YAAG,aAAa,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,GAAS,GACrE,EACX,OAAO,EACP,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CACjB,CAAC;YACJ,CAAC,CAAC,IACD,CACJ,CAAC;AACJ,CAAC,CAAC,CAAC;AACH,qFAAqF;AACrF,MAAM,CAAC,MAAM,MAAM,GAAG,UAEE,CAAC;AACzB,MAAM,UAAU,iBAAiB,CAAC,KAAkB;IAClD,OAAO,oBAAoB,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;AACrF,CAAC;AACD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAoB3F,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAWnE,OAAO,EACL,YAAY,EACZ,cAAc,EACd,UAAU,EACV,eAAe,EACf,eAAe,GAChB,MAAM,eAAe,CAAC","sourcesContent":["import {\n Component,\n createElement,\n forwardRef,\n useImperativeHandle,\n useLayoutEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from 'react';\nimport type { ComponentType, CSSProperties, ReactNode, RefAttributes, ReactElement } from 'react';\nimport { createRoot } from 'react-dom/client';\nimport { createPortal, flushSync } from 'react-dom';\nimport { mountLayout, PaneRegistry } from 'quilt-vanilla';\nimport type { LayoutOptions, MountedLayout, PaneContext, PaneRenderer } from 'quilt-vanilla';\nimport type { Layout as LayoutSnapshot, LayoutStore } from 'quilt-core';\nexport type PaneProps<State = unknown> = Omit<PaneContext<State>, 'element'>;\nclass Boundary extends Component<\n { children: ReactNode; onError: (error: unknown) => void; fallback?: string },\n { failed: boolean }\n> {\n override state = { failed: false };\n static getDerivedStateFromError() {\n return { failed: true };\n }\n override componentDidCatch(error: unknown) {\n this.props.onError(error);\n }\n override render() {\n return this.state.failed\n ? createElement(\n 'div',\n { role: 'alert' },\n this.props.fallback ?? 'Pane could not be rendered.',\n )\n : this.props.children;\n }\n}\n/** A separate root is created for each pane view, including companion documents. */\nexport function reactRenderer<State = unknown>(\n component: ComponentType<PaneProps<State>>,\n): PaneRenderer<State> {\n return (context) => {\n const root = createRoot(context.element);\n let failure: unknown;\n const { element: _element, ...props } = context;\n try {\n flushSync(() =>\n root.render(\n createElement(Boundary, {\n onError: (error) => {\n failure = error;\n },\n children: createElement(component, props),\n }),\n ),\n );\n if (failure) throw failure;\n } catch (error) {\n root.unmount();\n throw error;\n }\n return {\n dispose() {\n root.unmount();\n },\n update(pane) {\n root.render(\n createElement(Boundary, {\n onError: (error) => {\n context.reportError(error);\n },\n children: createElement(component, { ...props, pane }),\n }),\n );\n },\n };\n };\n}\n\ninterface PortalEntry {\n id: number;\n context: PaneContext;\n component: ComponentType<PaneProps>;\n ready: () => void;\n fail: (error: unknown) => void;\n}\nfunction Ready({ children, onReady }: { children: ReactNode; onReady: () => void }) {\n useLayoutEffect(onReady, [onReady]);\n return children;\n}\nfunction createBridge() {\n let sequence = 0;\n let entries: PortalEntry[] = [];\n const listeners = new Set<() => void>();\n const emit = () => {\n for (const listener of listeners) listener();\n };\n const cache = new WeakMap<ComponentType<PaneProps>, PaneRenderer>();\n return {\n subscribe: (listener: () => void) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n snapshot: () => entries,\n renderer(component: ComponentType<PaneProps>): PaneRenderer {\n let renderer = cache.get(component);\n if (renderer) return renderer;\n renderer = (context) => {\n let resolve!: () => void, reject!: (error: unknown) => void;\n const ready = new Promise<void>((yes, no) => {\n resolve = yes;\n reject = no;\n });\n void ready.catch(() => {});\n const entry: PortalEntry = {\n id: ++sequence,\n context,\n component,\n ready: resolve,\n fail: reject,\n };\n entries = [...entries, entry];\n emit();\n return {\n ready,\n update(pane) {\n entry.context = { ...entry.context, pane };\n entries = [...entries];\n emit();\n },\n dispose() {\n entries = entries.filter((value) => value !== entry);\n emit();\n reject(new Error('Pane disposed before readiness'));\n },\n };\n };\n cache.set(component, renderer);\n return renderer;\n },\n };\n}\nexport type LayoutProps<State = unknown> = Omit<\n LayoutOptions<State>,\n 'renderers' | 'registry' | 'tabs'\n> & {\n className?: string;\n style?: CSSProperties;\n} & (\n | { registry: PaneRegistry<ComponentType<PaneProps<State>>>; components?: never; tabs?: never }\n | {\n registry?: undefined;\n components?: Record<string, ComponentType<PaneProps<State>>>;\n tabs?: import('quilt-vanilla').TabRegistry;\n }\n );\nconst LayoutImpl = forwardRef<MountedLayout, LayoutProps>(function Layout(props, ref) {\n const { store, components, registry, className, style, ...rest } = props;\n if (registry && (components || props.tabs))\n throw new Error('registry cannot be combined with components or tabs');\n const bridge = useMemo(createBridge, [store]);\n const entries = useSyncExternalStore(bridge.subscribe, bridge.snapshot, bridge.snapshot);\n const host = useRef<HTMLDivElement>(null);\n const mounted = useRef<MountedLayout | null>(null);\n const latest = useRef(props);\n latest.current = props;\n const adapt = () => {\n const current = latest.current;\n const {\n store: _store,\n components: map,\n registry: source,\n className: _className,\n style: _style,\n ...options\n } = current;\n const complete = Object.fromEntries(\n [\n 'tabs',\n 'theme',\n 'tabBar',\n 'getPaneState',\n 'onError',\n 'prepareWindow',\n 'openWindow',\n 'renderIcon',\n 'shortcuts',\n 'messages',\n 'popouts',\n 'confirmClose',\n 'registry',\n 'renderers',\n ].map((key) => [key, undefined]),\n );\n if (source) {\n const adapted = new PaneRegistry<PaneRenderer>(\n source.list().map((entry) => ({ ...entry, view: bridge.renderer(entry.view) })),\n );\n return { ...complete, ...options, registry: adapted };\n }\n return {\n ...complete,\n ...options,\n renderers: Object.fromEntries(\n Object.entries(map ?? {}).map(([key, component]) => [key, bridge.renderer(component)]),\n ),\n };\n };\n useImperativeHandle(ref, () => {\n const instance = () => {\n if (!mounted.current) throw new Error('Layout is not mounted');\n return mounted.current;\n };\n return {\n setTheme: (theme) => instance().setTheme(theme),\n setTabBar: (options) => instance().setTabBar(options),\n updateOptions: (options) => instance().updateOptions(options),\n refreshTheme: () => instance().refreshTheme(),\n retryPane: (id) => instance().retryPane(id),\n exportWorkspace: () => instance().exportWorkspace(),\n loadWorkspace: (input) => instance().loadWorkspace(input),\n requestClose: (id, kind) => mounted.current?.requestClose(id, kind) ?? Promise.resolve(false),\n popout: (id, placement) => mounted.current?.popout(id, placement) ?? Promise.resolve(false),\n returnPane: (id) => mounted.current?.returnPane(id),\n dispose: () => mounted.current?.dispose(),\n };\n }, []);\n useLayoutEffect(() => {\n let cancelled = false;\n let instance: MountedLayout | undefined;\n queueMicrotask(() => {\n if (cancelled || !host.current) return;\n instance = mountLayout(host.current, { store, ...adapt() } as LayoutOptions);\n mounted.current = instance;\n });\n return () => {\n cancelled = true;\n if (mounted.current === instance) mounted.current = null;\n queueMicrotask(() => instance?.dispose());\n };\n }, [store, bridge]);\n // Only changes to public options update the DOM adapter; bridge renders don't rebuild chrome.\n useLayoutEffect(() => {\n mounted.current?.updateOptions(adapt());\n }, [\n components,\n registry,\n rest.tabs,\n rest.theme,\n rest.tabBar,\n rest.getPaneState,\n rest.onError,\n rest.prepareWindow,\n rest.openWindow,\n rest.renderIcon,\n rest.shortcuts,\n rest.messages,\n rest.popouts,\n rest.confirmClose,\n ]);\n useLayoutEffect(\n () => registry?.subscribe(() => mounted.current?.updateOptions(adapt())),\n [registry, bridge],\n );\n return (\n <>\n <div ref={host} className={className} style={{ width: '100%', height: '100%', ...style }} />\n {entries.map((entry) => {\n const { element, ...context } = entry.context;\n return createPortal(\n <Boundary\n fallback={\n props.messages?.['Pane could not be rendered.'] ?? 'Pane could not be rendered.'\n }\n onError={(error) => {\n entry.fail(error);\n entry.context.reportError(error);\n }}\n >\n <Ready onReady={entry.ready}>{createElement(entry.component, context)}</Ready>\n </Boundary>,\n element,\n String(entry.id),\n );\n })}\n </>\n );\n});\n/** State is application-owned and shared by every registered pane in this layout. */\nexport const Layout = LayoutImpl as <State = unknown>(\n props: LayoutProps<State> & RefAttributes<MountedLayout<State>>,\n) => ReactElement | null;\nexport function useLayoutSnapshot(store: LayoutStore): LayoutSnapshot {\n return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);\n}\nexport { LayoutStore, LayoutError, createLayout, parseLayout, validate } from 'quilt-core';\nexport type {\n AutoCollapse,\n LayoutStoreOptions,\n Json,\n Capability,\n Axis,\n Pane,\n Group,\n Split,\n Node,\n WindowPlacement,\n Popout,\n Layout as LayoutSnapshot,\n Issue,\n CommandOptions,\n Change,\n Bounds,\n JoinOptions,\n} from 'quilt-core';\nexport { TabRegistry, themes, themeFamilies } from 'quilt-vanilla';\nexport type {\n TabRegistration,\n LayoutTheme,\n LayoutOptions,\n MountedLayout,\n PaneContext,\n PaneView,\n PaneRenderer,\n} from 'quilt-vanilla';\n\nexport {\n PaneRegistry,\n parseWorkspace,\n dockLayout,\n themeProperties,\n defaultMessages,\n} from 'quilt-vanilla';\nexport type {\n PaneRegistration,\n WorkspacePreset,\n Messages,\n CloseRequest,\n KeyBinding,\n TabBarOptions,\n TabBarStyle,\n} from 'quilt-vanilla';\n"]}
|
package/dist/styles.css
CHANGED
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
line-height: 1.45;
|
|
11
11
|
isolation: isolate;
|
|
12
12
|
}
|
|
13
|
-
.layouts
|
|
14
|
-
.layouts
|
|
15
|
-
.layouts
|
|
13
|
+
.layouts [data-layouts-chrome],
|
|
14
|
+
.layouts [data-layouts-chrome]::before,
|
|
15
|
+
.layouts [data-layouts-chrome]::after {
|
|
16
16
|
box-sizing: border-box;
|
|
17
17
|
}
|
|
18
|
-
.layouts [hidden] {
|
|
18
|
+
.layouts [data-layouts-chrome][hidden] {
|
|
19
19
|
display: none !important;
|
|
20
20
|
}
|
|
21
21
|
.layouts-stage {
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
opacity: 0.4;
|
|
97
97
|
cursor: default;
|
|
98
98
|
}
|
|
99
|
-
.layouts :focus-visible {
|
|
99
|
+
.layouts :where([data-layouts-chrome]):focus-visible {
|
|
100
100
|
outline: 2px solid var(--layouts-focus, #b3decf);
|
|
101
101
|
outline-offset: -2px;
|
|
102
102
|
}
|
|
@@ -348,20 +348,20 @@
|
|
|
348
348
|
|
|
349
349
|
/* Consumer-owned scrollbar roles also apply to nested application content. */
|
|
350
350
|
.layouts,
|
|
351
|
-
.layouts
|
|
351
|
+
.layouts [data-layouts-chrome] {
|
|
352
352
|
scrollbar-width: thin;
|
|
353
353
|
scrollbar-color: var(--layouts-scrollbar-thumb, var(--layouts-line, #39414e))
|
|
354
354
|
var(--layouts-scrollbar-track, transparent);
|
|
355
355
|
}
|
|
356
|
-
.layouts ::-webkit-scrollbar {
|
|
356
|
+
.layouts [data-layouts-chrome]::-webkit-scrollbar {
|
|
357
357
|
width: var(--layouts-scrollbar-size, 6px);
|
|
358
358
|
height: var(--layouts-scrollbar-size, 6px);
|
|
359
359
|
}
|
|
360
|
-
.layouts ::-webkit-scrollbar-thumb {
|
|
360
|
+
.layouts [data-layouts-chrome]::-webkit-scrollbar-thumb {
|
|
361
361
|
background: var(--layouts-scrollbar-thumb, var(--layouts-line, #39414e));
|
|
362
362
|
border-radius: var(--layouts-radius, 5px);
|
|
363
363
|
}
|
|
364
|
-
.layouts ::-webkit-scrollbar-track {
|
|
364
|
+
.layouts [data-layouts-chrome]::-webkit-scrollbar-track {
|
|
365
365
|
background: var(--layouts-scrollbar-track, transparent);
|
|
366
366
|
}
|
|
367
367
|
.layouts-submenu {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quilt-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "React split panes and tabs with shared DOM chrome and browser window popouts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"prepack": "node ../../scripts/build-package.mjs react --dependencies"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"quilt-core": "0.
|
|
34
|
-
"quilt-vanilla": "0.
|
|
33
|
+
"quilt-core": "0.2.1",
|
|
34
|
+
"quilt-vanilla": "0.2.1"
|
|
35
35
|
},
|
|
36
36
|
"sideEffects": [
|
|
37
37
|
"**/*.css"
|
package/src/index.tsx
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export type { TabBarOptions, TabBarStyle } from 'quilt-vanilla';
|
|
2
1
|
import {
|
|
3
2
|
Component,
|
|
4
3
|
createElement,
|
|
@@ -9,15 +8,15 @@ import {
|
|
|
9
8
|
useRef,
|
|
10
9
|
useSyncExternalStore,
|
|
11
10
|
} from 'react';
|
|
12
|
-
import type { ComponentType, CSSProperties, ReactNode } from 'react';
|
|
11
|
+
import type { ComponentType, CSSProperties, ReactNode, RefAttributes, ReactElement } from 'react';
|
|
13
12
|
import { createRoot } from 'react-dom/client';
|
|
14
|
-
import { flushSync } from 'react-dom';
|
|
15
|
-
import { mountLayout } from 'quilt-vanilla';
|
|
13
|
+
import { createPortal, flushSync } from 'react-dom';
|
|
14
|
+
import { mountLayout, PaneRegistry } from 'quilt-vanilla';
|
|
16
15
|
import type { LayoutOptions, MountedLayout, PaneContext, PaneRenderer } from 'quilt-vanilla';
|
|
17
16
|
import type { Layout as LayoutSnapshot, LayoutStore } from 'quilt-core';
|
|
18
|
-
export type PaneProps = Omit<PaneContext
|
|
17
|
+
export type PaneProps<State = unknown> = Omit<PaneContext<State>, 'element'>;
|
|
19
18
|
class Boundary extends Component<
|
|
20
|
-
{ children: ReactNode; onError: (error: unknown) => void },
|
|
19
|
+
{ children: ReactNode; onError: (error: unknown) => void; fallback?: string },
|
|
21
20
|
{ failed: boolean }
|
|
22
21
|
> {
|
|
23
22
|
override state = { failed: false };
|
|
@@ -28,11 +27,19 @@ class Boundary extends Component<
|
|
|
28
27
|
this.props.onError(error);
|
|
29
28
|
}
|
|
30
29
|
override render() {
|
|
31
|
-
return this.state.failed
|
|
30
|
+
return this.state.failed
|
|
31
|
+
? createElement(
|
|
32
|
+
'div',
|
|
33
|
+
{ role: 'alert' },
|
|
34
|
+
this.props.fallback ?? 'Pane could not be rendered.',
|
|
35
|
+
)
|
|
36
|
+
: this.props.children;
|
|
32
37
|
}
|
|
33
38
|
}
|
|
34
39
|
/** A separate root is created for each pane view, including companion documents. */
|
|
35
|
-
export function reactRenderer
|
|
40
|
+
export function reactRenderer<State = unknown>(
|
|
41
|
+
component: ComponentType<PaneProps<State>>,
|
|
42
|
+
): PaneRenderer<State> {
|
|
36
43
|
return (context) => {
|
|
37
44
|
const root = createRoot(context.element);
|
|
38
45
|
let failure: unknown;
|
|
@@ -61,7 +68,7 @@ export function reactRenderer(component: ComponentType<PaneProps>): PaneRenderer
|
|
|
61
68
|
root.render(
|
|
62
69
|
createElement(Boundary, {
|
|
63
70
|
onError: (error) => {
|
|
64
|
-
context.
|
|
71
|
+
context.reportError(error);
|
|
65
72
|
},
|
|
66
73
|
children: createElement(component, { ...props, pane }),
|
|
67
74
|
}),
|
|
@@ -70,97 +77,222 @@ export function reactRenderer(component: ComponentType<PaneProps>): PaneRenderer
|
|
|
70
77
|
};
|
|
71
78
|
};
|
|
72
79
|
}
|
|
73
|
-
|
|
74
|
-
|
|
80
|
+
|
|
81
|
+
interface PortalEntry {
|
|
82
|
+
id: number;
|
|
83
|
+
context: PaneContext;
|
|
84
|
+
component: ComponentType<PaneProps>;
|
|
85
|
+
ready: () => void;
|
|
86
|
+
fail: (error: unknown) => void;
|
|
87
|
+
}
|
|
88
|
+
function Ready({ children, onReady }: { children: ReactNode; onReady: () => void }) {
|
|
89
|
+
useLayoutEffect(onReady, [onReady]);
|
|
90
|
+
return children;
|
|
91
|
+
}
|
|
92
|
+
function createBridge() {
|
|
93
|
+
let sequence = 0;
|
|
94
|
+
let entries: PortalEntry[] = [];
|
|
95
|
+
const listeners = new Set<() => void>();
|
|
96
|
+
const emit = () => {
|
|
97
|
+
for (const listener of listeners) listener();
|
|
98
|
+
};
|
|
99
|
+
const cache = new WeakMap<ComponentType<PaneProps>, PaneRenderer>();
|
|
100
|
+
return {
|
|
101
|
+
subscribe: (listener: () => void) => {
|
|
102
|
+
listeners.add(listener);
|
|
103
|
+
return () => {
|
|
104
|
+
listeners.delete(listener);
|
|
105
|
+
};
|
|
106
|
+
},
|
|
107
|
+
snapshot: () => entries,
|
|
108
|
+
renderer(component: ComponentType<PaneProps>): PaneRenderer {
|
|
109
|
+
let renderer = cache.get(component);
|
|
110
|
+
if (renderer) return renderer;
|
|
111
|
+
renderer = (context) => {
|
|
112
|
+
let resolve!: () => void, reject!: (error: unknown) => void;
|
|
113
|
+
const ready = new Promise<void>((yes, no) => {
|
|
114
|
+
resolve = yes;
|
|
115
|
+
reject = no;
|
|
116
|
+
});
|
|
117
|
+
void ready.catch(() => {});
|
|
118
|
+
const entry: PortalEntry = {
|
|
119
|
+
id: ++sequence,
|
|
120
|
+
context,
|
|
121
|
+
component,
|
|
122
|
+
ready: resolve,
|
|
123
|
+
fail: reject,
|
|
124
|
+
};
|
|
125
|
+
entries = [...entries, entry];
|
|
126
|
+
emit();
|
|
127
|
+
return {
|
|
128
|
+
ready,
|
|
129
|
+
update(pane) {
|
|
130
|
+
entry.context = { ...entry.context, pane };
|
|
131
|
+
entries = [...entries];
|
|
132
|
+
emit();
|
|
133
|
+
},
|
|
134
|
+
dispose() {
|
|
135
|
+
entries = entries.filter((value) => value !== entry);
|
|
136
|
+
emit();
|
|
137
|
+
reject(new Error('Pane disposed before readiness'));
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
cache.set(component, renderer);
|
|
142
|
+
return renderer;
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
export type LayoutProps<State = unknown> = Omit<
|
|
147
|
+
LayoutOptions<State>,
|
|
148
|
+
'renderers' | 'registry' | 'tabs'
|
|
149
|
+
> & {
|
|
75
150
|
className?: string;
|
|
76
151
|
style?: CSSProperties;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
tabBar,
|
|
85
|
-
components,
|
|
86
|
-
getPaneState,
|
|
87
|
-
onError,
|
|
88
|
-
prepareWindow,
|
|
89
|
-
openWindow,
|
|
90
|
-
renderIcon,
|
|
91
|
-
shortcuts,
|
|
92
|
-
className,
|
|
93
|
-
style,
|
|
94
|
-
} = props;
|
|
95
|
-
const host = useRef<HTMLDivElement>(null),
|
|
96
|
-
mounted = useRef<MountedLayout | null>(null);
|
|
97
|
-
const currentTabBar = useRef(tabBar);
|
|
98
|
-
currentTabBar.current = tabBar;
|
|
99
|
-
useLayoutEffect(() => {
|
|
100
|
-
mounted.current?.setTabBar(tabBar ?? {});
|
|
101
|
-
}, [tabBar]);
|
|
102
|
-
const currentTheme = useRef(theme);
|
|
103
|
-
currentTheme.current = theme;
|
|
104
|
-
useLayoutEffect(() => {
|
|
105
|
-
mounted.current?.setTheme(theme ?? {});
|
|
106
|
-
}, [theme]);
|
|
107
|
-
const renderers = useMemo(
|
|
108
|
-
() => Object.fromEntries(Object.entries(components).map(([id, c]) => [id, reactRenderer(c)])),
|
|
109
|
-
[components],
|
|
152
|
+
} & (
|
|
153
|
+
| { registry: PaneRegistry<ComponentType<PaneProps<State>>>; components?: never; tabs?: never }
|
|
154
|
+
| {
|
|
155
|
+
registry?: undefined;
|
|
156
|
+
components?: Record<string, ComponentType<PaneProps<State>>>;
|
|
157
|
+
tabs?: import('quilt-vanilla').TabRegistry;
|
|
158
|
+
}
|
|
110
159
|
);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
160
|
+
const LayoutImpl = forwardRef<MountedLayout, LayoutProps>(function Layout(props, ref) {
|
|
161
|
+
const { store, components, registry, className, style, ...rest } = props;
|
|
162
|
+
if (registry && (components || props.tabs))
|
|
163
|
+
throw new Error('registry cannot be combined with components or tabs');
|
|
164
|
+
const bridge = useMemo(createBridge, [store]);
|
|
165
|
+
const entries = useSyncExternalStore(bridge.subscribe, bridge.snapshot, bridge.snapshot);
|
|
166
|
+
const host = useRef<HTMLDivElement>(null);
|
|
167
|
+
const mounted = useRef<MountedLayout | null>(null);
|
|
168
|
+
const latest = useRef(props);
|
|
169
|
+
latest.current = props;
|
|
170
|
+
const adapt = () => {
|
|
171
|
+
const current = latest.current;
|
|
172
|
+
const {
|
|
173
|
+
store: _store,
|
|
174
|
+
components: map,
|
|
175
|
+
registry: source,
|
|
176
|
+
className: _className,
|
|
177
|
+
style: _style,
|
|
178
|
+
...options
|
|
179
|
+
} = current;
|
|
180
|
+
const complete = Object.fromEntries(
|
|
181
|
+
[
|
|
182
|
+
'tabs',
|
|
183
|
+
'theme',
|
|
184
|
+
'tabBar',
|
|
185
|
+
'getPaneState',
|
|
186
|
+
'onError',
|
|
187
|
+
'prepareWindow',
|
|
188
|
+
'openWindow',
|
|
189
|
+
'renderIcon',
|
|
190
|
+
'shortcuts',
|
|
191
|
+
'messages',
|
|
192
|
+
'popouts',
|
|
193
|
+
'confirmClose',
|
|
194
|
+
'registry',
|
|
195
|
+
'renderers',
|
|
196
|
+
].map((key) => [key, undefined]),
|
|
197
|
+
);
|
|
198
|
+
if (source) {
|
|
199
|
+
const adapted = new PaneRegistry<PaneRenderer>(
|
|
200
|
+
source.list().map((entry) => ({ ...entry, view: bridge.renderer(entry.view) })),
|
|
201
|
+
);
|
|
202
|
+
return { ...complete, ...options, registry: adapted };
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
...complete,
|
|
206
|
+
...options,
|
|
207
|
+
renderers: Object.fromEntries(
|
|
208
|
+
Object.entries(map ?? {}).map(([key, component]) => [key, bridge.renderer(component)]),
|
|
209
|
+
),
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
useImperativeHandle(ref, () => {
|
|
213
|
+
const instance = () => {
|
|
214
|
+
if (!mounted.current) throw new Error('Layout is not mounted');
|
|
215
|
+
return mounted.current;
|
|
216
|
+
};
|
|
217
|
+
return {
|
|
218
|
+
setTheme: (theme) => instance().setTheme(theme),
|
|
219
|
+
setTabBar: (options) => instance().setTabBar(options),
|
|
220
|
+
updateOptions: (options) => instance().updateOptions(options),
|
|
221
|
+
refreshTheme: () => instance().refreshTheme(),
|
|
222
|
+
retryPane: (id) => instance().retryPane(id),
|
|
223
|
+
exportWorkspace: () => instance().exportWorkspace(),
|
|
224
|
+
loadWorkspace: (input) => instance().loadWorkspace(input),
|
|
225
|
+
requestClose: (id, kind) => mounted.current?.requestClose(id, kind) ?? Promise.resolve(false),
|
|
226
|
+
popout: (id, placement) => mounted.current?.popout(id, placement) ?? Promise.resolve(false),
|
|
117
227
|
returnPane: (id) => mounted.current?.returnPane(id),
|
|
118
228
|
dispose: () => mounted.current?.dispose(),
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
);
|
|
229
|
+
};
|
|
230
|
+
}, []);
|
|
122
231
|
useLayoutEffect(() => {
|
|
123
232
|
let cancelled = false;
|
|
124
233
|
let instance: MountedLayout | undefined;
|
|
125
|
-
// Pane roots must mount outside the parent React commit (flushSync is forbidden there).
|
|
126
234
|
queueMicrotask(() => {
|
|
127
235
|
if (cancelled || !host.current) return;
|
|
128
|
-
instance = mountLayout(host.current, {
|
|
129
|
-
store,
|
|
130
|
-
renderers,
|
|
131
|
-
...(currentTabBar.current ? { tabBar: currentTabBar.current } : {}),
|
|
132
|
-
...(tabs ? { tabs } : {}),
|
|
133
|
-
...(currentTheme.current ? { theme: currentTheme.current } : {}),
|
|
134
|
-
...(getPaneState ? { getPaneState } : {}),
|
|
135
|
-
...(onError ? { onError } : {}),
|
|
136
|
-
...(prepareWindow ? { prepareWindow } : {}),
|
|
137
|
-
...(openWindow ? { openWindow } : {}),
|
|
138
|
-
...(renderIcon ? { renderIcon } : {}),
|
|
139
|
-
...(shortcuts !== undefined ? { shortcuts } : {}),
|
|
140
|
-
});
|
|
236
|
+
instance = mountLayout(host.current, { store, ...adapt() } as LayoutOptions);
|
|
141
237
|
mounted.current = instance;
|
|
142
238
|
});
|
|
143
239
|
return () => {
|
|
144
240
|
cancelled = true;
|
|
145
|
-
|
|
146
|
-
queueMicrotask(() =>
|
|
147
|
-
if (mounted.current === previous) mounted.current = null;
|
|
241
|
+
if (mounted.current === instance) mounted.current = null;
|
|
242
|
+
queueMicrotask(() => instance?.dispose());
|
|
148
243
|
};
|
|
244
|
+
}, [store, bridge]);
|
|
245
|
+
// Only changes to public options update the DOM adapter; bridge renders don't rebuild chrome.
|
|
246
|
+
useLayoutEffect(() => {
|
|
247
|
+
mounted.current?.updateOptions(adapt());
|
|
149
248
|
}, [
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
tabs,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
249
|
+
components,
|
|
250
|
+
registry,
|
|
251
|
+
rest.tabs,
|
|
252
|
+
rest.theme,
|
|
253
|
+
rest.tabBar,
|
|
254
|
+
rest.getPaneState,
|
|
255
|
+
rest.onError,
|
|
256
|
+
rest.prepareWindow,
|
|
257
|
+
rest.openWindow,
|
|
258
|
+
rest.renderIcon,
|
|
259
|
+
rest.shortcuts,
|
|
260
|
+
rest.messages,
|
|
261
|
+
rest.popouts,
|
|
262
|
+
rest.confirmClose,
|
|
159
263
|
]);
|
|
264
|
+
useLayoutEffect(
|
|
265
|
+
() => registry?.subscribe(() => mounted.current?.updateOptions(adapt())),
|
|
266
|
+
[registry, bridge],
|
|
267
|
+
);
|
|
160
268
|
return (
|
|
161
|
-
|
|
269
|
+
<>
|
|
270
|
+
<div ref={host} className={className} style={{ width: '100%', height: '100%', ...style }} />
|
|
271
|
+
{entries.map((entry) => {
|
|
272
|
+
const { element, ...context } = entry.context;
|
|
273
|
+
return createPortal(
|
|
274
|
+
<Boundary
|
|
275
|
+
fallback={
|
|
276
|
+
props.messages?.['Pane could not be rendered.'] ?? 'Pane could not be rendered.'
|
|
277
|
+
}
|
|
278
|
+
onError={(error) => {
|
|
279
|
+
entry.fail(error);
|
|
280
|
+
entry.context.reportError(error);
|
|
281
|
+
}}
|
|
282
|
+
>
|
|
283
|
+
<Ready onReady={entry.ready}>{createElement(entry.component, context)}</Ready>
|
|
284
|
+
</Boundary>,
|
|
285
|
+
element,
|
|
286
|
+
String(entry.id),
|
|
287
|
+
);
|
|
288
|
+
})}
|
|
289
|
+
</>
|
|
162
290
|
);
|
|
163
291
|
});
|
|
292
|
+
/** State is application-owned and shared by every registered pane in this layout. */
|
|
293
|
+
export const Layout = LayoutImpl as <State = unknown>(
|
|
294
|
+
props: LayoutProps<State> & RefAttributes<MountedLayout<State>>,
|
|
295
|
+
) => ReactElement | null;
|
|
164
296
|
export function useLayoutSnapshot(store: LayoutStore): LayoutSnapshot {
|
|
165
297
|
return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
166
298
|
}
|
|
@@ -194,3 +326,20 @@ export type {
|
|
|
194
326
|
PaneView,
|
|
195
327
|
PaneRenderer,
|
|
196
328
|
} from 'quilt-vanilla';
|
|
329
|
+
|
|
330
|
+
export {
|
|
331
|
+
PaneRegistry,
|
|
332
|
+
parseWorkspace,
|
|
333
|
+
dockLayout,
|
|
334
|
+
themeProperties,
|
|
335
|
+
defaultMessages,
|
|
336
|
+
} from 'quilt-vanilla';
|
|
337
|
+
export type {
|
|
338
|
+
PaneRegistration,
|
|
339
|
+
WorkspacePreset,
|
|
340
|
+
Messages,
|
|
341
|
+
CloseRequest,
|
|
342
|
+
KeyBinding,
|
|
343
|
+
TabBarOptions,
|
|
344
|
+
TabBarStyle,
|
|
345
|
+
} from 'quilt-vanilla';
|