@hpcc-js/phosphor 3.7.2 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +139 -85
- package/dist/index.js.map +1 -1
- package/dist/index.umd.cjs +2 -2
- package/dist/index.umd.cjs.map +1 -1
- package/package.json +4 -4
- package/src/BasePanel.ts +87 -0
- package/src/DockPanel.ts +125 -65
- package/src/SplitPanel.ts +82 -32
- package/src/TabPanel.ts +43 -52
- package/src/WidgetAdapter.css +2 -1
- package/types/BasePanel.d.ts +28 -0
- package/types/DockPanel.d.ts +19 -10
- package/types/SplitPanel.d.ts +17 -10
- package/types/TabPanel.d.ts +17 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hpcc-js/phosphor",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.0",
|
|
4
4
|
"description": "hpcc-js - Viz Phosphor",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.umd.cjs",
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
"update-major": "npx --yes npm-check-updates -u"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@hpcc-js/common": "^3.8.
|
|
42
|
+
"@hpcc-js/common": "^3.8.2"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@hpcc-js/esbuild-plugins": "^1.9.
|
|
45
|
+
"@hpcc-js/esbuild-plugins": "^1.9.2",
|
|
46
46
|
"@lumino/algorithm": "2.0.4",
|
|
47
47
|
"@lumino/commands": "2.3.3",
|
|
48
48
|
"@lumino/messaging": "2.0.4",
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
"url": "https://github.com/hpcc-systems/Visualization/issues"
|
|
60
60
|
},
|
|
61
61
|
"homepage": "https://github.com/hpcc-systems/Visualization",
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "9d31241482c78decca86e3bf624244ce5b40f3d6"
|
|
63
63
|
}
|
package/src/BasePanel.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { HTMLWidget, Widget, Utility } from "@hpcc-js/common";
|
|
2
|
+
import { Widget as PWidget, IMessageHandler, IMessageHook, Message, MessageLoop } from "./phosphor-shim.ts";
|
|
3
|
+
import { Msg, WidgetAdapter, WidgetAdapterArray } from "./WidgetAdapter.ts";
|
|
4
|
+
|
|
5
|
+
export namespace BasePanel {
|
|
6
|
+
export interface IAddWidgetOptions {
|
|
7
|
+
/** Minimum size in pixels */
|
|
8
|
+
minSize?: number;
|
|
9
|
+
/** Preferred/default size in pixels — used as initial size hint */
|
|
10
|
+
defaultSize?: number;
|
|
11
|
+
/** Inner padding in pixels (default 8) */
|
|
12
|
+
padding?: number;
|
|
13
|
+
|
|
14
|
+
/** Reference widget for split/tab positioning */
|
|
15
|
+
refWidget?: Widget;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export abstract class BasePanel extends HTMLWidget implements IMessageHandler, IMessageHook {
|
|
20
|
+
|
|
21
|
+
protected abstract _content: WidgetAdapterArray;
|
|
22
|
+
|
|
23
|
+
constructor() {
|
|
24
|
+
super();
|
|
25
|
+
this._tag = "div";
|
|
26
|
+
MessageLoop.installMessageHook(this, this);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
getWidget(wa: PWidget): Widget | undefined {
|
|
30
|
+
if (wa instanceof WidgetAdapter) {
|
|
31
|
+
return wa.widget;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getWidgetAdapter(widget: Widget): WidgetAdapter | null {
|
|
36
|
+
let retVal = null;
|
|
37
|
+
this._content.some(wa => {
|
|
38
|
+
if (wa.widget === widget) {
|
|
39
|
+
retVal = wa;
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
});
|
|
44
|
+
return retVal;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
protected _prevActive: Widget;
|
|
48
|
+
active(): Widget {
|
|
49
|
+
return this._prevActive;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Phosphor Messaging ---
|
|
53
|
+
protected _lazyLayoutChanged = Utility.debounce(async () => {
|
|
54
|
+
this.layoutChanged();
|
|
55
|
+
}, 1000);
|
|
56
|
+
|
|
57
|
+
processMessage(msg: Message): void {
|
|
58
|
+
switch (msg.type) {
|
|
59
|
+
case Msg.WAActivateRequest.type:
|
|
60
|
+
const wa = (msg as Msg.WAActivateRequest).wa;
|
|
61
|
+
const widget = wa.widget;
|
|
62
|
+
if (this._prevActive !== widget) {
|
|
63
|
+
this._prevActive = widget;
|
|
64
|
+
this.childActivation(widget, wa);
|
|
65
|
+
}
|
|
66
|
+
break;
|
|
67
|
+
case Msg.WALayoutChanged.type:
|
|
68
|
+
this._lazyLayoutChanged();
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
messageHook(handler: IMessageHandler, msg: Message): boolean {
|
|
74
|
+
if (handler === this) {
|
|
75
|
+
this.processMessage(msg);
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Events ---
|
|
81
|
+
childActivation(w: Widget, wa: WidgetAdapter) {
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
layoutChanged() {
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
}
|
package/src/DockPanel.ts
CHANGED
|
@@ -1,43 +1,144 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Widget, select as d3Select } from "@hpcc-js/common";
|
|
2
|
+
import { BasePanel } from "./BasePanel.ts";
|
|
3
|
+
import { DockLayout, DockPanel as PhosphorDockPanel, MessageLoop, Widget as PWidget } from "./phosphor-shim.ts";
|
|
4
|
+
import { IClosable, WidgetAdapter } from "./WidgetAdapter.ts";
|
|
3
5
|
import { PDockPanel } from "./PDockPanel.ts";
|
|
4
|
-
import { IClosable, Msg, WidgetAdapter } from "./WidgetAdapter.ts";
|
|
5
6
|
|
|
6
7
|
import "../src/DockPanel.css";
|
|
7
8
|
|
|
8
|
-
export
|
|
9
|
+
export namespace DockPanel {
|
|
10
|
+
export interface IAddWidgetOptions extends BasePanel.IAddWidgetOptions {
|
|
11
|
+
/** Tab title */
|
|
12
|
+
title?: string;
|
|
13
|
+
/** Insertion mode relative to refWidget */
|
|
14
|
+
location?: PhosphorDockPanel.InsertMode;
|
|
15
|
+
/** Whether the tab can be closed */
|
|
16
|
+
closable?: boolean | IClosable;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class DockPanel extends BasePanel {
|
|
9
21
|
private _dock = new PDockPanel({ mode: "multiple-document" });
|
|
22
|
+
protected _content = this._dock.content();
|
|
10
23
|
|
|
11
24
|
constructor() {
|
|
12
25
|
super();
|
|
13
|
-
this._tag = "div";
|
|
14
26
|
this._dock.id = "p" + this.id();
|
|
15
|
-
MessageLoop.installMessageHook(this, this);
|
|
16
27
|
}
|
|
17
28
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
});
|
|
27
|
-
return retVal;
|
|
28
|
-
}
|
|
29
|
+
private _pendingDefaults: Map<WidgetAdapter, { defaultSize?: number }> = new Map();
|
|
30
|
+
addWidget(widget: Widget, options: DockPanel.IAddWidgetOptions): this;
|
|
31
|
+
/** @deprecated Use options object form instead */
|
|
32
|
+
addWidget(widget: Widget, title: string, location?: PhosphorDockPanel.InsertMode, refWidget?: Widget, closable?: boolean | IClosable, padding?: number): this;
|
|
33
|
+
addWidget(widget: Widget, titleOrOptions: string | DockPanel.IAddWidgetOptions, location: PhosphorDockPanel.InsertMode = "split-right", refWidget?: Widget, closable?: boolean | IClosable, padding: number = 8) {
|
|
34
|
+
const opts: DockPanel.IAddWidgetOptions = typeof titleOrOptions === "string"
|
|
35
|
+
? { title: titleOrOptions, location, refWidget, closable, padding }
|
|
36
|
+
: titleOrOptions;
|
|
29
37
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
38
|
+
const {
|
|
39
|
+
title = "",
|
|
40
|
+
location: loc = "split-right",
|
|
41
|
+
refWidget: ref,
|
|
42
|
+
closable: canClose,
|
|
43
|
+
padding: pad = 8,
|
|
44
|
+
minSize,
|
|
45
|
+
defaultSize
|
|
46
|
+
} = opts;
|
|
47
|
+
|
|
48
|
+
const addMode: PhosphorDockPanel.IAddOptions = { mode: loc, ref: this.getWidgetAdapter(ref) };
|
|
49
|
+
const wa = new WidgetAdapter(this, widget, {}, canClose);
|
|
33
50
|
wa.title.label = title;
|
|
34
|
-
wa.padding =
|
|
51
|
+
wa.padding = pad;
|
|
52
|
+
|
|
53
|
+
if (minSize != null) {
|
|
54
|
+
const style = wa.node.style;
|
|
55
|
+
if (loc === "split-left" || loc === "split-right") {
|
|
56
|
+
style.minWidth = `${minSize}px`;
|
|
57
|
+
} else {
|
|
58
|
+
style.minHeight = `${minSize}px`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
35
62
|
this._dock.addWidget(wa, addMode);
|
|
36
63
|
this._dock.appendContent(wa);
|
|
37
64
|
this._dock.tabsMovable = true;
|
|
65
|
+
|
|
66
|
+
if (defaultSize != null) {
|
|
67
|
+
this._pendingDefaults.set(wa, { defaultSize });
|
|
68
|
+
}
|
|
69
|
+
|
|
38
70
|
return this;
|
|
39
71
|
}
|
|
40
72
|
|
|
73
|
+
private _applyPendingDefaults() {
|
|
74
|
+
if (this._pendingDefaults.size === 0) return;
|
|
75
|
+
|
|
76
|
+
const config = this._dock.saveLayout() as DockLayout.ILayoutConfig;
|
|
77
|
+
if (!config.main) {
|
|
78
|
+
this._pendingDefaults.clear();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Build a lookup: widgetId → { defaultSize }
|
|
83
|
+
const lookup = new Map<string, { defaultSize?: number }>();
|
|
84
|
+
for (const [wa, defaults] of this._pendingDefaults) {
|
|
85
|
+
lookup.set(wa.widget.id(), defaults);
|
|
86
|
+
}
|
|
87
|
+
this._pendingDefaults.clear();
|
|
88
|
+
|
|
89
|
+
const containerWidth = this.width();
|
|
90
|
+
const containerHeight = this.height();
|
|
91
|
+
|
|
92
|
+
const applySizes = (area: DockLayout.AreaConfig, availWidth: number, availHeight: number): void => {
|
|
93
|
+
if (!area || area.type !== "split-area") return;
|
|
94
|
+
|
|
95
|
+
const isHorizontal = area.orientation === "horizontal";
|
|
96
|
+
const totalSpace = isHorizontal ? availWidth : availHeight;
|
|
97
|
+
const n = area.children.length;
|
|
98
|
+
|
|
99
|
+
// Collect requested pixel sizes per child
|
|
100
|
+
let usedSpace = 0;
|
|
101
|
+
let flexCount = 0;
|
|
102
|
+
const pixelSizes: (number | null)[] = area.children.map((child, i) => {
|
|
103
|
+
if (child.type === "tab-area") {
|
|
104
|
+
for (const w of (child as any).widgets) {
|
|
105
|
+
const defaults = lookup.get(w?.__id);
|
|
106
|
+
if (defaults) {
|
|
107
|
+
const size = defaults.defaultSize;
|
|
108
|
+
if (size != null) {
|
|
109
|
+
usedSpace += size;
|
|
110
|
+
return size;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
flexCount++;
|
|
116
|
+
return null;
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Only apply if at least one child has a default
|
|
120
|
+
if (flexCount < n) {
|
|
121
|
+
const remainingSpace = Math.max(0, totalSpace - usedSpace);
|
|
122
|
+
const flexSize = flexCount > 0 ? remainingSpace / flexCount : 0;
|
|
123
|
+
area.sizes = pixelSizes.map(px => px != null ? px : flexSize);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Recurse into nested split areas with their allocated portion
|
|
127
|
+
for (let i = 0; i < area.children.length; i++) {
|
|
128
|
+
const child = area.children[i];
|
|
129
|
+
if (child.type === "split-area") {
|
|
130
|
+
const ratio = area.sizes[i] / (area.sizes.reduce((a, b) => a + b, 0) || 1);
|
|
131
|
+
const childW = isHorizontal ? availWidth * ratio : availWidth;
|
|
132
|
+
const childH = isHorizontal ? availHeight : availHeight * ratio;
|
|
133
|
+
applySizes(child, childW, childH);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
applySizes(config.main, containerWidth, containerHeight);
|
|
139
|
+
this._dock.restoreLayout(config as any);
|
|
140
|
+
}
|
|
141
|
+
|
|
41
142
|
removeWidget(widget: Widget) {
|
|
42
143
|
const wa = this.getWidgetAdapter(widget);
|
|
43
144
|
if (wa) {
|
|
@@ -74,25 +175,16 @@ export class DockPanel extends HTMLWidget implements IMessageHandler, IMessageHo
|
|
|
74
175
|
return this;
|
|
75
176
|
}
|
|
76
177
|
|
|
77
|
-
private _pPlaceholder;
|
|
78
178
|
enter(domNode, element) {
|
|
79
179
|
super.enter(domNode, element);
|
|
80
|
-
this.
|
|
81
|
-
PWidget.attach(this._dock, this._pPlaceholder.node());
|
|
180
|
+
PWidget.attach(this._dock, domNode);
|
|
82
181
|
}
|
|
83
182
|
|
|
84
183
|
_prevHideSingleTabs;
|
|
85
184
|
update(domNode, element) {
|
|
86
185
|
super.update(domNode, element);
|
|
87
|
-
|
|
88
|
-
this._pPlaceholder
|
|
89
|
-
.style("width", this.width() + "px")
|
|
90
|
-
.style("height", this.height() + "px")
|
|
91
|
-
.style("overflow", "hidden")
|
|
92
|
-
;
|
|
93
|
-
|
|
94
186
|
element.select(".lm-Widget")
|
|
95
|
-
.style("width", this.
|
|
187
|
+
.style("width", this.width() + "px")
|
|
96
188
|
.style("height", this.height() + "px")
|
|
97
189
|
;
|
|
98
190
|
|
|
@@ -125,6 +217,7 @@ export class DockPanel extends HTMLWidget implements IMessageHandler, IMessageHo
|
|
|
125
217
|
this.layoutObj(null);
|
|
126
218
|
}
|
|
127
219
|
return super.render((w) => {
|
|
220
|
+
this._applyPendingDefaults();
|
|
128
221
|
this._dock.content().watchRendered(this, callback);
|
|
129
222
|
this._dock.update();
|
|
130
223
|
setTimeout(() => {
|
|
@@ -150,39 +243,6 @@ export class DockPanel extends HTMLWidget implements IMessageHandler, IMessageHo
|
|
|
150
243
|
this._dock.fit();
|
|
151
244
|
}
|
|
152
245
|
|
|
153
|
-
// Phosphor Messaging ---
|
|
154
|
-
messageHook(handler: IMessageHandler, msg: Message): boolean {
|
|
155
|
-
if (handler === this) {
|
|
156
|
-
this.processMessage(msg);
|
|
157
|
-
}
|
|
158
|
-
return true;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
private _lazyLayoutChanged = Utility.debounce(async () => {
|
|
162
|
-
this.layoutChanged();
|
|
163
|
-
}, 1000);
|
|
164
|
-
|
|
165
|
-
_prevActive: Widget;
|
|
166
|
-
processMessage(msg: Message): void {
|
|
167
|
-
switch (msg.type) {
|
|
168
|
-
case Msg.WAActivateRequest.type:
|
|
169
|
-
const wa = (msg as Msg.WAActivateRequest).wa;
|
|
170
|
-
const widget = wa.widget;
|
|
171
|
-
if (this._prevActive !== widget) {
|
|
172
|
-
this._prevActive = widget;
|
|
173
|
-
this.childActivation(widget, wa);
|
|
174
|
-
}
|
|
175
|
-
break;
|
|
176
|
-
case Msg.WALayoutChanged.type:
|
|
177
|
-
this._lazyLayoutChanged();
|
|
178
|
-
break;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
active(): Widget {
|
|
183
|
-
return this._prevActive;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
246
|
// Events ---
|
|
187
247
|
childActivation(w: Widget, wa: WidgetAdapter) {
|
|
188
248
|
}
|
package/src/SplitPanel.ts
CHANGED
|
@@ -1,36 +1,61 @@
|
|
|
1
1
|
import { HTMLWidget, SVGWidget, Widget } from "@hpcc-js/common";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { BasePanel } from "./BasePanel.ts";
|
|
3
|
+
import { SplitPanel as PSplitPanel, Widget as PWidget } from "./phosphor-shim.ts";
|
|
4
|
+
import { WidgetAdapter, WidgetAdapterArray } from "./WidgetAdapter.ts";
|
|
4
5
|
|
|
5
6
|
import "../src/DockPanel.css";
|
|
6
7
|
|
|
7
|
-
export
|
|
8
|
+
export namespace SplitPanel {
|
|
9
|
+
|
|
10
|
+
export interface IAddWidgetOptions extends BasePanel.IAddWidgetOptions {
|
|
11
|
+
/** Maximum size in pixels */
|
|
12
|
+
maxSize?: number;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class SplitPanel extends BasePanel {
|
|
8
17
|
private _split: PSplitPanel;
|
|
9
|
-
|
|
18
|
+
protected _content = WidgetAdapterArray.create();
|
|
10
19
|
|
|
11
|
-
constructor(
|
|
20
|
+
constructor(readonly _orientation: "horizontal" | "vertical" = "vertical") {
|
|
12
21
|
super();
|
|
13
|
-
this._split = new PSplitPanel({ orientation });
|
|
14
|
-
this._tag = "div";
|
|
22
|
+
this._split = new PSplitPanel({ orientation: _orientation });
|
|
15
23
|
this._split.id = "p" + this.id();
|
|
16
24
|
}
|
|
17
25
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
private _pendingDefaults: Map<number, { defaultSize?: number }> = new Map();
|
|
27
|
+
addWidget(widget: SVGWidget | HTMLWidget, options?: SplitPanel.IAddWidgetOptions): this {
|
|
28
|
+
const opts = options || {};
|
|
29
|
+
const wa = new WidgetAdapter(this, widget);
|
|
30
|
+
wa.padding = opts.padding ?? 0;
|
|
31
|
+
|
|
32
|
+
if (opts.minSize != null || opts.maxSize != null) {
|
|
33
|
+
const style = wa.node.style;
|
|
34
|
+
if (opts.minSize != null) {
|
|
35
|
+
if (this._orientation === "horizontal") {
|
|
36
|
+
style.minWidth = `${opts.minSize}px`;
|
|
37
|
+
} else {
|
|
38
|
+
style.minHeight = `${opts.minSize}px`;
|
|
39
|
+
}
|
|
24
40
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
41
|
+
if (opts.maxSize != null) {
|
|
42
|
+
if (this._orientation === "horizontal") {
|
|
43
|
+
style.maxWidth = `${opts.maxSize}px`;
|
|
44
|
+
} else {
|
|
45
|
+
style.maxHeight = `${opts.maxSize}px`;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
29
49
|
|
|
30
|
-
addWidget(widget: SVGWidget | HTMLWidget) {
|
|
31
|
-
const wa = new WidgetAdapter(this, widget);
|
|
32
50
|
this._split.addWidget(wa);
|
|
33
|
-
this.
|
|
51
|
+
this._content.push(wa);
|
|
52
|
+
|
|
53
|
+
if (opts.defaultSize != null) {
|
|
54
|
+
this._pendingDefaults.set(this._content.length - 1, {
|
|
55
|
+
defaultSize: opts.defaultSize
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
34
59
|
return this;
|
|
35
60
|
}
|
|
36
61
|
|
|
@@ -62,26 +87,51 @@ export class SplitPanel extends HTMLWidget {
|
|
|
62
87
|
|
|
63
88
|
render(callback?: (w: Widget) => void): this {
|
|
64
89
|
return super.render(w => {
|
|
65
|
-
this.
|
|
90
|
+
this._applyPendingDefaults();
|
|
91
|
+
this._content.watchRendered(this, callback);
|
|
66
92
|
this._split.update();
|
|
67
93
|
});
|
|
68
94
|
}
|
|
69
95
|
|
|
70
|
-
private
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
96
|
+
private _applyPendingDefaults() {
|
|
97
|
+
if (this._pendingDefaults.size === 0) return;
|
|
98
|
+
|
|
99
|
+
const isHorizontal = this._orientation === "horizontal";
|
|
100
|
+
const totalSpace = isHorizontal ? this.width() : this.height();
|
|
101
|
+
const n = this._content.length;
|
|
102
|
+
|
|
103
|
+
let usedSpace = 0;
|
|
104
|
+
let flexCount = 0;
|
|
105
|
+
const pixelSizes: (number | null)[] = [];
|
|
106
|
+
|
|
107
|
+
for (let i = 0; i < n; i++) {
|
|
108
|
+
const defaults = this._pendingDefaults.get(i);
|
|
109
|
+
if (defaults) {
|
|
110
|
+
const size = defaults.defaultSize;
|
|
111
|
+
if (size != null) {
|
|
112
|
+
pixelSizes.push(size);
|
|
113
|
+
usedSpace += size;
|
|
114
|
+
continue;
|
|
78
115
|
}
|
|
79
|
-
|
|
116
|
+
}
|
|
117
|
+
pixelSizes.push(null);
|
|
118
|
+
flexCount++;
|
|
80
119
|
}
|
|
120
|
+
|
|
121
|
+
this._pendingDefaults.clear();
|
|
122
|
+
|
|
123
|
+
const remainingSpace = Math.max(0, totalSpace - usedSpace);
|
|
124
|
+
const flexSize = flexCount > 0 ? remainingSpace / flexCount : 0;
|
|
125
|
+
const sizes = pixelSizes.map(px => px != null ? px : flexSize);
|
|
126
|
+
this._split.setRelativeSizes(sizes);
|
|
81
127
|
}
|
|
82
128
|
|
|
83
|
-
//
|
|
84
|
-
childActivation(w: Widget) {
|
|
129
|
+
// Events ---
|
|
130
|
+
childActivation(w: Widget, wa: WidgetAdapter) {
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
layoutChanged() {
|
|
85
134
|
}
|
|
86
135
|
}
|
|
87
136
|
SplitPanel.prototype._class += " phosphor_SplitPanel";
|
|
137
|
+
|
package/src/TabPanel.ts
CHANGED
|
@@ -1,55 +1,63 @@
|
|
|
1
1
|
import { HTMLWidget, SVGWidget, Widget } from "@hpcc-js/common";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { BasePanel } from "./BasePanel.ts";
|
|
3
|
+
import { TabPanel as PTabPanel, Widget as PWidget } from "./phosphor-shim.ts";
|
|
4
|
+
import { WidgetAdapter, WidgetAdapterArray, WidgetAdapterExt } from "./WidgetAdapter.ts";
|
|
4
5
|
|
|
5
6
|
import "../src/DockPanel.css";
|
|
6
7
|
|
|
7
|
-
export
|
|
8
|
+
export namespace TabPanel {
|
|
9
|
+
|
|
10
|
+
export interface IAddWidgetOptions {
|
|
11
|
+
/** Inner padding in pixels (default 8) */
|
|
12
|
+
padding?: number;
|
|
13
|
+
/** Tab title */
|
|
14
|
+
title?: string;
|
|
15
|
+
/** Widget adapter extensions (overflow settings) */
|
|
16
|
+
ext?: WidgetAdapterExt;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class TabPanel extends BasePanel {
|
|
8
21
|
private _tab = new PTabPanel({ tabPlacement: "top" });
|
|
9
|
-
protected
|
|
22
|
+
protected _content = WidgetAdapterArray.create();
|
|
10
23
|
|
|
11
24
|
constructor() {
|
|
12
25
|
super();
|
|
13
|
-
this._tag = "div";
|
|
14
26
|
this._tab.id = "p" + this.id();
|
|
15
27
|
}
|
|
16
28
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
29
|
+
addWidget(widget: SVGWidget | HTMLWidget, options: TabPanel.IAddWidgetOptions): this;
|
|
30
|
+
/** @deprecated Use options object form instead */
|
|
31
|
+
addWidget(widget: SVGWidget | HTMLWidget, title: string, ext?: WidgetAdapterExt): this;
|
|
32
|
+
addWidget(widget: SVGWidget | HTMLWidget, titleOrOptions: string | TabPanel.IAddWidgetOptions, ext: WidgetAdapterExt = {}) {
|
|
33
|
+
const opts: TabPanel.IAddWidgetOptions = typeof titleOrOptions === "string"
|
|
34
|
+
? { title: titleOrOptions, ext }
|
|
35
|
+
: titleOrOptions;
|
|
22
36
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return true;
|
|
29
|
-
}
|
|
30
|
-
return false;
|
|
31
|
-
});
|
|
32
|
-
return retVal;
|
|
33
|
-
}
|
|
37
|
+
const {
|
|
38
|
+
title = "",
|
|
39
|
+
ext: widgetExt = {},
|
|
40
|
+
padding = 8
|
|
41
|
+
} = opts;
|
|
34
42
|
|
|
35
|
-
addWidget(widget: SVGWidget | HTMLWidget, title: string, ext: WidgetAdapterExt = {}) {
|
|
36
43
|
if (!this._prevActive) {
|
|
37
44
|
this._prevActive = widget;
|
|
38
45
|
}
|
|
39
|
-
const wa = new WidgetAdapter(this, widget, undefined, undefined,
|
|
46
|
+
const wa = new WidgetAdapter(this, widget, undefined, undefined, widgetExt);
|
|
40
47
|
wa.title.label = title;
|
|
41
|
-
wa.padding =
|
|
48
|
+
wa.padding = padding;
|
|
49
|
+
|
|
42
50
|
this._tab.addWidget(wa);
|
|
43
|
-
this.
|
|
51
|
+
this._content.push(wa);
|
|
44
52
|
return this;
|
|
45
53
|
}
|
|
46
54
|
|
|
47
55
|
removeWidget(widget: SVGWidget | HTMLWidget) {
|
|
48
56
|
const wa = this.getWidgetAdapter(widget);
|
|
49
57
|
if (wa) {
|
|
50
|
-
const found = this.
|
|
58
|
+
const found = this._content.indexOf(wa);
|
|
51
59
|
if (found >= 0) {
|
|
52
|
-
this.
|
|
60
|
+
this._content.splice(found, 1);
|
|
53
61
|
}
|
|
54
62
|
widget.target(null);
|
|
55
63
|
wa.dispose();
|
|
@@ -77,35 +85,11 @@ export class TabPanel extends HTMLWidget {
|
|
|
77
85
|
|
|
78
86
|
render(callback?: (w: Widget) => void): this {
|
|
79
87
|
return super.render(w => {
|
|
80
|
-
this.
|
|
88
|
+
this._content.watchRendered(this, callback);
|
|
81
89
|
this._tab.update();
|
|
82
90
|
});
|
|
83
91
|
}
|
|
84
92
|
|
|
85
|
-
// Phosphor Messaging ---
|
|
86
|
-
messageHook(handler: IMessageHandler, msg: Message): boolean {
|
|
87
|
-
if (handler === this) {
|
|
88
|
-
this.processMessage(msg);
|
|
89
|
-
}
|
|
90
|
-
return true;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
private _prevActive: Widget;
|
|
94
|
-
processMessage(msg: Message): void {
|
|
95
|
-
switch (msg.type) {
|
|
96
|
-
case "wa-activate-request":
|
|
97
|
-
const widget = (msg as Msg.WAActivateRequest).wa.widget;
|
|
98
|
-
if (this._prevActive !== widget) {
|
|
99
|
-
this._prevActive = widget;
|
|
100
|
-
this.childActivation(widget);
|
|
101
|
-
}
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
childActivation(w: Widget) {
|
|
107
|
-
}
|
|
108
|
-
|
|
109
93
|
active(): Widget;
|
|
110
94
|
active(_: Widget);
|
|
111
95
|
active(_?: Widget): Widget | this {
|
|
@@ -113,5 +97,12 @@ export class TabPanel extends HTMLWidget {
|
|
|
113
97
|
this._tab.currentWidget = this.getWidgetAdapter(_);
|
|
114
98
|
return this;
|
|
115
99
|
}
|
|
100
|
+
|
|
101
|
+
// Events ---
|
|
102
|
+
childActivation(w: Widget, wa: WidgetAdapter) {
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
layoutChanged() {
|
|
106
|
+
}
|
|
116
107
|
}
|
|
117
108
|
TabPanel.prototype._class += " phosphor_TabPanel";
|
package/src/WidgetAdapter.css
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { HTMLWidget, Widget } from "@hpcc-js/common";
|
|
2
|
+
import { Widget as PWidget, IMessageHandler, IMessageHook, Message } from "./phosphor-shim.ts";
|
|
3
|
+
import { WidgetAdapter, WidgetAdapterArray } from "./WidgetAdapter.ts";
|
|
4
|
+
export declare namespace BasePanel {
|
|
5
|
+
interface IAddWidgetOptions {
|
|
6
|
+
/** Minimum size in pixels */
|
|
7
|
+
minSize?: number;
|
|
8
|
+
/** Preferred/default size in pixels — used as initial size hint */
|
|
9
|
+
defaultSize?: number;
|
|
10
|
+
/** Inner padding in pixels (default 8) */
|
|
11
|
+
padding?: number;
|
|
12
|
+
/** Reference widget for split/tab positioning */
|
|
13
|
+
refWidget?: Widget;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export declare abstract class BasePanel extends HTMLWidget implements IMessageHandler, IMessageHook {
|
|
17
|
+
protected abstract _content: WidgetAdapterArray;
|
|
18
|
+
constructor();
|
|
19
|
+
getWidget(wa: PWidget): Widget | undefined;
|
|
20
|
+
getWidgetAdapter(widget: Widget): WidgetAdapter | null;
|
|
21
|
+
protected _prevActive: Widget;
|
|
22
|
+
active(): Widget;
|
|
23
|
+
protected _lazyLayoutChanged: (..._dummyArgs: any[]) => void;
|
|
24
|
+
processMessage(msg: Message): void;
|
|
25
|
+
messageHook(handler: IMessageHandler, msg: Message): boolean;
|
|
26
|
+
childActivation(w: Widget, wa: WidgetAdapter): void;
|
|
27
|
+
layoutChanged(): void;
|
|
28
|
+
}
|