flexdesk 0.17.11 → 0.17.12

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/tiles.js CHANGED
@@ -36,7 +36,7 @@ import {
36
36
  purge,
37
37
  resize,
38
38
  strideDownsampleShared
39
- } from "./chunk-2YTM3GJE.js";
39
+ } from "./chunk-75FLVAUT.js";
40
40
  import "./chunk-NIJEJAZ7.js";
41
41
  import {
42
42
  isHexColor
@@ -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.`); a
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flexdesk",
3
- "version": "0.17.11",
3
+ "version": "0.17.12",
4
4
  "description": "A tiling window manager and widget set for desktop-class web applications. Zero runtime dependencies.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -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) { showMessage(cell, opts.emptyMessage); deferred.delete(index); return false; }
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
- showMessage(cell, opts.emptyMessage);
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) {