flexdesk 0.17.11 → 0.17.13
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/CHANGELOG.md +33 -0
- package/css/base.css +19 -0
- package/css/flexdesk.css +28 -2
- package/css/overrides.css +9 -2
- package/dist/charts.js +3 -3
- package/dist/{chunk-NIJEJAZ7.js → chunk-2PM6ZMNS.js} +2 -2
- package/dist/{chunk-F35XSQWA.js → chunk-IG74S3ZH.js} +2 -2
- package/dist/{chunk-CVMCZZX2.js → chunk-MADMGRX2.js} +18 -7
- package/dist/chunk-MADMGRX2.js.map +7 -0
- package/dist/{chunk-2YTM3GJE.js → chunk-TLIV6QSU.js} +16 -5
- package/dist/{chunk-2YTM3GJE.js.map → chunk-TLIV6QSU.js.map} +2 -2
- package/dist/{chunk-BE2PU3LL.js → chunk-ZMH3OC7H.js} +2 -2
- package/dist/flexdesk.css +28 -2
- package/dist/flow.js +3 -3
- package/dist/tiles.js +5 -5
- package/dist/widgets.js +4 -4
- package/dist/wm.js +3 -3
- package/docs/components/charts.md +37 -1
- package/docs/components/menus.md +59 -0
- package/package.json +1 -1
- package/src/charts/plot_grid.js +21 -3
- package/src/charts/plot_popout_window.js +7 -0
- package/src/ui/components/managed_window.js +24 -6
- package/dist/chunk-CVMCZZX2.js.map +0 -7
- /package/dist/{chunk-NIJEJAZ7.js.map → chunk-2PM6ZMNS.js.map} +0 -0
- /package/dist/{chunk-F35XSQWA.js.map → chunk-IG74S3ZH.js.map} +0 -0
- /package/dist/{chunk-BE2PU3LL.js.map → chunk-ZMH3OC7H.js.map} +0 -0
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
normalizeHex,
|
|
10
10
|
selectBox,
|
|
11
11
|
toggleChips
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-MADMGRX2.js";
|
|
13
13
|
import {
|
|
14
14
|
DragReorder
|
|
15
15
|
} from "./chunk-WUFLGETN.js";
|
|
@@ -1308,4 +1308,4 @@ export {
|
|
|
1308
1308
|
openConfirm,
|
|
1309
1309
|
openModal
|
|
1310
1310
|
};
|
|
1311
|
-
//# sourceMappingURL=chunk-
|
|
1311
|
+
//# sourceMappingURL=chunk-ZMH3OC7H.js.map
|
package/dist/flexdesk.css
CHANGED
|
@@ -1695,6 +1695,25 @@ body.twm-dt-col-resizing {
|
|
|
1695
1695
|
.twm-global-top-bar, .twm-global-top-bar .twm-bar-left, .twm-global-top-bar .twm-bar-center, .twm-global-top-bar .twm-bar-right {
|
|
1696
1696
|
min-width: 0; /* allow flex children to shrink */
|
|
1697
1697
|
}
|
|
1698
|
+
/* Global menu row (STORY-0119, 0.17.13): a shell's `chrome.menuBar` in a
|
|
1699
|
+
* full-width strip of its own, directly under .twm-global-top-bar in
|
|
1700
|
+
* body.twm-body's column (host.css), so the host below shrinks by its height.
|
|
1701
|
+
* It is a sibling of the top bar, not inside it: a window-drag region bound
|
|
1702
|
+
* to the top bar does not include it. No z-index, so it is no stacking
|
|
1703
|
+
* context: the top bar's dropdowns draw over it, and the .twm-menubar inside
|
|
1704
|
+
* keeps its own styles, its menus (position: fixed, --z-over-windows) drawing
|
|
1705
|
+
* over the page. Its height is the bar's plus the padding. */
|
|
1706
|
+
.twm-global-menu-bar {
|
|
1707
|
+
display: flex;
|
|
1708
|
+
align-items: center;
|
|
1709
|
+
width: 100%;
|
|
1710
|
+
box-sizing: border-box;
|
|
1711
|
+
padding: var(--space-xxs) var(--space-xs);
|
|
1712
|
+
background-color: var(--surface-1);
|
|
1713
|
+
color: var(--text-body);
|
|
1714
|
+
border-bottom: 1px solid var(--surface-0);
|
|
1715
|
+
overflow: visible; /* the bar's menus extend below */
|
|
1716
|
+
}
|
|
1698
1717
|
.twm-bar-center {
|
|
1699
1718
|
white-space: nowrap;
|
|
1700
1719
|
overflow: hidden;
|
|
@@ -8528,8 +8547,9 @@ body.twm-dragging * { user-select: none; }
|
|
|
8528
8547
|
* the WM host fills the remaining space.
|
|
8529
8548
|
*/
|
|
8530
8549
|
|
|
8531
|
-
/* Body is a flex column: top bar,
|
|
8532
|
-
* override the legacy `position: fixed` on both
|
|
8550
|
+
/* Body is a flex column: top bar, menu row (when there is one), WM host
|
|
8551
|
+
* (fill), bottom bar. We override the legacy `position: fixed` on both
|
|
8552
|
+
* bars so they become
|
|
8533
8553
|
* deterministic flex items and the host's height is exactly the
|
|
8534
8554
|
* leftover space — no math, no overlap, no off-by-N pixels. Their
|
|
8535
8555
|
* `z-index` stays put for dropdown stacking. */
|
|
@@ -8547,6 +8567,12 @@ body.twm-body > .twm-global-bottom-bar {
|
|
|
8547
8567
|
inset: auto !important;
|
|
8548
8568
|
flex: 0 0 auto;
|
|
8549
8569
|
}
|
|
8570
|
+
/* The menu row (STORY-0119) sits between the top bar and the host, in
|
|
8571
|
+
* normal flow: a fixed-size strip, so the host's leftover space is less by
|
|
8572
|
+
* exactly its height. */
|
|
8573
|
+
body.twm-body > .twm-global-menu-bar {
|
|
8574
|
+
flex: 0 0 auto;
|
|
8575
|
+
}
|
|
8550
8576
|
body.twm-body > .twm-host {
|
|
8551
8577
|
flex: 1 1 auto;
|
|
8552
8578
|
min-height: 0;
|
package/dist/flow.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
modalHost
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ZMH3OC7H.js";
|
|
4
4
|
import {
|
|
5
5
|
DataTable
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-2PM6ZMNS.js";
|
|
7
7
|
import {
|
|
8
8
|
choiceList,
|
|
9
9
|
selectBox,
|
|
10
10
|
shownValue,
|
|
11
11
|
toggleChips
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-MADMGRX2.js";
|
|
13
13
|
import "./chunk-HNU6MM2B.js";
|
|
14
14
|
import "./chunk-WUFLGETN.js";
|
|
15
15
|
import {
|
package/dist/tiles.js
CHANGED
|
@@ -17,11 +17,11 @@ import {
|
|
|
17
17
|
} from "./chunk-MIW7D6WC.js";
|
|
18
18
|
import {
|
|
19
19
|
showContextMenu
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-IG74S3ZH.js";
|
|
21
21
|
import {
|
|
22
22
|
modalHost,
|
|
23
23
|
openModal
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-ZMH3OC7H.js";
|
|
25
25
|
import {
|
|
26
26
|
buildHistogramTrace,
|
|
27
27
|
buildLegendLayout,
|
|
@@ -36,11 +36,11 @@ import {
|
|
|
36
36
|
purge,
|
|
37
37
|
resize,
|
|
38
38
|
strideDownsampleShared
|
|
39
|
-
} from "./chunk-
|
|
40
|
-
import "./chunk-
|
|
39
|
+
} from "./chunk-TLIV6QSU.js";
|
|
40
|
+
import "./chunk-2PM6ZMNS.js";
|
|
41
41
|
import {
|
|
42
42
|
isHexColor
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-MADMGRX2.js";
|
|
44
44
|
import "./chunk-HNU6MM2B.js";
|
|
45
45
|
import "./chunk-WUFLGETN.js";
|
|
46
46
|
import {
|
package/dist/widgets.js
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
import {
|
|
27
27
|
hideContextMenu,
|
|
28
28
|
showContextMenu
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-IG74S3ZH.js";
|
|
30
30
|
import {
|
|
31
31
|
ColorListField,
|
|
32
32
|
SortableList,
|
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
openForm,
|
|
36
36
|
openModal,
|
|
37
37
|
setModalHost
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-ZMH3OC7H.js";
|
|
39
39
|
import {
|
|
40
40
|
ACTIONS,
|
|
41
41
|
AutocompleteField,
|
|
@@ -82,7 +82,7 @@ import {
|
|
|
82
82
|
resolvePaging,
|
|
83
83
|
toTSV,
|
|
84
84
|
tsvCell
|
|
85
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-2PM6ZMNS.js";
|
|
86
86
|
import {
|
|
87
87
|
ColorField,
|
|
88
88
|
DEFAULT_WINDOW_STORAGE_KEY,
|
|
@@ -96,7 +96,7 @@ import {
|
|
|
96
96
|
normalizeHex,
|
|
97
97
|
selectBox,
|
|
98
98
|
shownValue
|
|
99
|
-
} from "./chunk-
|
|
99
|
+
} from "./chunk-MADMGRX2.js";
|
|
100
100
|
import {
|
|
101
101
|
getSetting
|
|
102
102
|
} from "./chunk-HNU6MM2B.js";
|
package/dist/wm.js
CHANGED
|
@@ -8,14 +8,14 @@ import {
|
|
|
8
8
|
} from "./chunk-REJ5HLIM.js";
|
|
9
9
|
import {
|
|
10
10
|
showContextMenu
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-IG74S3ZH.js";
|
|
12
12
|
import {
|
|
13
13
|
openForm
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-ZMH3OC7H.js";
|
|
15
15
|
import {
|
|
16
16
|
ManagedWindow,
|
|
17
17
|
isRowActivation
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-MADMGRX2.js";
|
|
19
19
|
import "./chunk-HNU6MM2B.js";
|
|
20
20
|
import {
|
|
21
21
|
NotebookTabBar
|
|
@@ -4,6 +4,40 @@
|
|
|
4
4
|
|
|
5
5
|
Plotly-backed charts: the wrapper, the chart types, subplot layouts, downsampling, the plot grid, the plot-config editor and the two plot windows. Plotly itself is not in the bundle; call `setPlotlySource()` with the path to your copy. The plot window's `streamEvents` option is described with the [other application-neutral options](../concepts/domain-neutrality.md#no-application-globals-the-gallery-picker-and-plot-window-events-0170).
|
|
6
6
|
|
|
7
|
+
## An empty cell can name what is missing: `emptyMessage` as a function (0.17.12)
|
|
8
|
+
|
|
9
|
+
[`renderPlotGrid`](#a-plot-config-in-any-container-renderplotgrid-0170),
|
|
10
|
+
`PlotPopoutWindow` and `openPlotPopoutWindow` take `emptyMessage` as a string,
|
|
11
|
+
as before, or as a function of the subplot:
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
const grid = renderPlotGrid(el, plotConfig, results, {
|
|
15
|
+
emptyMessage: (subplot, results, index) => {
|
|
16
|
+
const name = subplot.yAxes[0].series[0]?.variable;
|
|
17
|
+
return results ? `${name} not in this run` : 'No data yet.';
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
await grid.update({ results: next }); // the empty cells say what is missing in `next`
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The function is called each time a cell is drawn empty, with that cell's
|
|
24
|
+
subplot, the grid's results at that moment (as the grid was given them, so null or undefined when there are none) and the
|
|
25
|
+
cell's index in the layout: on the first draw, on every `update()` (new
|
|
26
|
+
`results`, a new `plotConfig`, `overlays` or `hiddenSeries`), on a resize that
|
|
27
|
+
draws a deferred cell, and when a subplot fails to draw. So the message follows
|
|
28
|
+
results that stream in or change, without disposing and rebuilding the grid. A
|
|
29
|
+
slot with no subplot still shows `noSubplotMessage`.
|
|
30
|
+
|
|
31
|
+
The plot window hands its `emptyMessage` to its grid, so the function is called
|
|
32
|
+
on the window's own redraws too: its refresh and stream events and
|
|
33
|
+
`updateConfig()`. `openPlotPopoutWindow` forwards it with its other options.
|
|
34
|
+
The Data tab's `tableEmptyMessage` stays a string.
|
|
35
|
+
|
|
36
|
+
A string `emptyMessage` is shown exactly as before, and no option or export
|
|
37
|
+
changed.
|
|
38
|
+
|
|
39
|
+
`tests/plot_grid.test.mjs` §8 pins it.
|
|
40
|
+
|
|
7
41
|
## Interpolation `'auto'` is a listed choice (0.17.9)
|
|
8
42
|
|
|
9
43
|
A subplot's `interpolation` is `'linear'`, `'step'`, `'cubic'` or `'auto'`.
|
|
@@ -197,7 +231,9 @@ subplot: its chart type, interpolation, data points and stack groups, every
|
|
|
197
231
|
y-axis (left or right, linear or log, min, max, step), reference lines and
|
|
198
232
|
shaded bands, the NaN strategy, the x-axis clipped to the results' time
|
|
199
233
|
range, or an x variable instead of time (`xAxis: { useTime: false, variable }`).
|
|
200
|
-
A subplot with nothing to draw shows `emptyMessage` (default `No data yet
|
|
234
|
+
A subplot with nothing to draw shows `emptyMessage` (default `No data yet.`;
|
|
235
|
+
[a function of the subplot](#an-empty-cell-can-name-what-is-missing-emptymessage-as-a-function-01712)
|
|
236
|
+
since 0.17.12); a
|
|
201
237
|
slot with no subplot shows `noSubplotMessage` (`No subplot configured.`); null
|
|
202
238
|
results empty every subplot. A cell that is detached or has no size is not
|
|
203
239
|
drawn: the grid's own ResizeObserver draws it once it has one. `dispose()`
|
package/docs/components/menus.md
CHANGED
|
@@ -101,6 +101,65 @@ The bar is `.twm-menubar` (not the older `.twm-menu-bar` icon strip).
|
|
|
101
101
|
`tests/menu_bar.test.mjs` drives it in jsdom; `tests/menu_bar_css.test.mjs`
|
|
102
102
|
holds its stylesheet.
|
|
103
103
|
|
|
104
|
+
## The menu bar in its own row under the top bar (0.17.13)
|
|
105
|
+
|
|
106
|
+
A shell's menu bar can sit on a second line, as a full-width strip directly
|
|
107
|
+
under the top bar. Put `chrome.menuBar` in a `.twm-global-menu-bar` that is a
|
|
108
|
+
child of `body.twm-body`, between `.twm-global-top-bar` and the host:
|
|
109
|
+
|
|
110
|
+
```html
|
|
111
|
+
<body class="twm-body">
|
|
112
|
+
<div class="twm-global-top-bar">
|
|
113
|
+
<div class="twm-bar-left">…</div>
|
|
114
|
+
<div class="twm-bar-center twm-top-nav">…</div>
|
|
115
|
+
<div class="twm-bar-right">…</div>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="twm-global-menu-bar">
|
|
118
|
+
<div id="menus"></div> <!-- chrome.menuBar -->
|
|
119
|
+
</div>
|
|
120
|
+
<div class="twm-host">…</div> <!-- createShell's root -->
|
|
121
|
+
<div class="twm-global-bottom-bar">…</div>
|
|
122
|
+
</body>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
const shell = await createShell({
|
|
127
|
+
// …
|
|
128
|
+
menuBar: { menus, onAction },
|
|
129
|
+
chrome: { menuBar: document.getElementById('menus') },
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
- **The host gives up the row's height.** `body.twm-body` is a flex column,
|
|
134
|
+
and the row is a fixed-size item in it (`flex: 0 0 auto`). The host still
|
|
135
|
+
takes whatever is left, so it is shorter by the row's height and nothing is
|
|
136
|
+
drawn under the row. The row's height is the bar's (24px) plus its padding.
|
|
137
|
+
- **It is styled with FlexDesk tokens.** It is a flex row with
|
|
138
|
+
`padding: var(--space-xxs) var(--space-xs)`, background `var(--surface-1)`,
|
|
139
|
+
text `var(--text-body)` and a bottom border `var(--surface-0)`, the same
|
|
140
|
+
border as the top bar. The `.twm-menubar` inside it keeps its own styles.
|
|
141
|
+
- **It is not part of the top bar.** It is a sibling of `.twm-global-top-bar`,
|
|
142
|
+
not inside it. A window-drag region bound to the top bar
|
|
143
|
+
(`WindowChromeController`'s `topBar`, which defaults to
|
|
144
|
+
`.twm-global-top-bar`) does not include the row. A press on the row never
|
|
145
|
+
drags the window, and its menus open and take clicks as usual. The row sets
|
|
146
|
+
no `z-index`, so the top bar's dropdowns are drawn over it, and the bar's
|
|
147
|
+
menus (`position: fixed`) are drawn over the page.
|
|
148
|
+
- **Windows on the page stop under it.** A `ManagedWindow` on `document.body`
|
|
149
|
+
(no `container`) is clamped, centred and maximised below the top bar and
|
|
150
|
+
the row, not under the row. Windows inside the shell already stay within
|
|
151
|
+
the host.
|
|
152
|
+
- **A modal leaves it usable, like the top bar.** A modal `ManagedWindow`'s
|
|
153
|
+
backdrop starts below the top bar (`top: 35px`), so the row, like the top
|
|
154
|
+
bar, is not dimmed and its menus can still be opened while a modal is open.
|
|
155
|
+
|
|
156
|
+
A shell without the row looks exactly as before: nothing changes until the
|
|
157
|
+
page has a `.twm-global-menu-bar`. The row works only inside `body.twm-body`.
|
|
158
|
+
Outside it, the top bar is `position: fixed` and would cover the row.
|
|
159
|
+
`tests/menu_row_layout.test.mjs` loads flexdesk.css in jsdom and checks the
|
|
160
|
+
row's rules, its place after the top bar, the drag region and the window
|
|
161
|
+
bounds.
|
|
162
|
+
|
|
104
163
|
## Action dropdowns: separators (0.17.0)
|
|
105
164
|
|
|
106
165
|
`new ActionDropdown({ trigger, options, onSelect })` draws one button per
|
package/package.json
CHANGED
package/src/charts/plot_grid.js
CHANGED
|
@@ -72,6 +72,16 @@ function resolveSeriesData(series, results) {
|
|
|
72
72
|
return series._resultTime ? { y, x: series._resultTime } : { y };
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* The text of an empty cell: `message` itself when it is a string, else what
|
|
77
|
+
* the function `message(subplot, results, index)` returns. PlotPopoutWindow
|
|
78
|
+
* hands its `emptyMessage` to the grid unchanged, so this is the one place the
|
|
79
|
+
* two forms are told apart.
|
|
80
|
+
*/
|
|
81
|
+
function emptyMessageFor(message, subplot, results, index) {
|
|
82
|
+
return typeof message === 'function' ? message(subplot, results, index) : message;
|
|
83
|
+
}
|
|
84
|
+
|
|
75
85
|
/**
|
|
76
86
|
* Draw `plotConfig` into `container`.
|
|
77
87
|
*
|
|
@@ -95,7 +105,10 @@ function resolveSeriesData(series, results) {
|
|
|
95
105
|
* @param {Object} [options.theme] - Overrides for PLOT_GRID_THEME.
|
|
96
106
|
* @param {boolean} [options.alwaysShowLegend=false] - Draw a 'hidden' legend at the top.
|
|
97
107
|
* @param {Object} [options.layout] - Plotly layout fields merged over every figure's.
|
|
98
|
-
* @param {string} [options.emptyMessage='No data yet.']
|
|
108
|
+
* @param {string|Function} [options.emptyMessage='No data yet.'] - What a subplot
|
|
109
|
+
* with nothing to draw says: a string, or `(subplot, results, index) => string`,
|
|
110
|
+
* called each time a cell is drawn empty (first draw, every `update()`, a
|
|
111
|
+
* failed draw) with the grid's results at that moment (as the grid was given them, so null or undefined when there are none).
|
|
99
112
|
* @param {string} [options.noSubplotMessage='No subplot configured.']
|
|
100
113
|
* @param {Function} [options.onError] - `({ index, error })` when drawing a subplot
|
|
101
114
|
* throws — on an update or on a resize that draws a deferred one; that
|
|
@@ -170,6 +183,11 @@ function renderPlotGrid(container, plotConfig, results, options = {}) {
|
|
|
170
183
|
deferred.clear();
|
|
171
184
|
}
|
|
172
185
|
|
|
186
|
+
/** Show the empty message in cell `index`, for its subplot and the current results. */
|
|
187
|
+
function showEmpty(cell, index) {
|
|
188
|
+
showMessage(cell, emptyMessageFor(opts.emptyMessage, config?.subplots?.[index] ?? null, data, index));
|
|
189
|
+
}
|
|
190
|
+
|
|
173
191
|
function showMessage(cell, text) {
|
|
174
192
|
purgeCell(cell);
|
|
175
193
|
const msg = document.createElement('div');
|
|
@@ -348,7 +366,7 @@ function renderPlotGrid(container, plotConfig, results, options = {}) {
|
|
|
348
366
|
|
|
349
367
|
const figure = await buildFigure(sp, index);
|
|
350
368
|
if (!current()) return false;
|
|
351
|
-
if (!figure) {
|
|
369
|
+
if (!figure) { showEmpty(cell, index); deferred.delete(index); return false; }
|
|
352
370
|
|
|
353
371
|
if (!hasSize(cell)) { deferred.add(index); return false; }
|
|
354
372
|
deferred.delete(index);
|
|
@@ -381,7 +399,7 @@ function renderPlotGrid(container, plotConfig, results, options = {}) {
|
|
|
381
399
|
if (disposed) return false;
|
|
382
400
|
if (cells[index] === cell && tokens[index] === token) {
|
|
383
401
|
deferred.delete(index);
|
|
384
|
-
|
|
402
|
+
showEmpty(cell, index);
|
|
385
403
|
}
|
|
386
404
|
opts.onError?.({ index, error });
|
|
387
405
|
return false;
|
|
@@ -62,6 +62,11 @@ export class PlotPopoutWindow {
|
|
|
62
62
|
* updates through `refreshEvent` and explicit calls only.
|
|
63
63
|
* @param {Function} [options.resolveSeries] - `(series, results, subplot) => { y, x? } | null`
|
|
64
64
|
* (plot_grid.js); used by the chart and the data table. Default resolveSeriesData.
|
|
65
|
+
* @param {string|Function} [options.emptyMessage='No data yet.'] - What a chart
|
|
66
|
+
* cell with nothing to draw says: a string, or `(subplot, results, index) =>
|
|
67
|
+
* string`, called on every redraw that leaves the cell empty (renderPlotGrid).
|
|
68
|
+
* @param {string} [options.tableEmptyMessage='No data yet.'] - What the Data tab
|
|
69
|
+
* says with no rows.
|
|
65
70
|
*/
|
|
66
71
|
constructor({ id, title, plotConfig, getResults, services, host, onClose, actuals,
|
|
67
72
|
configRenderer, seriesTransforms, resolveSeries = resolveSeriesData,
|
|
@@ -1010,6 +1015,8 @@ const _openWindows = new Map();
|
|
|
1010
1015
|
* @param {Function} options.getResults - Returns { time: number[], series: { [key]: number[] } }
|
|
1011
1016
|
* @param {Object} options.services - { eventBus, logger }
|
|
1012
1017
|
* @param {Object} [options.actuals] - ETL overlay data
|
|
1018
|
+
* @param {string|Function} [options.emptyMessage] - As PlotPopoutWindow's: a string
|
|
1019
|
+
* or `(subplot, results, index) => string`. Every option is forwarded.
|
|
1013
1020
|
* @returns {PlotPopoutWindow}
|
|
1014
1021
|
*/
|
|
1015
1022
|
export async function openPlotPopoutWindow(options) {
|
|
@@ -34,6 +34,17 @@ const MAX_Z_INDEX = 6999;
|
|
|
34
34
|
const TOP_BAR_HEIGHT = 35;
|
|
35
35
|
const BOTTOM_BAR_HEIGHT = 22;
|
|
36
36
|
|
|
37
|
+
/** STORY-0119. The space above a window on `document.body`: the top bar, and
|
|
38
|
+
* under it a shell's menu row (`.twm-global-menu-bar`, a sibling of the top
|
|
39
|
+
* bar in body.twm-body's column) when the page has one. The row is measured,
|
|
40
|
+
* not assumed, because its height is its content's. Without a row this is
|
|
41
|
+
* TOP_BAR_HEIGHT, exactly as before. */
|
|
42
|
+
function _topInset() {
|
|
43
|
+
const row = typeof document !== 'undefined'
|
|
44
|
+
? document.querySelector('body > .twm-global-menu-bar') : null;
|
|
45
|
+
return TOP_BAR_HEIGHT + (row ? row.getBoundingClientRect().height : 0);
|
|
46
|
+
}
|
|
47
|
+
|
|
37
48
|
/** C11. How close to an edge the POINTER must come for a snap zone to arm.
|
|
38
49
|
* Small enough that it takes intent, large enough to hit without aiming. */
|
|
39
50
|
const SNAP_EDGE = 12;
|
|
@@ -953,6 +964,10 @@ export class ManagedWindow {
|
|
|
953
964
|
* width = window.innerWidth
|
|
954
965
|
* height = window.innerHeight - TOP_BAR_HEIGHT - BOTTOM_BAR_HEIGHT
|
|
955
966
|
*
|
|
967
|
+
* (STORY-0119: TOP_BAR_HEIGHT here is `_topInset()`, which is
|
|
968
|
+
* TOP_BAR_HEIGHT on a page without a menu row and adds the row's height
|
|
969
|
+
* on a page with one.)
|
|
970
|
+
*
|
|
956
971
|
* so, substituting into the clamps below:
|
|
957
972
|
*
|
|
958
973
|
* maxWidth = width = window.innerWidth ✓
|
|
@@ -1005,11 +1020,12 @@ export class ManagedWindow {
|
|
|
1005
1020
|
height: this.container.clientHeight,
|
|
1006
1021
|
};
|
|
1007
1022
|
}
|
|
1023
|
+
const top = _topInset();
|
|
1008
1024
|
return {
|
|
1009
1025
|
minX: 0,
|
|
1010
|
-
minY:
|
|
1026
|
+
minY: top,
|
|
1011
1027
|
width: window.innerWidth,
|
|
1012
|
-
height: window.innerHeight -
|
|
1028
|
+
height: window.innerHeight - top - BOTTOM_BAR_HEIGHT,
|
|
1013
1029
|
};
|
|
1014
1030
|
}
|
|
1015
1031
|
|
|
@@ -1266,7 +1282,8 @@ export class ManagedWindow {
|
|
|
1266
1282
|
} else {
|
|
1267
1283
|
// Center on screen (between top and bottom bars)
|
|
1268
1284
|
this.x = Math.max(0, (maxWidth - this.width) / 2);
|
|
1269
|
-
|
|
1285
|
+
const top = _topInset();
|
|
1286
|
+
this.y = Math.max(top, top + (maxHeight - this.height) / 2);
|
|
1270
1287
|
}
|
|
1271
1288
|
}
|
|
1272
1289
|
|
|
@@ -1584,7 +1601,8 @@ export class ManagedWindow {
|
|
|
1584
1601
|
|
|
1585
1602
|
// Calculate max available dimensions
|
|
1586
1603
|
const maxWidth = window.innerWidth;
|
|
1587
|
-
const
|
|
1604
|
+
const top = _topInset();
|
|
1605
|
+
const maxHeight = window.innerHeight - top - BOTTOM_BAR_HEIGHT;
|
|
1588
1606
|
|
|
1589
1607
|
let newX = state.startWinX;
|
|
1590
1608
|
let newY = state.startWinY;
|
|
@@ -1604,12 +1622,12 @@ export class ManagedWindow {
|
|
|
1604
1622
|
|
|
1605
1623
|
// Handle vertical resize
|
|
1606
1624
|
if (dir.includes('s')) {
|
|
1607
|
-
newH = Math.max(this.minHeight, Math.min(state.startHeight + dy, maxHeight - (newY -
|
|
1625
|
+
newH = Math.max(this.minHeight, Math.min(state.startHeight + dy, maxHeight - (newY - top)));
|
|
1608
1626
|
}
|
|
1609
1627
|
if (dir.includes('n')) {
|
|
1610
1628
|
const maxDy = state.startHeight - this.minHeight;
|
|
1611
1629
|
const actualDy = Math.min(dy, maxDy);
|
|
1612
|
-
newY = Math.max(
|
|
1630
|
+
newY = Math.max(top, state.startWinY + actualDy);
|
|
1613
1631
|
newH = state.startHeight - (newY - state.startWinY);
|
|
1614
1632
|
}
|
|
1615
1633
|
|