dockview 6.6.0 → 7.0.2
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 +33 -26
- package/dist/cjs/index.d.ts +0 -9
- package/dist/cjs/index.js +12 -7
- package/dist/dockview.js +20486 -914
- package/dist/dockview.min.js +16 -2
- package/dist/dockview.min.js.map +1 -1
- package/dist/esm/index.d.ts +0 -9
- package/dist/esm/index.js +12 -7
- package/dist/package/main.cjs.js +856 -941
- package/dist/package/main.cjs.min.js +8 -2
- package/dist/package/main.esm.min.mjs +8 -2
- package/dist/package/main.esm.mjs +854 -934
- package/dist/styles/dockview.css +275 -13
- package/package.json +16 -12
- package/dist/cjs/dockview/defaultTab.d.ts +0 -7
- package/dist/cjs/dockview/defaultTab.js +0 -99
- package/dist/cjs/dockview/dockview.d.ts +0 -33
- package/dist/cjs/dockview/dockview.js +0 -285
- package/dist/cjs/dockview/headerActionsRenderer.d.ts +0 -24
- package/dist/cjs/dockview/headerActionsRenderer.js +0 -86
- package/dist/cjs/dockview/reactContentPart.d.ts +0 -21
- package/dist/cjs/dockview/reactContentPart.js +0 -52
- package/dist/cjs/dockview/reactContextMenuItemPart.d.ts +0 -14
- package/dist/cjs/dockview/reactContextMenuItemPart.js +0 -31
- package/dist/cjs/dockview/reactGroupDragGhostPart.d.ts +0 -20
- package/dist/cjs/dockview/reactGroupDragGhostPart.js +0 -32
- package/dist/cjs/dockview/reactHeaderPart.d.ts +0 -17
- package/dist/cjs/dockview/reactHeaderPart.js +0 -46
- package/dist/cjs/dockview/reactTabGroupChipPart.d.ts +0 -23
- package/dist/cjs/dockview/reactTabGroupChipPart.js +0 -36
- package/dist/cjs/dockview/reactWatermarkPart.d.ts +0 -18
- package/dist/cjs/dockview/reactWatermarkPart.js +0 -47
- package/dist/cjs/gridview/gridview.d.ts +0 -17
- package/dist/cjs/gridview/gridview.js +0 -105
- package/dist/cjs/gridview/view.d.ts +0 -9
- package/dist/cjs/gridview/view.js +0 -41
- package/dist/cjs/paneview/paneview.d.ts +0 -20
- package/dist/cjs/paneview/paneview.js +0 -137
- package/dist/cjs/paneview/view.d.ts +0 -19
- package/dist/cjs/paneview/view.js +0 -44
- package/dist/cjs/react.d.ts +0 -35
- package/dist/cjs/react.js +0 -183
- package/dist/cjs/splitview/splitview.d.ts +0 -17
- package/dist/cjs/splitview/splitview.js +0 -105
- package/dist/cjs/splitview/view.d.ts +0 -9
- package/dist/cjs/splitview/view.js +0 -39
- package/dist/cjs/svg.d.ts +0 -3
- package/dist/cjs/svg.js +0 -15
- package/dist/cjs/types.d.ts +0 -4
- package/dist/cjs/types.js +0 -2
- package/dist/esm/dockview/defaultTab.d.ts +0 -7
- package/dist/esm/dockview/defaultTab.js +0 -65
- package/dist/esm/dockview/dockview.d.ts +0 -33
- package/dist/esm/dockview/dockview.js +0 -252
- package/dist/esm/dockview/headerActionsRenderer.d.ts +0 -24
- package/dist/esm/dockview/headerActionsRenderer.js +0 -72
- package/dist/esm/dockview/reactContentPart.d.ts +0 -21
- package/dist/esm/dockview/reactContentPart.js +0 -43
- package/dist/esm/dockview/reactContextMenuItemPart.d.ts +0 -14
- package/dist/esm/dockview/reactContextMenuItemPart.js +0 -22
- package/dist/esm/dockview/reactGroupDragGhostPart.d.ts +0 -20
- package/dist/esm/dockview/reactGroupDragGhostPart.js +0 -23
- package/dist/esm/dockview/reactHeaderPart.d.ts +0 -17
- package/dist/esm/dockview/reactHeaderPart.js +0 -37
- package/dist/esm/dockview/reactTabGroupChipPart.d.ts +0 -23
- package/dist/esm/dockview/reactTabGroupChipPart.js +0 -27
- package/dist/esm/dockview/reactWatermarkPart.d.ts +0 -18
- package/dist/esm/dockview/reactWatermarkPart.js +0 -38
- package/dist/esm/gridview/gridview.d.ts +0 -17
- package/dist/esm/gridview/gridview.js +0 -72
- package/dist/esm/gridview/view.d.ts +0 -9
- package/dist/esm/gridview/view.js +0 -19
- package/dist/esm/paneview/paneview.d.ts +0 -20
- package/dist/esm/paneview/paneview.js +0 -104
- package/dist/esm/paneview/view.d.ts +0 -19
- package/dist/esm/paneview/view.js +0 -35
- package/dist/esm/react.d.ts +0 -35
- package/dist/esm/react.js +0 -134
- package/dist/esm/splitview/splitview.d.ts +0 -17
- package/dist/esm/splitview/splitview.js +0 -72
- package/dist/esm/splitview/view.d.ts +0 -9
- package/dist/esm/splitview/view.js +0 -17
- package/dist/esm/svg.d.ts +0 -3
- package/dist/esm/svg.js +0 -7
- package/dist/esm/types.d.ts +0 -4
- package/dist/esm/types.js +0 -1
package/dist/package/main.cjs.js
CHANGED
|
@@ -1,1045 +1,960 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* dockview
|
|
3
|
-
* @version
|
|
3
|
+
* @version 7.0.2
|
|
4
4
|
* @link https://github.com/mathuo/dockview
|
|
5
5
|
* @license MIT
|
|
6
6
|
*/
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
10
|
+
|
|
9
11
|
var dockviewCore = require('dockview-core');
|
|
10
|
-
var React = require('react');
|
|
11
|
-
var ReactDOM = require('react-dom');
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* the ref of props we just set on the renderered component as the props passed to the inner
|
|
19
|
-
* component
|
|
20
|
-
*/
|
|
21
|
-
const ReactComponentBridge = (props, ref) => {
|
|
22
|
-
const [, triggerRender] = React.useState(0);
|
|
23
|
-
const _props = React.useRef(props.componentProps);
|
|
24
|
-
React.useImperativeHandle(ref, () => ({
|
|
25
|
-
update: (componentProps) => {
|
|
26
|
-
_props.current = Object.assign(Object.assign({}, _props.current), componentProps);
|
|
27
|
-
/**
|
|
28
|
-
* setting a arbitrary piece of state within this component will
|
|
29
|
-
* trigger a re-render.
|
|
30
|
-
* we use this rather than updating through a prop since we can
|
|
31
|
-
* pass a ref into the vanilla-js world.
|
|
32
|
-
*
|
|
33
|
-
* Use a monotonic counter rather than `Date.now()` so two
|
|
34
|
-
* updates within the same millisecond still produce distinct
|
|
35
|
-
* state values and avoid React's bailout.
|
|
36
|
-
*/
|
|
37
|
-
triggerRender((n) => n + 1);
|
|
38
|
-
},
|
|
39
|
-
}), []);
|
|
40
|
-
return React.createElement(props.component, _props.current);
|
|
41
|
-
};
|
|
42
|
-
ReactComponentBridge.displayName = 'DockviewReactJsBridge';
|
|
43
|
-
/**
|
|
44
|
-
* Since we are storing the React.Portal references in a rendered array they
|
|
45
|
-
* require a key property like any other React element rendered in an array
|
|
46
|
-
* to prevent excessive re-rendering
|
|
14
|
+
* dockview-modules
|
|
15
|
+
* @version 7.0.2
|
|
16
|
+
* @link https://github.com/mathuo/dockview
|
|
17
|
+
* @license MIT
|
|
47
18
|
*/
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
19
|
+
|
|
20
|
+
class TabGroupChipsService {
|
|
21
|
+
constructor(host) {
|
|
22
|
+
this._host = host;
|
|
23
|
+
}
|
|
24
|
+
attachToGroup(group) {
|
|
25
|
+
return new dockviewCore.DockviewCompositeDisposable(group.model.onDidCreateTabGroup((e) => {
|
|
26
|
+
this._host.fireDidCreateTabGroup(e);
|
|
27
|
+
}), group.model.onDidDestroyTabGroup((e) => {
|
|
28
|
+
this._host.fireDidDestroyTabGroup(e);
|
|
29
|
+
}), group.model.onDidAddPanelToTabGroup((e) => {
|
|
30
|
+
this._host.fireDidAddPanelToTabGroup(e);
|
|
31
|
+
}), group.model.onDidRemovePanelFromTabGroup((e) => {
|
|
32
|
+
this._host.fireDidRemovePanelFromTabGroup(e);
|
|
33
|
+
}), group.model.onDidTabGroupChange((e) => {
|
|
34
|
+
this._host.fireDidTabGroupChange(e);
|
|
35
|
+
}), group.model.onDidTabGroupCollapsedChange((e) => {
|
|
36
|
+
this._host.fireDidTabGroupCollapsedChange(e);
|
|
37
|
+
}));
|
|
63
38
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
throw new Error('invalid operation: resource is already disposed');
|
|
67
|
-
}
|
|
68
|
-
if (!this.componentInstance) {
|
|
69
|
-
// if the component is yet to be mounted store the props
|
|
70
|
-
this._initialProps = Object.assign(Object.assign({}, this._initialProps), props);
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
this.componentInstance.update(props);
|
|
74
|
-
}
|
|
39
|
+
dispose() {
|
|
40
|
+
// No internal state to tear down — emitters live on the host.
|
|
75
41
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
this.componentInstance.update(this._initialProps);
|
|
96
|
-
this._initialProps = {}; // don't keep a reference to the users object once no longer required
|
|
42
|
+
}
|
|
43
|
+
const TabGroupChipsModule = dockviewCore.defineModule({
|
|
44
|
+
name: 'TabGroupChips',
|
|
45
|
+
serviceKey: 'tabGroupChipsService',
|
|
46
|
+
create: (host) => new TabGroupChipsService(host),
|
|
47
|
+
init: (host, service) => {
|
|
48
|
+
// Self-attach to existing and future groups; tear down when groups
|
|
49
|
+
// are removed. Component doesn't need to know about this wiring.
|
|
50
|
+
const perGroupDisposables = new Map();
|
|
51
|
+
return new dockviewCore.DockviewCompositeDisposable(host.onDidAddGroup((group) => {
|
|
52
|
+
perGroupDisposables.set(group, service.attachToGroup(group));
|
|
53
|
+
}), host.onDidRemoveGroup((group) => {
|
|
54
|
+
var _a;
|
|
55
|
+
(_a = perGroupDisposables.get(group)) === null || _a === void 0 ? void 0 : _a.dispose();
|
|
56
|
+
perGroupDisposables.delete(group);
|
|
57
|
+
}), {
|
|
58
|
+
dispose: () => {
|
|
59
|
+
for (const d of perGroupDisposables.values()) {
|
|
60
|
+
d.dispose();
|
|
97
61
|
}
|
|
62
|
+
perGroupDisposables.clear();
|
|
98
63
|
},
|
|
99
64
|
});
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
function popoverZIndexFor(target) {
|
|
69
|
+
if (!(target instanceof HTMLElement)) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
// Floating overlays live in the shell as siblings of the popover anchor
|
|
73
|
+
// and the AriaLevelTracker sets their inline z-index. Without this, a
|
|
74
|
+
// popover opened from inside a floating group would render behind it
|
|
75
|
+
// because they share the shell stacking context.
|
|
76
|
+
const relativeParent = dockviewCore.findRelativeZIndexParent(target);
|
|
77
|
+
return (relativeParent === null || relativeParent === void 0 ? void 0 : relativeParent.style.zIndex)
|
|
78
|
+
? `calc(${relativeParent.style.zIndex} * 2)`
|
|
79
|
+
: undefined;
|
|
114
80
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
*/
|
|
120
|
-
const usePortalsLifecycle = () => {
|
|
121
|
-
const [portals, setPortals] = React.useState([]);
|
|
122
|
-
React.useDebugValue(`Portal count: ${portals.length}`);
|
|
123
|
-
const addPortal = React.useCallback((portal) => {
|
|
124
|
-
setPortals((existingPortals) => [...existingPortals, portal]);
|
|
125
|
-
let disposed = false;
|
|
126
|
-
return dockviewCore.DockviewDisposable.from(() => {
|
|
127
|
-
if (disposed) {
|
|
128
|
-
throw new Error('invalid operation: resource already disposed');
|
|
129
|
-
}
|
|
130
|
-
disposed = true;
|
|
131
|
-
setPortals((existingPortals) => existingPortals.filter((p) => p !== portal));
|
|
132
|
-
});
|
|
133
|
-
}, []);
|
|
134
|
-
return [portals, addPortal];
|
|
135
|
-
};
|
|
136
|
-
function isReactComponent(component) {
|
|
137
|
-
/**
|
|
138
|
-
* Yes, we could use "react-is" but that would introduce an unwanted peer dependency
|
|
139
|
-
* so for now we will check in a rather crude fashion...
|
|
140
|
-
*/
|
|
141
|
-
return (typeof component === 'function' /** Functional Componnts */ ||
|
|
142
|
-
!!(component === null || component === void 0 ? void 0 : component.$$typeof) /** React.memo(...) Components */);
|
|
81
|
+
let _nextId = 0;
|
|
82
|
+
const nextContextMenuItemId = () => `dv-ctx-menu-item-${_nextId++}`;
|
|
83
|
+
function isItemConfig(item) {
|
|
84
|
+
return typeof item === 'object';
|
|
143
85
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
this._element.style.width = '100%';
|
|
161
|
-
}
|
|
162
|
-
focus() {
|
|
163
|
-
// TODO
|
|
164
|
-
}
|
|
165
|
-
init(parameters) {
|
|
166
|
-
this.part = new ReactPart(this.element, this.reactPortalStore, this.component, {
|
|
167
|
-
params: parameters.params,
|
|
168
|
-
api: parameters.api,
|
|
169
|
-
containerApi: parameters.containerApi,
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
update(event) {
|
|
173
|
-
var _a;
|
|
174
|
-
(_a = this.part) === null || _a === void 0 ? void 0 : _a.update({ params: event.params });
|
|
175
|
-
}
|
|
176
|
-
layout(_width, _height) {
|
|
177
|
-
// noop
|
|
178
|
-
}
|
|
179
|
-
dispose() {
|
|
180
|
-
var _a;
|
|
181
|
-
this._onDidFocus.dispose();
|
|
182
|
-
this._onDidBlur.dispose();
|
|
183
|
-
(_a = this.part) === null || _a === void 0 ? void 0 : _a.dispose();
|
|
184
|
-
}
|
|
86
|
+
function buildItem(label, close, action, disabled) {
|
|
87
|
+
const el = document.createElement('div');
|
|
88
|
+
el.className = 'dv-context-menu-item';
|
|
89
|
+
el.setAttribute('role', 'menuitem');
|
|
90
|
+
if (disabled) {
|
|
91
|
+
el.classList.add('dv-context-menu-item--disabled');
|
|
92
|
+
el.setAttribute('aria-disabled', 'true');
|
|
93
|
+
}
|
|
94
|
+
el.textContent = label;
|
|
95
|
+
if (!disabled) {
|
|
96
|
+
el.addEventListener('click', () => {
|
|
97
|
+
action();
|
|
98
|
+
close();
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return el;
|
|
185
102
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
constructor(id, component, reactPortalStore) {
|
|
192
|
-
this.id = id;
|
|
193
|
-
this.component = component;
|
|
194
|
-
this.reactPortalStore = reactPortalStore;
|
|
195
|
-
this._element = document.createElement('div');
|
|
196
|
-
this._element.className = 'dv-react-part';
|
|
197
|
-
this._element.style.height = '100%';
|
|
198
|
-
this._element.style.width = '100%';
|
|
199
|
-
}
|
|
200
|
-
focus() {
|
|
201
|
-
//noop
|
|
202
|
-
}
|
|
203
|
-
init(parameters) {
|
|
204
|
-
this.part = new ReactPart(this.element, this.reactPortalStore, this.component, {
|
|
205
|
-
params: parameters.params,
|
|
206
|
-
api: parameters.api,
|
|
207
|
-
containerApi: parameters.containerApi,
|
|
208
|
-
tabLocation: parameters.tabLocation,
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
update(event) {
|
|
212
|
-
var _a;
|
|
213
|
-
(_a = this.part) === null || _a === void 0 ? void 0 : _a.update({ params: event.params });
|
|
214
|
-
}
|
|
215
|
-
layout(_width, _height) {
|
|
216
|
-
// noop - retrieval from api
|
|
217
|
-
}
|
|
218
|
-
dispose() {
|
|
219
|
-
var _a;
|
|
220
|
-
(_a = this.part) === null || _a === void 0 ? void 0 : _a.dispose();
|
|
221
|
-
}
|
|
103
|
+
function buildSeparator() {
|
|
104
|
+
const el = document.createElement('div');
|
|
105
|
+
el.className = 'dv-context-menu-separator';
|
|
106
|
+
el.setAttribute('role', 'separator');
|
|
107
|
+
return el;
|
|
222
108
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
return this._element;
|
|
227
|
-
}
|
|
228
|
-
constructor(id, component, reactPortalStore) {
|
|
229
|
-
this.id = id;
|
|
230
|
-
this.component = component;
|
|
231
|
-
this.reactPortalStore = reactPortalStore;
|
|
232
|
-
this._element = document.createElement('div');
|
|
233
|
-
this._element.className = 'dv-react-part';
|
|
234
|
-
this._element.style.height = '100%';
|
|
235
|
-
this._element.style.width = '100%';
|
|
109
|
+
function isCoarsePrimaryInput() {
|
|
110
|
+
if (typeof window === 'undefined' || !window.matchMedia) {
|
|
111
|
+
return false;
|
|
236
112
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
113
|
+
const coarse = window.matchMedia('(pointer: coarse)').matches;
|
|
114
|
+
const fine = window.matchMedia('(pointer: fine)').matches;
|
|
115
|
+
return coarse && !fine;
|
|
116
|
+
}
|
|
117
|
+
function buildRenameInput(tabGroup) {
|
|
118
|
+
const wrapper = document.createElement('div');
|
|
119
|
+
wrapper.className = 'dv-context-menu-rename';
|
|
120
|
+
const input = document.createElement('input');
|
|
121
|
+
input.className = 'dv-context-menu-rename-input';
|
|
122
|
+
input.type = 'text';
|
|
123
|
+
input.placeholder = 'Name This Group';
|
|
124
|
+
input.value = tabGroup.label;
|
|
125
|
+
input.addEventListener('input', () => {
|
|
126
|
+
tabGroup.setLabel(input.value);
|
|
127
|
+
});
|
|
128
|
+
input.addEventListener('keydown', (e) => {
|
|
129
|
+
if (e.key !== 'Escape' && e.key !== 'Enter') {
|
|
130
|
+
e.stopPropagation();
|
|
250
131
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
132
|
+
});
|
|
133
|
+
input.addEventListener('click', (e) => {
|
|
134
|
+
e.stopPropagation();
|
|
135
|
+
});
|
|
136
|
+
wrapper.appendChild(input);
|
|
137
|
+
// Skip auto-focus on touch-primary devices: focusing the input pops the
|
|
138
|
+
// on-screen keyboard, which fires `window resize`, which `PopupService`
|
|
139
|
+
// listens to and uses to dismiss the popover — so the menu opens, the
|
|
140
|
+
// keyboard appears, and the menu immediately closes before the user can
|
|
141
|
+
// type. The user can still tap the input to focus it intentionally.
|
|
142
|
+
if (!isCoarsePrimaryInput()) {
|
|
143
|
+
requestAnimationFrame(() => {
|
|
144
|
+
input.focus();
|
|
145
|
+
input.select();
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return wrapper;
|
|
260
149
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
return
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
150
|
+
function buildColorPicker(tabGroup, palette) {
|
|
151
|
+
const wrapper = document.createElement('div');
|
|
152
|
+
wrapper.className = 'dv-context-menu-color-picker';
|
|
153
|
+
if (!palette.enabled) {
|
|
154
|
+
// Opt-out: render no swatches. Returning a wrapper rather than null
|
|
155
|
+
// keeps the call site simple; the wrapper is empty and visually inert.
|
|
156
|
+
return wrapper;
|
|
157
|
+
}
|
|
158
|
+
for (const entry of palette.entries()) {
|
|
159
|
+
const swatch = document.createElement('div');
|
|
160
|
+
swatch.className = 'dv-context-menu-color-swatch';
|
|
161
|
+
// Use a CSS custom property rather than setting `backgroundColor`
|
|
162
|
+
// directly: the IDL setter validates the value against a color
|
|
163
|
+
// grammar and rejects `var(...)` references in some environments
|
|
164
|
+
// (notably jsdom; some browsers have historically had similar
|
|
165
|
+
// quirks). The matching SCSS rule reads the var at use time.
|
|
166
|
+
swatch.style.setProperty('--dv-tab-group-color', entry.value);
|
|
167
|
+
if (entry.label) {
|
|
168
|
+
swatch.title = entry.label;
|
|
169
|
+
}
|
|
170
|
+
if (tabGroup.color === entry.id) {
|
|
171
|
+
swatch.classList.add('dv-context-menu-color-swatch--selected');
|
|
172
|
+
}
|
|
173
|
+
swatch.addEventListener('click', () => {
|
|
174
|
+
tabGroup.setColor(entry.id);
|
|
175
|
+
});
|
|
176
|
+
wrapper.appendChild(swatch);
|
|
177
|
+
}
|
|
178
|
+
return wrapper;
|
|
179
|
+
}
|
|
180
|
+
class ContextMenuController {
|
|
181
|
+
constructor(accessor) {
|
|
182
|
+
this.accessor = accessor;
|
|
278
183
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
this.
|
|
288
|
-
|
|
289
|
-
this.updateLocation(event.location);
|
|
290
|
-
}));
|
|
291
|
-
this._part = new ReactPart(this.element, this.reactPortalStore, this.component, {
|
|
292
|
-
api: parameters.api,
|
|
293
|
-
containerApi: parameters.containerApi,
|
|
294
|
-
panels: this._group.model.panels,
|
|
295
|
-
activePanel: this._group.model.activePanel,
|
|
296
|
-
isGroupActive: this._group.api.isActive,
|
|
297
|
-
group: this._group,
|
|
298
|
-
headerPosition: this._group.model.headerPosition,
|
|
299
|
-
location: parameters.api.location,
|
|
184
|
+
show(panel, group, event) {
|
|
185
|
+
var _a, _b;
|
|
186
|
+
if (!this.accessor.options.getTabContextMenuItems) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const items = this.accessor.options.getTabContextMenuItems({
|
|
190
|
+
panel,
|
|
191
|
+
group,
|
|
192
|
+
api: this.accessor.api,
|
|
193
|
+
event,
|
|
300
194
|
});
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
(
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
195
|
+
if (items.length === 0) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
event.preventDefault();
|
|
199
|
+
const popupService = this.accessor.getPopupServiceForGroup(group);
|
|
200
|
+
const close = () => popupService.close();
|
|
201
|
+
const menuEl = document.createElement('div');
|
|
202
|
+
menuEl.className = 'dv-context-menu';
|
|
203
|
+
menuEl.setAttribute('role', 'menu');
|
|
204
|
+
for (const item of items) {
|
|
205
|
+
if (item === 'separator') {
|
|
206
|
+
menuEl.appendChild(buildSeparator());
|
|
207
|
+
}
|
|
208
|
+
else if (item === 'close') {
|
|
209
|
+
menuEl.appendChild(buildItem('Close', close, () => panel.api.close()));
|
|
210
|
+
}
|
|
211
|
+
else if (item === 'closeOthers') {
|
|
212
|
+
menuEl.appendChild(buildItem('Close Others', close, () => {
|
|
213
|
+
group.panels
|
|
214
|
+
.filter((p) => p !== panel)
|
|
215
|
+
.forEach((p) => p.api.close());
|
|
216
|
+
}));
|
|
217
|
+
}
|
|
218
|
+
else if (item === 'closeAll') {
|
|
219
|
+
menuEl.appendChild(buildItem('Close All', close, () => {
|
|
220
|
+
[...group.panels].forEach((p) => p.api.close());
|
|
221
|
+
}));
|
|
222
|
+
}
|
|
223
|
+
else if (isItemConfig(item) && item.element) {
|
|
224
|
+
menuEl.appendChild(item.element);
|
|
225
|
+
}
|
|
226
|
+
else if (isItemConfig(item) && item.component) {
|
|
227
|
+
const renderer = (_b = (_a = this.accessor.options).createContextMenuItemComponent) === null || _b === void 0 ? void 0 : _b.call(_a, {
|
|
228
|
+
id: nextContextMenuItemId(),
|
|
229
|
+
component: item.component,
|
|
230
|
+
});
|
|
231
|
+
if (renderer) {
|
|
232
|
+
renderer.init({
|
|
233
|
+
panel,
|
|
234
|
+
group,
|
|
235
|
+
api: this.accessor.api,
|
|
236
|
+
close,
|
|
237
|
+
componentProps: item.componentProps,
|
|
238
|
+
});
|
|
239
|
+
menuEl.appendChild(renderer.element);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
else if (isItemConfig(item) && item.label) {
|
|
243
|
+
menuEl.appendChild(buildItem(item.label, close, () => { var _a; return (_a = item.action) === null || _a === void 0 ? void 0 : _a.call(item); }, item.disabled));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
popupService.openPopover(menuEl, {
|
|
247
|
+
x: event.clientX,
|
|
248
|
+
y: event.clientY,
|
|
249
|
+
zIndex: popoverZIndexFor(event.target),
|
|
319
250
|
});
|
|
320
251
|
}
|
|
321
|
-
|
|
322
|
-
this.
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
252
|
+
showForChip(tabGroup, group, event) {
|
|
253
|
+
if (!this.accessor.options.getTabGroupChipContextMenuItems) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const items = this.accessor.options.getTabGroupChipContextMenuItems({
|
|
257
|
+
tabGroup,
|
|
258
|
+
group,
|
|
259
|
+
api: this.accessor.api,
|
|
260
|
+
event,
|
|
261
|
+
});
|
|
262
|
+
if (items.length === 0) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
event.preventDefault();
|
|
266
|
+
const popupService = this.accessor.getPopupServiceForGroup(group);
|
|
267
|
+
const close = () => popupService.close();
|
|
268
|
+
const menuEl = document.createElement('div');
|
|
269
|
+
menuEl.className = 'dv-context-menu';
|
|
270
|
+
menuEl.setAttribute('role', 'menu');
|
|
271
|
+
for (const item of items) {
|
|
272
|
+
if (item === 'separator') {
|
|
273
|
+
menuEl.appendChild(buildSeparator());
|
|
274
|
+
}
|
|
275
|
+
else if (item === 'rename') {
|
|
276
|
+
menuEl.appendChild(buildRenameInput(tabGroup));
|
|
277
|
+
}
|
|
278
|
+
else if (item === 'colorPicker') {
|
|
279
|
+
menuEl.appendChild(buildColorPicker(tabGroup, this.accessor.tabGroupColorPalette));
|
|
280
|
+
}
|
|
281
|
+
else if (isItemConfig(item) && item.element) {
|
|
282
|
+
menuEl.appendChild(item.element);
|
|
283
|
+
}
|
|
284
|
+
else if (isItemConfig(item) && item.label) {
|
|
285
|
+
menuEl.appendChild(buildItem(item.label, close, () => { var _a; return (_a = item.action) === null || _a === void 0 ? void 0 : _a.call(item); }, item.disabled));
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
popupService.openPopover(menuEl, {
|
|
289
|
+
x: event.clientX,
|
|
290
|
+
y: event.clientY,
|
|
291
|
+
zIndex: popoverZIndexFor(event.target),
|
|
326
292
|
});
|
|
327
|
-
}
|
|
328
|
-
updateLocation(location) {
|
|
329
|
-
this.update({ params: { location } });
|
|
330
293
|
}
|
|
331
294
|
}
|
|
295
|
+
const ContextMenuModule = dockviewCore.defineModule({
|
|
296
|
+
name: 'ContextMenu',
|
|
297
|
+
serviceKey: 'contextMenuService',
|
|
298
|
+
create: (host) => new ContextMenuController(host),
|
|
299
|
+
});
|
|
332
300
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
301
|
+
/** Cursor offset of the group drag ghost, matched to the long-shipped default. */
|
|
302
|
+
const GROUP_DRAG_GHOST_OFFSET_X = 30;
|
|
303
|
+
const GROUP_DRAG_GHOST_OFFSET_Y = -10;
|
|
304
|
+
/**
|
|
305
|
+
* The narrow surface the {@link AdvancedDnDService} needs from the host
|
|
306
|
+
* (the `DockviewComponent`).
|
|
307
|
+
*
|
|
308
|
+
* The `onWill*` emitters stay on the component so the public event shape is
|
|
309
|
+
* unchanged whether or not this module is registered — the service is only the
|
|
310
|
+
* dispatch point those fires are routed through. Engine policy (e.g. the
|
|
311
|
+
* `disableDnd` guard) stays on the component, ahead of the dispatch.
|
|
312
|
+
*/
|
|
313
|
+
/**
|
|
314
|
+
* Owns the dispatch of the advanced drag-and-drop hooks — `onWillDragPanel`,
|
|
315
|
+
* `onWillDragGroup`, `onWillDrop` and `onWillShowOverlay`.
|
|
316
|
+
*
|
|
317
|
+
* At this stage the service is a thin dispatcher that forwards to the host's
|
|
318
|
+
* emitters; the customisation surface it gates (custom drop overlays, custom
|
|
319
|
+
* group drag ghosts, hook veto/transform behaviour) is currently inlined in
|
|
320
|
+
* core and is extracted into this service in later phases. Routing the
|
|
321
|
+
* dispatch through a module slot is what lets that customisation layer move
|
|
322
|
+
* into a separately-distributed package in a future major version without
|
|
323
|
+
* changing the public event surface.
|
|
324
|
+
*
|
|
325
|
+
* The service holds no drag state of its own — the gesture is driven by the
|
|
326
|
+
* DnD backends, and the per-group subscriptions live on the component's group
|
|
327
|
+
* lifecycle (so groups created mid-move are not missed).
|
|
328
|
+
*/
|
|
329
|
+
class AdvancedDnDService {
|
|
330
|
+
constructor(host) {
|
|
331
|
+
this.host = host;
|
|
336
332
|
}
|
|
337
|
-
|
|
338
|
-
this.
|
|
339
|
-
this.component = component;
|
|
340
|
-
this.reactPortalStore = reactPortalStore;
|
|
341
|
-
this._element = document.createElement('div');
|
|
342
|
-
this._element.className = 'dv-react-part';
|
|
343
|
-
this._element.style.height = '100%';
|
|
344
|
-
this._element.style.width = '100%';
|
|
333
|
+
dispatchWillDragPanel(event) {
|
|
334
|
+
this.host.fireWillDragPanel(event);
|
|
345
335
|
}
|
|
346
|
-
|
|
347
|
-
this.
|
|
336
|
+
dispatchWillDragGroup(event) {
|
|
337
|
+
this.host.fireWillDragGroup(event);
|
|
348
338
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
(_a = this.part) === null || _a === void 0 ? void 0 : _a.dispose();
|
|
339
|
+
dispatchWillDrop(event) {
|
|
340
|
+
this.host.fireWillDrop(event);
|
|
352
341
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
class ReactTabGroupChipPart {
|
|
356
|
-
get element() {
|
|
357
|
-
return this._element;
|
|
342
|
+
dispatchWillShowOverlay(event) {
|
|
343
|
+
this.host.fireWillShowOverlay(event);
|
|
358
344
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
345
|
+
buildGroupDragGhost(group) {
|
|
346
|
+
const createGhost = this.host.options.createGroupDragGhostComponent;
|
|
347
|
+
if (!createGhost) {
|
|
348
|
+
return undefined;
|
|
349
|
+
}
|
|
350
|
+
const renderer = createGhost(group);
|
|
351
|
+
renderer.init({ group, api: this.host.api });
|
|
352
|
+
return {
|
|
353
|
+
element: renderer.element,
|
|
354
|
+
offsetX: GROUP_DRAG_GHOST_OFFSET_X,
|
|
355
|
+
offsetY: GROUP_DRAG_GHOST_OFFSET_Y,
|
|
356
|
+
dispose: renderer.dispose ? () => { var _a; return (_a = renderer.dispose) === null || _a === void 0 ? void 0 : _a.call(renderer); } : undefined,
|
|
357
|
+
};
|
|
365
358
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
api: params.api,
|
|
370
|
-
});
|
|
359
|
+
resolveOverlayModel(location, group) {
|
|
360
|
+
var _a, _b;
|
|
361
|
+
return (_b = (_a = this.host.options).dropOverlayModel) === null || _b === void 0 ? void 0 : _b.call(_a, { location, group });
|
|
371
362
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
363
|
+
showPreviewOverlay(group, position) {
|
|
364
|
+
const target = group.model.contentDropTarget;
|
|
365
|
+
target.showOverlay(position);
|
|
366
|
+
return dockviewCore.DockviewDisposable.from(() => target.clearOverlay());
|
|
375
367
|
}
|
|
376
368
|
dispose() {
|
|
377
|
-
|
|
378
|
-
(_a = this.part) === null || _a === void 0 ? void 0 : _a.dispose();
|
|
369
|
+
// no-op — see class doc: the service holds no state to tear down.
|
|
379
370
|
}
|
|
380
371
|
}
|
|
372
|
+
const AdvancedDnDModule = dockviewCore.defineModule({
|
|
373
|
+
name: 'AdvancedDnD',
|
|
374
|
+
serviceKey: 'advancedDnDService',
|
|
375
|
+
create: (host) => new AdvancedDnDService(host),
|
|
376
|
+
});
|
|
381
377
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
378
|
+
/**
|
|
379
|
+
* Marks (on the dockview root element) that a keyboard move is in progress, so
|
|
380
|
+
* the default navigation listener stands down while the advanced docking module
|
|
381
|
+
* drives the keys. A neutral DOM signal keeps the two listeners coordinated
|
|
382
|
+
* without either service holding a reference to the other.
|
|
383
|
+
*/
|
|
384
|
+
const KEYBOARD_MOVE_ATTRIBUTE = 'data-dv-kbd-moving';
|
|
385
|
+
/**
|
|
386
|
+
* Does `e` match a binding string like `'ctrl+]'` / `'shift+f6'`? Modifiers are
|
|
387
|
+
* matched exactly (a binding without `shift` will not fire while Shift is held),
|
|
388
|
+
* and the final part is compared to `KeyboardEvent.key`, lower-cased.
|
|
389
|
+
*/
|
|
390
|
+
function matchesBinding(e, binding) {
|
|
391
|
+
const parts = binding.toLowerCase().split('+');
|
|
392
|
+
const key = parts[parts.length - 1];
|
|
393
|
+
const mods = parts.slice(0, -1);
|
|
394
|
+
return (e.ctrlKey === mods.includes('ctrl') &&
|
|
395
|
+
e.shiftKey === mods.includes('shift') &&
|
|
396
|
+
e.altKey === mods.includes('alt') &&
|
|
397
|
+
e.metaKey === (mods.includes('meta') || mods.includes('cmd')) &&
|
|
398
|
+
e.key.toLowerCase() === key);
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Resolve the `keyboardNavigation` opt-in to its options object, or `undefined`
|
|
402
|
+
* when keyboard support is off. Both keyboard modules read the same opt-in.
|
|
403
|
+
*/
|
|
404
|
+
function readKeyboardNavigation(options) {
|
|
405
|
+
const opt = options.keyboardNavigation;
|
|
406
|
+
if (!opt) {
|
|
407
|
+
return undefined;
|
|
402
408
|
}
|
|
409
|
+
return opt === true ? {} : opt;
|
|
403
410
|
}
|
|
404
411
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
412
|
+
const DEFAULT_KEYMAP$1 = {
|
|
413
|
+
nextTab: 'ctrl+]',
|
|
414
|
+
prevTab: 'ctrl+[',
|
|
415
|
+
focusNextGroup: 'f6',
|
|
416
|
+
focusPrevGroup: 'shift+f6',
|
|
417
|
+
focusTabs: 'ctrl+shift+\\',
|
|
418
|
+
};
|
|
419
|
+
/**
|
|
420
|
+
* Keyboard navigation & focus management — operate the dock without a mouse.
|
|
421
|
+
* Opt-in via `keyboardNavigation`, with a rebindable {@link DockviewKeybindings}
|
|
422
|
+
* keymap.
|
|
423
|
+
*
|
|
424
|
+
* - **Switch tab** (`Ctrl+]` / `Ctrl+[`) — cycle the focused group's tabs.
|
|
425
|
+
* - **Focus group** (`F6` / `Shift+F6`) — move focus to the next / previous
|
|
426
|
+
* group in sequence.
|
|
427
|
+
* - **Focus the tab strip** (`Ctrl+Shift+\`) — jump focus from panel content to
|
|
428
|
+
* the active group's tab strip (the strip's roving-tabindex takes over).
|
|
429
|
+
* - **Focus restore on close** — when removing a panel/group pulls focus out of
|
|
430
|
+
* the dock, focus returns to the neighbour the layout just activated instead
|
|
431
|
+
* of being stranded on `<body>`.
|
|
432
|
+
* - **Floating `Esc`** — `Esc` inside a floating group returns focus to the
|
|
433
|
+
* control that had it before entering the float (polite: bubble phase,
|
|
434
|
+
* respects `defaultPrevented`, so panel content keeps `Esc`).
|
|
435
|
+
* - **Floating Tab-containment** — Tab wraps within the floating group so focus
|
|
436
|
+
* doesn't leak to the grid behind it.
|
|
437
|
+
*
|
|
438
|
+
* Stands down while an advanced keyboard move is in progress (see
|
|
439
|
+
* {@link KEYBOARD_MOVE_ATTRIBUTE}) so the docking module owns the keys then.
|
|
440
|
+
*/
|
|
441
|
+
class AccessibilityService extends dockviewCore.DockviewCompositeDisposable {
|
|
442
|
+
constructor(host) {
|
|
443
|
+
super();
|
|
444
|
+
this.host = host;
|
|
445
|
+
this._focusWasInside = false;
|
|
446
|
+
// Listen on the document (capture) rather than the dockview element:
|
|
447
|
+
// edge groups live in the shell *outside* the gridview, and the shell
|
|
448
|
+
// is created after this service, so a fixed element would miss them.
|
|
449
|
+
const doc = host.rootElement.ownerDocument;
|
|
450
|
+
const onKeyDown = (e) => this._onKeyDown(e);
|
|
451
|
+
doc.addEventListener('keydown', onKeyDown, true);
|
|
452
|
+
// Remember the last control focused in the main dock (outside any
|
|
453
|
+
// float) so Esc inside a floating group can return focus to its
|
|
454
|
+
// invoking control. Observe-only — never consumes.
|
|
455
|
+
const onFocusIn = (e) => {
|
|
456
|
+
const t = e.target;
|
|
457
|
+
if (t instanceof HTMLElement &&
|
|
458
|
+
this.host.rootElement.contains(t) &&
|
|
459
|
+
!t.closest('[role="dialog"]')) {
|
|
460
|
+
this._lastNonFloatFocus = t;
|
|
435
461
|
}
|
|
436
|
-
});
|
|
437
|
-
if (dockviewRef.current) {
|
|
438
|
-
dockviewRef.current.updateOptions(changes);
|
|
439
|
-
}
|
|
440
|
-
prevProps.current = props;
|
|
441
|
-
}, dockviewCore.PROPERTY_KEYS_DOCKVIEW.map((key) => props[key]));
|
|
442
|
-
React.useEffect(() => {
|
|
443
|
-
var _a;
|
|
444
|
-
if (!domRef.current) {
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
const frameworkTabComponents = (_a = props.tabComponents) !== null && _a !== void 0 ? _a : {};
|
|
448
|
-
if (props.defaultTabComponent) {
|
|
449
|
-
frameworkTabComponents[DEFAULT_REACT_TAB] =
|
|
450
|
-
props.defaultTabComponent;
|
|
451
|
-
}
|
|
452
|
-
const frameworkOptions = {
|
|
453
|
-
createLeftHeaderActionComponent: createGroupControlElement(props.leftHeaderActionsComponent, { addPortal }),
|
|
454
|
-
createRightHeaderActionComponent: createGroupControlElement(props.rightHeaderActionsComponent, { addPortal }),
|
|
455
|
-
createPrefixHeaderActionComponent: createGroupControlElement(props.prefixHeaderActionsComponent, { addPortal }),
|
|
456
|
-
createComponent: (options) => {
|
|
457
|
-
return new ReactPanelContentPart(options.id, props.components[options.name], {
|
|
458
|
-
addPortal,
|
|
459
|
-
});
|
|
460
|
-
},
|
|
461
|
-
createTabComponent(options) {
|
|
462
|
-
return new ReactPanelHeaderPart(options.id, frameworkTabComponents[options.name], {
|
|
463
|
-
addPortal,
|
|
464
|
-
});
|
|
465
|
-
},
|
|
466
|
-
createWatermarkComponent: props.watermarkComponent
|
|
467
|
-
? () => {
|
|
468
|
-
return new ReactWatermarkPart('watermark', props.watermarkComponent, {
|
|
469
|
-
addPortal,
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
: undefined,
|
|
473
|
-
defaultTabComponent: props.defaultTabComponent
|
|
474
|
-
? DEFAULT_REACT_TAB
|
|
475
|
-
: undefined,
|
|
476
|
-
createContextMenuItemComponent: (options) => {
|
|
477
|
-
if (!options.component) {
|
|
478
|
-
return undefined;
|
|
479
|
-
}
|
|
480
|
-
return new ReactContextMenuItemPart(options.id, options.component, { addPortal });
|
|
481
|
-
},
|
|
482
462
|
};
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
api.layout(clientWidth, clientHeight);
|
|
503
|
-
if (props.onReady) {
|
|
504
|
-
props.onReady({ api });
|
|
505
|
-
}
|
|
506
|
-
dockviewRef.current = api;
|
|
507
|
-
return () => {
|
|
508
|
-
dockviewRef.current = undefined;
|
|
509
|
-
api.dispose();
|
|
510
|
-
};
|
|
511
|
-
}, []);
|
|
512
|
-
React.useEffect(() => {
|
|
513
|
-
if (!dockviewRef.current) {
|
|
514
|
-
return () => {
|
|
515
|
-
// noop
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
const disposable = dockviewRef.current.onDidDrop((event) => {
|
|
519
|
-
if (props.onDidDrop) {
|
|
520
|
-
props.onDidDrop(event);
|
|
463
|
+
doc.addEventListener('focusin', onFocusIn, true);
|
|
464
|
+
// Esc-from-float restore runs in the BUBBLE phase and respects
|
|
465
|
+
// defaultPrevented, so panel content that uses Esc keeps priority.
|
|
466
|
+
const onEscape = (e) => this._onFloatingEscape(e);
|
|
467
|
+
doc.addEventListener('keydown', onEscape, false);
|
|
468
|
+
this.addDisposables({
|
|
469
|
+
dispose: () => doc.removeEventListener('keydown', onKeyDown, true),
|
|
470
|
+
}, {
|
|
471
|
+
dispose: () => doc.removeEventListener('focusin', onFocusIn, true),
|
|
472
|
+
}, {
|
|
473
|
+
dispose: () => doc.removeEventListener('keydown', onEscape, false),
|
|
474
|
+
},
|
|
475
|
+
// When a close pulls focus out of the dock, return it to the
|
|
476
|
+
// neighbour the component just activated rather than leaving it
|
|
477
|
+
// stranded on <body>. Snapshot before the teardown (focus still on
|
|
478
|
+
// the closing panel), restore after.
|
|
479
|
+
host.onWillMutateLayout((e) => {
|
|
480
|
+
if (e.kind === 'remove' && this._nav) {
|
|
481
|
+
this._focusWasInside = this._isFocusInside();
|
|
521
482
|
}
|
|
522
|
-
})
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
if (!dockviewRef.current) {
|
|
529
|
-
return () => {
|
|
530
|
-
// noop
|
|
531
|
-
};
|
|
532
|
-
}
|
|
533
|
-
const disposable = dockviewRef.current.onWillDrop((event) => {
|
|
534
|
-
if (props.onWillDrop) {
|
|
535
|
-
props.onWillDrop(event);
|
|
483
|
+
}), host.onDidMutateLayout((e) => {
|
|
484
|
+
if (e.kind === 'remove' &&
|
|
485
|
+
this._nav &&
|
|
486
|
+
this._focusWasInside &&
|
|
487
|
+
!this._isFocusInside()) {
|
|
488
|
+
this._restoreFocus();
|
|
536
489
|
}
|
|
537
|
-
});
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
|
|
490
|
+
}));
|
|
491
|
+
}
|
|
492
|
+
get _moveActive() {
|
|
493
|
+
return this.host.rootElement.hasAttribute(KEYBOARD_MOVE_ATTRIBUTE);
|
|
494
|
+
}
|
|
495
|
+
_isFocusInside() {
|
|
496
|
+
const active = this.host.rootElement.ownerDocument.activeElement;
|
|
497
|
+
return active instanceof Node && this.host.rootElement.contains(active);
|
|
498
|
+
}
|
|
499
|
+
_onFloatingEscape(e) {
|
|
500
|
+
if (!this._nav ||
|
|
501
|
+
this._moveActive ||
|
|
502
|
+
e.defaultPrevented ||
|
|
503
|
+
e.key !== 'Escape') {
|
|
544
504
|
return;
|
|
545
505
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
? () => {
|
|
549
|
-
return new ReactTabGroupChipPart(props.tabGroupChipComponent, {
|
|
550
|
-
addPortal,
|
|
551
|
-
});
|
|
552
|
-
}
|
|
553
|
-
: undefined,
|
|
554
|
-
});
|
|
555
|
-
}, [props.tabGroupChipComponent]);
|
|
556
|
-
React.useEffect(() => {
|
|
557
|
-
if (!dockviewRef.current) {
|
|
506
|
+
const target = e.target;
|
|
507
|
+
if (!(target instanceof Element)) {
|
|
558
508
|
return;
|
|
559
509
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
return new ReactGroupDragGhostPart(props.groupDragGhostComponent, {
|
|
564
|
-
addPortal,
|
|
565
|
-
});
|
|
566
|
-
}
|
|
567
|
-
: undefined,
|
|
568
|
-
});
|
|
569
|
-
}, [props.groupDragGhostComponent]);
|
|
570
|
-
React.useEffect(() => {
|
|
571
|
-
if (!dockviewRef.current) {
|
|
510
|
+
// Only when focus is inside one of *this* dock's floating groups.
|
|
511
|
+
const float = target.closest('[role="dialog"]');
|
|
512
|
+
if (!float || !this.host.rootElement.contains(float)) {
|
|
572
513
|
return;
|
|
573
514
|
}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
515
|
+
e.preventDefault();
|
|
516
|
+
e.stopPropagation();
|
|
517
|
+
this._returnFocusFromFloat();
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Keep Tab inside the floating group that holds focus: at the last tabbable
|
|
521
|
+
* Tab wraps to the first, at the first Shift+Tab wraps to the last. Returns
|
|
522
|
+
* true if it handled the event. No-op outside a float.
|
|
523
|
+
*/
|
|
524
|
+
_trapFloatTab(e) {
|
|
525
|
+
const target = e.target;
|
|
526
|
+
if (!(target instanceof Element)) {
|
|
527
|
+
return false;
|
|
528
|
+
}
|
|
529
|
+
const float = target.closest('[role="dialog"]');
|
|
530
|
+
if (!float || !this.host.rootElement.contains(float)) {
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
533
|
+
// Always manage Tab inside a float, never just at the boundary: focus
|
|
534
|
+
// often sits on non-tabbable plumbing (the content container, which is
|
|
535
|
+
// tabindex="-1"), and the browser's default Tab from there escapes to
|
|
536
|
+
// the grid behind. Drive the cursor through the float's tabbables
|
|
537
|
+
// ourselves and swallow the default so it can't leak out.
|
|
538
|
+
e.preventDefault();
|
|
539
|
+
const tabbables = this._tabbables(float);
|
|
540
|
+
if (tabbables.length === 0) {
|
|
541
|
+
return true;
|
|
542
|
+
}
|
|
543
|
+
const active = float.ownerDocument.activeElement;
|
|
544
|
+
const index = active instanceof HTMLElement ? tabbables.indexOf(active) : -1;
|
|
545
|
+
const n = tabbables.length;
|
|
546
|
+
const next = index === -1
|
|
547
|
+
? e.shiftKey
|
|
548
|
+
? n - 1
|
|
549
|
+
: 0
|
|
550
|
+
: (index + (e.shiftKey ? -1 : 1) + n) % n;
|
|
551
|
+
tabbables[next].focus();
|
|
552
|
+
return true;
|
|
553
|
+
}
|
|
554
|
+
_tabbables(root) {
|
|
555
|
+
const nodes = root.querySelectorAll('a[href], button:not([disabled]), input:not([disabled]), ' +
|
|
556
|
+
'select:not([disabled]), textarea:not([disabled]), [tabindex]');
|
|
557
|
+
// tabIndex >= 0 keeps naturally-focusable controls and roving anchors
|
|
558
|
+
// (the active tab) while dropping tabindex="-1" plumbing (content
|
|
559
|
+
// containers, inactive tabs).
|
|
560
|
+
return Array.from(nodes).filter((el) => el.tabIndex >= 0);
|
|
561
|
+
}
|
|
562
|
+
_returnFocusFromFloat() {
|
|
583
563
|
var _a;
|
|
584
|
-
|
|
564
|
+
const prev = this._lastNonFloatFocus;
|
|
565
|
+
if (prev &&
|
|
566
|
+
prev.isConnected &&
|
|
567
|
+
this.host.rootElement.contains(prev) &&
|
|
568
|
+
!prev.closest('[role="dialog"]')) {
|
|
569
|
+
prev.focus();
|
|
585
570
|
return;
|
|
586
571
|
}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
},
|
|
601
|
-
});
|
|
602
|
-
}, [props.tabComponents, props.defaultTabComponent]);
|
|
603
|
-
React.useEffect(() => {
|
|
604
|
-
if (!dockviewRef.current) {
|
|
572
|
+
// Invoking control is gone — fall back to a grid group's content.
|
|
573
|
+
(_a = this.host.groups
|
|
574
|
+
.find((g) => g.api.location.type === 'grid')) === null || _a === void 0 ? void 0 : _a.model.focusContent();
|
|
575
|
+
}
|
|
576
|
+
get _nav() {
|
|
577
|
+
return readKeyboardNavigation(this.host.options);
|
|
578
|
+
}
|
|
579
|
+
get _keymap() {
|
|
580
|
+
var _a;
|
|
581
|
+
return Object.assign(Object.assign({}, DEFAULT_KEYMAP$1), (_a = this._nav) === null || _a === void 0 ? void 0 : _a.keymap);
|
|
582
|
+
}
|
|
583
|
+
_onKeyDown(e) {
|
|
584
|
+
if (!this._nav) {
|
|
605
585
|
return;
|
|
606
586
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
? () => {
|
|
610
|
-
return new ReactWatermarkPart('watermark', props.watermarkComponent, {
|
|
611
|
-
addPortal,
|
|
612
|
-
});
|
|
613
|
-
}
|
|
614
|
-
: undefined,
|
|
615
|
-
});
|
|
616
|
-
}, [props.watermarkComponent]);
|
|
617
|
-
React.useEffect(() => {
|
|
618
|
-
if (!dockviewRef.current) {
|
|
587
|
+
// Stand down while the docking module is driving a keyboard move.
|
|
588
|
+
if (this._moveActive) {
|
|
619
589
|
return;
|
|
620
590
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
}, [props.rightHeaderActionsComponent]);
|
|
625
|
-
React.useEffect(() => {
|
|
626
|
-
if (!dockviewRef.current) {
|
|
591
|
+
// Only act on events originating inside *this* dockview.
|
|
592
|
+
if (!(e.target instanceof Node) ||
|
|
593
|
+
!this.host.rootElement.contains(e.target)) {
|
|
627
594
|
return;
|
|
628
595
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
}, [props.leftHeaderActionsComponent]);
|
|
633
|
-
React.useEffect(() => {
|
|
634
|
-
if (!dockviewRef.current) {
|
|
596
|
+
// Trap Tab within a floating group so focus doesn't leak to the grid
|
|
597
|
+
// behind it (the float is non-modal, but its Tab order should be).
|
|
598
|
+
if (e.key === 'Tab' && this._trapFloatTab(e)) {
|
|
635
599
|
return;
|
|
636
600
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
return (React.createElement("div", { style: { height: '100%', width: '100%' }, ref: domRef }, portals));
|
|
642
|
-
});
|
|
643
|
-
DockviewReact.displayName = 'DockviewComponent';
|
|
644
|
-
|
|
645
|
-
/******************************************************************************
|
|
646
|
-
Copyright (c) Microsoft Corporation.
|
|
647
|
-
|
|
648
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
649
|
-
purpose with or without fee is hereby granted.
|
|
650
|
-
|
|
651
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
652
|
-
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
653
|
-
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
654
|
-
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
655
|
-
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
656
|
-
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
657
|
-
PERFORMANCE OF THIS SOFTWARE.
|
|
658
|
-
***************************************************************************** */
|
|
659
|
-
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
function __rest(s, e) {
|
|
663
|
-
var t = {};
|
|
664
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
665
|
-
t[p] = s[p];
|
|
666
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
667
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
668
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
669
|
-
t[p[i]] = s[p[i]];
|
|
670
|
-
}
|
|
671
|
-
return t;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
675
|
-
var e = new Error(message);
|
|
676
|
-
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
677
|
-
};
|
|
678
|
-
|
|
679
|
-
const CloseButton = () => (React.createElement("svg", { height: "11", width: "11", viewBox: "0 0 28 28", "aria-hidden": 'false', focusable: false, className: "dv-svg" },
|
|
680
|
-
React.createElement("path", { d: "M2.1 27.3L0 25.2L11.55 13.65L0 2.1L2.1 0L13.65 11.55L25.2 0L27.3 2.1L15.75 13.65L27.3 25.2L25.2 27.3L13.65 15.75L2.1 27.3Z" })));
|
|
681
|
-
|
|
682
|
-
function useTitle(api) {
|
|
683
|
-
const [title, setTitle] = React.useState(api.title);
|
|
684
|
-
React.useEffect(() => {
|
|
685
|
-
const disposable = api.onDidTitleChange((event) => {
|
|
686
|
-
setTitle(event.title);
|
|
687
|
-
});
|
|
688
|
-
// Depending on the order in which React effects are run, the title may already be out of sync (cf. issue #1003).
|
|
689
|
-
if (title !== api.title) {
|
|
690
|
-
setTitle(api.title);
|
|
601
|
+
const keymap = this._keymap;
|
|
602
|
+
if (matchesBinding(e, keymap.nextTab)) {
|
|
603
|
+
this._consume(e);
|
|
604
|
+
this._switchTab(false);
|
|
691
605
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
}, [api]);
|
|
696
|
-
return title;
|
|
697
|
-
}
|
|
698
|
-
const DockviewDefaultTab = (_a) => {
|
|
699
|
-
var { api, containerApi: _containerApi, params: _params, hideClose, closeActionOverride, onPointerDown, onPointerUp, onPointerLeave, tabLocation } = _a, rest = __rest(_a, ["api", "containerApi", "params", "hideClose", "closeActionOverride", "onPointerDown", "onPointerUp", "onPointerLeave", "tabLocation"]);
|
|
700
|
-
const title = useTitle(api);
|
|
701
|
-
const isMiddleMouseButton = React.useRef(false);
|
|
702
|
-
const onClose = React.useCallback((event) => {
|
|
703
|
-
event.preventDefault();
|
|
704
|
-
if (closeActionOverride) {
|
|
705
|
-
closeActionOverride();
|
|
606
|
+
else if (matchesBinding(e, keymap.prevTab)) {
|
|
607
|
+
this._consume(e);
|
|
608
|
+
this._switchTab(true);
|
|
706
609
|
}
|
|
707
|
-
else {
|
|
708
|
-
|
|
610
|
+
else if (matchesBinding(e, keymap.focusNextGroup)) {
|
|
611
|
+
this._consume(e);
|
|
612
|
+
this._cycleGroup(false);
|
|
709
613
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
}, [onPointerDown]);
|
|
718
|
-
const _onPointerUp = React.useCallback((event) => {
|
|
719
|
-
if (isMiddleMouseButton && event.button === 1 && !hideClose) {
|
|
720
|
-
isMiddleMouseButton.current = false;
|
|
721
|
-
onClose(event);
|
|
614
|
+
else if (matchesBinding(e, keymap.focusPrevGroup)) {
|
|
615
|
+
this._consume(e);
|
|
616
|
+
this._cycleGroup(true);
|
|
617
|
+
}
|
|
618
|
+
else if (matchesBinding(e, keymap.focusTabs)) {
|
|
619
|
+
this._consume(e);
|
|
620
|
+
this._focusTabs();
|
|
722
621
|
}
|
|
723
|
-
onPointerUp === null || onPointerUp === void 0 ? void 0 : onPointerUp(event);
|
|
724
|
-
}, [onPointerUp, onClose, hideClose]);
|
|
725
|
-
const _onPointerLeave = React.useCallback((event) => {
|
|
726
|
-
isMiddleMouseButton.current = false;
|
|
727
|
-
onPointerLeave === null || onPointerLeave === void 0 ? void 0 : onPointerLeave(event);
|
|
728
|
-
}, [onPointerLeave]);
|
|
729
|
-
return (React.createElement("div", Object.assign({ "data-testid": "dockview-dv-default-tab" }, rest, { onPointerDown: _onPointerDown, onPointerUp: _onPointerUp, onPointerLeave: _onPointerLeave, className: "dv-default-tab" }),
|
|
730
|
-
React.createElement("span", { className: "dv-default-tab-content" }, title),
|
|
731
|
-
!hideClose && (React.createElement("div", { className: "dv-default-tab-action", onPointerDown: onBtnPointerDown, onClick: onClose },
|
|
732
|
-
React.createElement(CloseButton, null)))));
|
|
733
|
-
};
|
|
734
|
-
|
|
735
|
-
class ReactPanelView extends dockviewCore.SplitviewPanel {
|
|
736
|
-
constructor(id, component, reactComponent, reactPortalStore) {
|
|
737
|
-
super(id, component);
|
|
738
|
-
this.reactComponent = reactComponent;
|
|
739
|
-
this.reactPortalStore = reactPortalStore;
|
|
740
622
|
}
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
});
|
|
623
|
+
// --- navigation (uses the public group API + the adjacentGroup primitive) ---
|
|
624
|
+
_focusTabs() {
|
|
625
|
+
var _a;
|
|
626
|
+
// Jump from panel content to the active group's tab strip; the
|
|
627
|
+
// tablist's own roving-tabindex handler takes over from there.
|
|
628
|
+
(_a = this.host.activeGroup) === null || _a === void 0 ? void 0 : _a.model.focusActiveTab();
|
|
748
629
|
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
if (key in props) {
|
|
754
|
-
obj[key] = props[key];
|
|
755
|
-
}
|
|
756
|
-
return obj;
|
|
757
|
-
}, {});
|
|
758
|
-
return coreOptions;
|
|
759
|
-
}
|
|
760
|
-
const SplitviewReact = React.forwardRef((props, ref) => {
|
|
761
|
-
const domRef = React.useRef(null);
|
|
762
|
-
const splitviewRef = React.useRef(undefined);
|
|
763
|
-
const [portals, addPortal] = usePortalsLifecycle();
|
|
764
|
-
React.useImperativeHandle(ref, () => domRef.current, []);
|
|
765
|
-
const prevProps = React.useRef({});
|
|
766
|
-
React.useEffect(() => {
|
|
767
|
-
const changes = {};
|
|
768
|
-
dockviewCore.PROPERTY_KEYS_SPLITVIEW.forEach((propKey) => {
|
|
769
|
-
const key = propKey;
|
|
770
|
-
const propValue = props[key];
|
|
771
|
-
if (key in props && propValue !== prevProps.current[key]) {
|
|
772
|
-
changes[key] = propValue;
|
|
773
|
-
}
|
|
774
|
-
});
|
|
775
|
-
if (splitviewRef.current) {
|
|
776
|
-
splitviewRef.current.updateOptions(changes);
|
|
777
|
-
}
|
|
778
|
-
prevProps.current = props;
|
|
779
|
-
}, dockviewCore.PROPERTY_KEYS_SPLITVIEW.map((key) => props[key]));
|
|
780
|
-
React.useEffect(() => {
|
|
781
|
-
if (!domRef.current) {
|
|
782
|
-
return () => {
|
|
783
|
-
// noop
|
|
784
|
-
};
|
|
630
|
+
_switchTab(reverse) {
|
|
631
|
+
const group = this.host.activeGroup;
|
|
632
|
+
if (!group) {
|
|
633
|
+
return;
|
|
785
634
|
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
return new ReactPanelView(options.id, options.name, props.components[options.name], { addPortal });
|
|
789
|
-
},
|
|
790
|
-
};
|
|
791
|
-
const api = dockviewCore.createSplitview(domRef.current, Object.assign(Object.assign({}, extractCoreOptions$2(props)), frameworkOptions));
|
|
792
|
-
const { clientWidth, clientHeight } = domRef.current;
|
|
793
|
-
api.layout(clientWidth, clientHeight);
|
|
794
|
-
if (props.onReady) {
|
|
795
|
-
props.onReady({ api });
|
|
635
|
+
if (reverse) {
|
|
636
|
+
group.model.moveToPrevious();
|
|
796
637
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
638
|
+
else {
|
|
639
|
+
group.model.moveToNext();
|
|
640
|
+
}
|
|
641
|
+
// Keep DOM focus inside the dock: switching hides the previously
|
|
642
|
+
// focused content, which would otherwise drop focus to <body> and
|
|
643
|
+
// leave the keymap unable to see the next key.
|
|
644
|
+
group.model.focusContent();
|
|
645
|
+
}
|
|
646
|
+
_cycleGroup(reverse) {
|
|
647
|
+
const current = this.host.activeGroup;
|
|
648
|
+
const target = current
|
|
649
|
+
? this.host.adjacentGroup(current, reverse)
|
|
650
|
+
: this.host.groups[0];
|
|
651
|
+
this._focusGroup(target);
|
|
652
|
+
}
|
|
653
|
+
_focusGroup(target) {
|
|
654
|
+
if (!target) {
|
|
805
655
|
return;
|
|
806
656
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
return new ReactPanelView(options.id, options.name, props.components[options.name], { addPortal });
|
|
810
|
-
},
|
|
811
|
-
});
|
|
812
|
-
}, [props.components]);
|
|
813
|
-
return (React.createElement("div", { style: { height: '100%', width: '100%' }, ref: domRef }, portals));
|
|
814
|
-
});
|
|
815
|
-
SplitviewReact.displayName = 'SplitviewComponent';
|
|
816
|
-
|
|
817
|
-
class ReactGridPanelView extends dockviewCore.GridviewPanel {
|
|
818
|
-
constructor(id, component, reactComponent, reactPortalStore) {
|
|
819
|
-
super(id, component);
|
|
820
|
-
this.reactComponent = reactComponent;
|
|
821
|
-
this.reactPortalStore = reactPortalStore;
|
|
657
|
+
target.api.setActive();
|
|
658
|
+
target.model.focusContent();
|
|
822
659
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
});
|
|
660
|
+
/** Return DOM focus to the active group's content, keeping it in the dock. */
|
|
661
|
+
_restoreFocus() {
|
|
662
|
+
var _a;
|
|
663
|
+
(_a = this.host.activeGroup) === null || _a === void 0 ? void 0 : _a.model.focusContent();
|
|
664
|
+
}
|
|
665
|
+
_consume(e) {
|
|
666
|
+
e.preventDefault();
|
|
667
|
+
e.stopPropagation();
|
|
832
668
|
}
|
|
833
669
|
}
|
|
670
|
+
const AccessibilityModule = dockviewCore.defineModule({
|
|
671
|
+
name: 'Accessibility',
|
|
672
|
+
serviceKey: 'accessibilityService',
|
|
673
|
+
create: (host) => new AccessibilityService(host),
|
|
674
|
+
});
|
|
834
675
|
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
676
|
+
const EDGE_FROM_KEY = {
|
|
677
|
+
ArrowLeft: 'left',
|
|
678
|
+
ArrowRight: 'right',
|
|
679
|
+
ArrowUp: 'top',
|
|
680
|
+
ArrowDown: 'bottom',
|
|
681
|
+
};
|
|
682
|
+
const DEFAULT_KEYMAP = {
|
|
683
|
+
focusGroupLeft: 'ctrl+shift+arrowleft',
|
|
684
|
+
focusGroupRight: 'ctrl+shift+arrowright',
|
|
685
|
+
focusGroupUp: 'ctrl+shift+arrowup',
|
|
686
|
+
focusGroupDown: 'ctrl+shift+arrowdown',
|
|
687
|
+
dock: 'ctrl+m',
|
|
688
|
+
};
|
|
689
|
+
/**
|
|
690
|
+
* Advanced keyboard control — drive the layout itself without a mouse. Opt-in
|
|
691
|
+
* via `keyboardNavigation` (shared with the default navigation module).
|
|
692
|
+
*
|
|
693
|
+
* - **Spatial group focus** (`Ctrl+Shift+Arrows`) — move focus to the visually
|
|
694
|
+
* adjacent group via the `adjacentGroupInDirection` geometry primitive.
|
|
695
|
+
* - **Keyboard docking** (`Ctrl+M`) — arms a two-phase move of the active panel
|
|
696
|
+
* with a live drop preview + screen-reader narration:
|
|
697
|
+
* 1. PICK TARGET — arrows cycle the groups (incl. the panel's own, so a tab
|
|
698
|
+
* can be split out); `Enter` selects one.
|
|
699
|
+
* 2. PICK EDGE — arrows choose a split edge (left/right/top/bottom) or the
|
|
700
|
+
* centre (tab-into); `Enter` commits, `Escape` steps back.
|
|
701
|
+
* `Escape` from the target phase cancels.
|
|
702
|
+
*
|
|
703
|
+
* While a move is in progress it marks the root with
|
|
704
|
+
* {@link KEYBOARD_MOVE_ATTRIBUTE} so the default navigation listener stands
|
|
705
|
+
* down and the move keys aren't double-handled.
|
|
706
|
+
*/
|
|
707
|
+
class KeyboardDockingService extends dockviewCore.DockviewCompositeDisposable {
|
|
708
|
+
constructor(host) {
|
|
709
|
+
super();
|
|
710
|
+
this.host = host;
|
|
711
|
+
this._move = null;
|
|
712
|
+
// Capture phase, on the document — matches the navigation listener so
|
|
713
|
+
// edge groups (outside the gridview) are covered.
|
|
714
|
+
const doc = host.rootElement.ownerDocument;
|
|
715
|
+
const onKeyDown = (e) => this._onKeyDown(e);
|
|
716
|
+
doc.addEventListener('keydown', onKeyDown, true);
|
|
717
|
+
this.addDisposables({ dispose: () => this._exit() }, {
|
|
718
|
+
dispose: () => doc.removeEventListener('keydown', onKeyDown, true),
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
get _enabled() {
|
|
722
|
+
return !!readKeyboardNavigation(this.host.options);
|
|
723
|
+
}
|
|
724
|
+
get _keymap() {
|
|
725
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
726
|
+
// The public `keyboardNavigation.keymap` carries the default-navigation
|
|
727
|
+
// bindings; this module's bindings are read from the same object when
|
|
728
|
+
// present (so a consumer can rebind them too).
|
|
729
|
+
const overrides = ((_b = (_a = readKeyboardNavigation(this.host.options)) === null || _a === void 0 ? void 0 : _a.keymap) !== null && _b !== void 0 ? _b : {});
|
|
730
|
+
return {
|
|
731
|
+
focusGroupLeft: (_c = overrides.focusGroupLeft) !== null && _c !== void 0 ? _c : DEFAULT_KEYMAP.focusGroupLeft,
|
|
732
|
+
focusGroupRight: (_d = overrides.focusGroupRight) !== null && _d !== void 0 ? _d : DEFAULT_KEYMAP.focusGroupRight,
|
|
733
|
+
focusGroupUp: (_e = overrides.focusGroupUp) !== null && _e !== void 0 ? _e : DEFAULT_KEYMAP.focusGroupUp,
|
|
734
|
+
focusGroupDown: (_f = overrides.focusGroupDown) !== null && _f !== void 0 ? _f : DEFAULT_KEYMAP.focusGroupDown,
|
|
735
|
+
dock: (_g = overrides.dock) !== null && _g !== void 0 ? _g : DEFAULT_KEYMAP.dock,
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
get _messages() {
|
|
739
|
+
return dockviewCore.resolveMessages(this.host.options.messages);
|
|
740
|
+
}
|
|
741
|
+
_onKeyDown(e) {
|
|
742
|
+
if (!this._enabled) {
|
|
743
|
+
return;
|
|
839
744
|
}
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
}
|
|
844
|
-
const GridviewReact = React.forwardRef((props, ref) => {
|
|
845
|
-
const domRef = React.useRef(null);
|
|
846
|
-
const gridviewRef = React.useRef(undefined);
|
|
847
|
-
const [portals, addPortal] = usePortalsLifecycle();
|
|
848
|
-
React.useImperativeHandle(ref, () => domRef.current, []);
|
|
849
|
-
const prevProps = React.useRef({});
|
|
850
|
-
React.useEffect(() => {
|
|
851
|
-
const changes = {};
|
|
852
|
-
dockviewCore.PROPERTY_KEYS_GRIDVIEW.forEach((propKey) => {
|
|
853
|
-
const key = propKey;
|
|
854
|
-
const propValue = props[key];
|
|
855
|
-
if (key in props && propValue !== prevProps.current[key]) {
|
|
856
|
-
changes[key] = propValue;
|
|
857
|
-
}
|
|
858
|
-
});
|
|
859
|
-
if (gridviewRef.current) {
|
|
860
|
-
gridviewRef.current.updateOptions(changes);
|
|
745
|
+
if (this._move) {
|
|
746
|
+
this._onMoveKey(e);
|
|
747
|
+
return;
|
|
861
748
|
}
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
return () => {
|
|
867
|
-
// noop
|
|
868
|
-
};
|
|
749
|
+
// Only act on events originating inside *this* dockview.
|
|
750
|
+
if (!(e.target instanceof Node) ||
|
|
751
|
+
!this.host.rootElement.contains(e.target)) {
|
|
752
|
+
return;
|
|
869
753
|
}
|
|
870
|
-
const
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
},
|
|
874
|
-
};
|
|
875
|
-
const api = dockviewCore.createGridview(domRef.current, Object.assign(Object.assign({}, extractCoreOptions$1(props)), frameworkOptions));
|
|
876
|
-
const { clientWidth, clientHeight } = domRef.current;
|
|
877
|
-
api.layout(clientWidth, clientHeight);
|
|
878
|
-
if (props.onReady) {
|
|
879
|
-
props.onReady({ api });
|
|
754
|
+
const keymap = this._keymap;
|
|
755
|
+
if (matchesBinding(e, keymap.dock)) {
|
|
756
|
+
this._enterMoveMode(e);
|
|
880
757
|
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
758
|
+
else if (matchesBinding(e, keymap.focusGroupLeft)) {
|
|
759
|
+
this._consume(e);
|
|
760
|
+
this._focusGroupInDirection('left');
|
|
761
|
+
}
|
|
762
|
+
else if (matchesBinding(e, keymap.focusGroupRight)) {
|
|
763
|
+
this._consume(e);
|
|
764
|
+
this._focusGroupInDirection('right');
|
|
765
|
+
}
|
|
766
|
+
else if (matchesBinding(e, keymap.focusGroupUp)) {
|
|
767
|
+
this._consume(e);
|
|
768
|
+
this._focusGroupInDirection('up');
|
|
769
|
+
}
|
|
770
|
+
else if (matchesBinding(e, keymap.focusGroupDown)) {
|
|
771
|
+
this._consume(e);
|
|
772
|
+
this._focusGroupInDirection('down');
|
|
890
773
|
}
|
|
891
|
-
gridviewRef.current.updateOptions({
|
|
892
|
-
createComponent: (options) => {
|
|
893
|
-
return new ReactGridPanelView(options.id, options.name, props.components[options.name], { addPortal });
|
|
894
|
-
},
|
|
895
|
-
});
|
|
896
|
-
}, [props.components]);
|
|
897
|
-
return (React.createElement("div", { style: { height: '100%', width: '100%' }, ref: domRef }, portals));
|
|
898
|
-
});
|
|
899
|
-
GridviewReact.displayName = 'GridviewComponent';
|
|
900
|
-
|
|
901
|
-
class PanePanelSection {
|
|
902
|
-
get element() {
|
|
903
|
-
return this._element;
|
|
904
|
-
}
|
|
905
|
-
constructor(id, component, reactPortalStore) {
|
|
906
|
-
this.id = id;
|
|
907
|
-
this.component = component;
|
|
908
|
-
this.reactPortalStore = reactPortalStore;
|
|
909
|
-
this._element = document.createElement('div');
|
|
910
|
-
this._element.style.height = '100%';
|
|
911
|
-
this._element.style.width = '100%';
|
|
912
|
-
}
|
|
913
|
-
init(parameters) {
|
|
914
|
-
this.part = new ReactPart(this.element, this.reactPortalStore, this.component, {
|
|
915
|
-
params: parameters.params,
|
|
916
|
-
api: parameters.api,
|
|
917
|
-
title: parameters.title,
|
|
918
|
-
containerApi: parameters.containerApi,
|
|
919
|
-
});
|
|
920
774
|
}
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
775
|
+
// --- spatial focus ---
|
|
776
|
+
_focusGroupInDirection(direction) {
|
|
777
|
+
const current = this.host.activeGroup;
|
|
778
|
+
if (!current) {
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
// Geometry lives on the host as the shared `adjacentGroupInDirection`
|
|
782
|
+
// primitive (also public on the api), so mouse and keyboard navigation
|
|
783
|
+
// agree on what "the group to the left" is.
|
|
784
|
+
this._focusGroup(this.host.adjacentGroupInDirection(current, direction));
|
|
925
785
|
}
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
786
|
+
_focusGroup(target) {
|
|
787
|
+
if (!target) {
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
target.api.setActive();
|
|
791
|
+
target.model.focusContent();
|
|
929
792
|
}
|
|
930
|
-
|
|
793
|
+
_restoreFocus() {
|
|
931
794
|
var _a;
|
|
932
|
-
(_a = this.
|
|
795
|
+
(_a = this.host.activeGroup) === null || _a === void 0 ? void 0 : _a.model.focusContent();
|
|
933
796
|
}
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
if (
|
|
939
|
-
|
|
940
|
-
}
|
|
941
|
-
return obj;
|
|
942
|
-
}, {});
|
|
943
|
-
return coreOptions;
|
|
944
|
-
}
|
|
945
|
-
const PaneviewReact = React.forwardRef((props, ref) => {
|
|
946
|
-
const domRef = React.useRef(null);
|
|
947
|
-
const paneviewRef = React.useRef(undefined);
|
|
948
|
-
const [portals, addPortal] = usePortalsLifecycle();
|
|
949
|
-
React.useImperativeHandle(ref, () => domRef.current, []);
|
|
950
|
-
const prevProps = React.useRef({});
|
|
951
|
-
React.useEffect(() => {
|
|
952
|
-
const changes = {};
|
|
953
|
-
dockviewCore.PROPERTY_KEYS_PANEVIEW.forEach((propKey) => {
|
|
954
|
-
const key = propKey;
|
|
955
|
-
const propValue = props[key];
|
|
956
|
-
if (key in props && propValue !== prevProps.current[key]) {
|
|
957
|
-
changes[key] = propValue;
|
|
958
|
-
}
|
|
959
|
-
});
|
|
960
|
-
if (paneviewRef.current) {
|
|
961
|
-
paneviewRef.current.updateOptions(changes);
|
|
962
|
-
}
|
|
963
|
-
prevProps.current = props;
|
|
964
|
-
}, dockviewCore.PROPERTY_KEYS_PANEVIEW.map((key) => props[key]));
|
|
965
|
-
React.useEffect(() => {
|
|
966
|
-
var _a;
|
|
967
|
-
if (!domRef.current) {
|
|
968
|
-
return () => {
|
|
969
|
-
// noop
|
|
970
|
-
};
|
|
797
|
+
// --- keyboard docking (move mode) ---
|
|
798
|
+
_enterMoveMode(e) {
|
|
799
|
+
const source = this.host.activePanel;
|
|
800
|
+
const groups = this.host.groups;
|
|
801
|
+
if (!source || groups.length === 0) {
|
|
802
|
+
return;
|
|
971
803
|
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
804
|
+
e.preventDefault();
|
|
805
|
+
e.stopPropagation();
|
|
806
|
+
// Start on the panel's own group so a single group of tabs can still
|
|
807
|
+
// split a tab out via the edge phase.
|
|
808
|
+
const groupIndex = Math.max(0, groups.indexOf(source.group));
|
|
809
|
+
this._move = {
|
|
810
|
+
source,
|
|
811
|
+
groups,
|
|
812
|
+
groupIndex,
|
|
813
|
+
phase: 'target',
|
|
814
|
+
position: 'center',
|
|
980
815
|
};
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
816
|
+
// Signal the navigation module to stand down while the move runs.
|
|
817
|
+
this.host.rootElement.setAttribute(KEYBOARD_MOVE_ATTRIBUTE, '');
|
|
818
|
+
this._render();
|
|
819
|
+
}
|
|
820
|
+
_onMoveKey(e) {
|
|
821
|
+
const move = this._move;
|
|
822
|
+
if (e.key === 'Escape') {
|
|
823
|
+
this._consume(e);
|
|
824
|
+
if (move.phase === 'edge') {
|
|
825
|
+
move.phase = 'target';
|
|
826
|
+
move.position = 'center';
|
|
827
|
+
this._render();
|
|
828
|
+
}
|
|
829
|
+
else {
|
|
830
|
+
this._exit();
|
|
831
|
+
this.host.announce(this._messages.moveCancelled());
|
|
832
|
+
this._restoreFocus();
|
|
833
|
+
}
|
|
834
|
+
return;
|
|
986
835
|
}
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
836
|
+
if (e.key === 'Enter') {
|
|
837
|
+
this._consume(e);
|
|
838
|
+
if (move.phase === 'target') {
|
|
839
|
+
move.phase = 'edge';
|
|
840
|
+
move.position = 'center';
|
|
841
|
+
this._render();
|
|
842
|
+
}
|
|
843
|
+
else {
|
|
844
|
+
this._commit();
|
|
845
|
+
}
|
|
995
846
|
return;
|
|
996
847
|
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
848
|
+
if (move.phase === 'target') {
|
|
849
|
+
const n = move.groups.length;
|
|
850
|
+
if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
|
|
851
|
+
this._consume(e);
|
|
852
|
+
move.groupIndex = (move.groupIndex + 1) % n;
|
|
853
|
+
this._render();
|
|
854
|
+
}
|
|
855
|
+
else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
|
|
856
|
+
this._consume(e);
|
|
857
|
+
move.groupIndex = (move.groupIndex - 1 + n) % n;
|
|
858
|
+
this._render();
|
|
859
|
+
}
|
|
1006
860
|
return;
|
|
1007
861
|
}
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
}, [props.headerComponents]);
|
|
1015
|
-
React.useEffect(() => {
|
|
1016
|
-
if (!paneviewRef.current) {
|
|
1017
|
-
return () => {
|
|
1018
|
-
// noop
|
|
1019
|
-
};
|
|
862
|
+
// edge phase
|
|
863
|
+
const edge = EDGE_FROM_KEY[e.key];
|
|
864
|
+
if (edge) {
|
|
865
|
+
this._consume(e);
|
|
866
|
+
move.position = edge;
|
|
867
|
+
this._render();
|
|
1020
868
|
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
869
|
+
else if (e.key === ' ' || e.key === 'c' || e.key === 'C') {
|
|
870
|
+
this._consume(e);
|
|
871
|
+
move.position = 'center';
|
|
872
|
+
this._render();
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
_render() {
|
|
876
|
+
var _a, _b;
|
|
877
|
+
const move = this._move;
|
|
878
|
+
const group = move.groups[move.groupIndex];
|
|
879
|
+
this._clearPreview();
|
|
880
|
+
this._preview = this.host.showDropPreview(group, move.position);
|
|
881
|
+
const name = (_b = (_a = group.activePanel) === null || _a === void 0 ? void 0 : _a.title) !== null && _b !== void 0 ? _b : group.id;
|
|
882
|
+
const m = this._messages;
|
|
883
|
+
if (move.phase === 'target') {
|
|
884
|
+
this.host.announce(m.movePickTarget(this._label(move.source), name, move.groupIndex + 1, move.groups.length));
|
|
885
|
+
}
|
|
886
|
+
else {
|
|
887
|
+
this.host.announce(m.movePickEdge(move.position, name));
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
_commit() {
|
|
891
|
+
var _a, _b;
|
|
892
|
+
const move = this._move;
|
|
893
|
+
const group = move.groups[move.groupIndex];
|
|
894
|
+
const position = move.position;
|
|
895
|
+
const source = move.source;
|
|
896
|
+
const name = (_b = (_a = group.activePanel) === null || _a === void 0 ? void 0 : _a.title) !== null && _b !== void 0 ? _b : group.id;
|
|
897
|
+
const m = this._messages;
|
|
898
|
+
this._exit();
|
|
899
|
+
try {
|
|
900
|
+
this.host.dockPanel(source, group, position);
|
|
901
|
+
this.host.announce(m.moveCommitted(this._label(source), name, position));
|
|
902
|
+
}
|
|
903
|
+
catch (_c) {
|
|
904
|
+
this.host.announce(m.moveNotAllowed());
|
|
905
|
+
}
|
|
906
|
+
// The move re-renders the grid; pull focus back into the dock so the
|
|
907
|
+
// keymap keeps working without a click.
|
|
908
|
+
this._restoreFocus();
|
|
909
|
+
}
|
|
910
|
+
_exit() {
|
|
911
|
+
this._clearPreview();
|
|
912
|
+
this._move = null;
|
|
913
|
+
this.host.rootElement.removeAttribute(KEYBOARD_MOVE_ATTRIBUTE);
|
|
914
|
+
}
|
|
915
|
+
_clearPreview() {
|
|
916
|
+
var _a;
|
|
917
|
+
(_a = this._preview) === null || _a === void 0 ? void 0 : _a.dispose();
|
|
918
|
+
this._preview = undefined;
|
|
919
|
+
}
|
|
920
|
+
_consume(e) {
|
|
921
|
+
e.preventDefault();
|
|
922
|
+
e.stopPropagation();
|
|
923
|
+
}
|
|
924
|
+
_label(panel) {
|
|
925
|
+
var _a;
|
|
926
|
+
return (_a = panel.title) !== null && _a !== void 0 ? _a : panel.id;
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
dockviewCore.defineModule({
|
|
930
|
+
name: 'KeyboardDocking',
|
|
931
|
+
serviceKey: 'keyboardDockingService',
|
|
932
|
+
create: (host) => new KeyboardDockingService(host),
|
|
933
|
+
dependsOn: [AdvancedDnDModule, dockviewCore.LiveRegionModule],
|
|
1031
934
|
});
|
|
1032
|
-
PaneviewReact.displayName = 'PaneviewComponent';
|
|
1033
935
|
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
936
|
+
/**
|
|
937
|
+
* The set of modules provided by this package. `dockview` registers these via
|
|
938
|
+
* `registerModules(Modules)` so they are available to every component.
|
|
939
|
+
*/
|
|
940
|
+
const Modules = [
|
|
941
|
+
TabGroupChipsModule,
|
|
942
|
+
ContextMenuModule,
|
|
943
|
+
AdvancedDnDModule,
|
|
944
|
+
AccessibilityModule,
|
|
945
|
+
];
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* `dockview` is the batteries-included entry point: it re-exports the core API
|
|
949
|
+
* and registers the separable feature modules so every component gets the full
|
|
950
|
+
* feature set out of the box. Consumers who want only the core can depend on
|
|
951
|
+
* `dockview-core` directly.
|
|
952
|
+
*/
|
|
953
|
+
dockviewCore.registerModules(Modules);
|
|
954
|
+
// Mark the public package as loaded so `dockview-core` doesn't warn about
|
|
955
|
+
// direct usage. Purely drives that developer warning — no functional effect.
|
|
956
|
+
dockviewCore.markDockviewPackageLoaded();
|
|
957
|
+
|
|
1043
958
|
Object.keys(dockviewCore).forEach(function (k) {
|
|
1044
959
|
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
1045
960
|
enumerable: true,
|