flexdesk 0.17.10 → 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/flexdesk.css CHANGED
@@ -2999,11 +2999,11 @@ body.twm-window-maximized .twm-resize-handle {
2999
2999
  color: var(--text-bright);
3000
3000
  }
3001
3001
  .twm-plot-window-toolbar .twm-btn-icon.twm-is-active {
3002
- color: #4A90D9;
3003
- background: rgba(74, 144, 217, 0.15);
3002
+ color: color-mix(in srgb, var(--accent-bright) 80%, white);
3003
+ background: color-mix(in srgb, color-mix(in srgb, var(--accent-bright) 80%, white) 15%, transparent);
3004
3004
  }
3005
3005
  .twm-plot-window-toolbar .twm-btn-icon.twm-is-active:hover {
3006
- background: rgba(74, 144, 217, 0.25);
3006
+ background: color-mix(in srgb, color-mix(in srgb, var(--accent-bright) 80%, white) 25%, transparent);
3007
3007
  }
3008
3008
  .twm-plot-window-content {
3009
3009
  flex: 1;
@@ -3074,7 +3074,7 @@ body.twm-window-maximized .twm-resize-handle {
3074
3074
  background: rgba(255, 255, 255, 0.02);
3075
3075
  }
3076
3076
  .twm-plot-window-container .twm-plot-tab-content .twm-preview-table tbody tr:hover {
3077
- background: rgba(74, 144, 217, 0.1);
3077
+ background: color-mix(in srgb, color-mix(in srgb, var(--accent-bright) 80%, white) 10%, transparent);
3078
3078
  }
3079
3079
  .twm-plot-window-container .twm-plot-tab-content .twm-preview-table td {
3080
3080
  color: #aaa;
@@ -3147,7 +3147,7 @@ body.twm-window-maximized .twm-resize-handle {
3147
3147
  background: rgba(255, 255, 255, 0.05);
3148
3148
  }
3149
3149
  .twm-plot-series-toggle-panel__item input[type="checkbox"] {
3150
- accent-color: #268bd2;
3150
+ accent-color: color-mix(in srgb, var(--accent-bright) 87%, white);
3151
3151
  flex-shrink: 0;
3152
3152
  }
3153
3153
  .twm-plot-series-toggle-panel__line-indicator {
@@ -3256,7 +3256,7 @@ body.twm-window-maximized .twm-resize-handle {
3256
3256
  outline: none;
3257
3257
  }
3258
3258
  .twm-data-series-plot-config select:focus {
3259
- border-color: #268bd2;
3259
+ border-color: color-mix(in srgb, var(--accent-bright) 87%, white);
3260
3260
  }
3261
3261
  .twm-data-series-plot-config__columns {
3262
3262
  max-height: 200px;
@@ -3298,7 +3298,7 @@ body.twm-window-maximized .twm-resize-handle {
3298
3298
  border-radius: 1px;
3299
3299
  }
3300
3300
  .twm-data-series-plot-config input[type="checkbox"] {
3301
- accent-color: #268bd2;
3301
+ accent-color: color-mix(in srgb, var(--accent-bright) 87%, white);
3302
3302
  flex-shrink: 0;
3303
3303
  }
3304
3304
  /* Computing Status Window */
@@ -3315,7 +3315,7 @@ body.twm-window-maximized .twm-resize-handle {
3315
3315
  width: 32px;
3316
3316
  height: 32px;
3317
3317
  border: 3px solid #333;
3318
- border-top-color: #4a90d9;
3318
+ border-top-color: color-mix(in srgb, var(--accent-bright) 80%, white);
3319
3319
  border-radius: 50%;
3320
3320
  animation: computing-spin 0.8s linear infinite;
3321
3321
  }
@@ -3808,22 +3808,21 @@ body.twm-window-maximized .twm-resize-handle {
3808
3808
  }
3809
3809
 
3810
3810
  /* Selected — set by `_onConfigClick()` while the config dialog is open, and
3811
- cleared from every sibling by the same handler. #78b9ff is a literal:
3812
- `--accent-bright` is #007acc, which is a border colour on this background
3813
- and not a text one, and the derived light accents (`--accent-light`,
3814
- `--accent-lighter`) are less blue than this, so using them would change
3815
- the colour. */
3811
+ cleared from every sibling by the same handler. It reads `--accent-lighter`
3812
+ (BUG-0045): `--accent-bright` is a border colour on this background and not
3813
+ a text one. It was #78b9ff, which no mix of `--accent-bright` with white
3814
+ gives exactly; `--accent-lighter` (about #73b6e3) is the nearest. */
3816
3815
  .tile.tile--selected {
3817
- border-color: #78b9ff;
3818
- box-shadow: 0 0 0 2px rgba(120, 185, 255, 0.3), var(--shadow-md);
3816
+ border-color: var(--accent-lighter);
3817
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-lighter) 30%, transparent), var(--shadow-md);
3819
3818
  }
3820
3819
 
3821
3820
  .tile.tile--selected .tile-header {
3822
- background: rgba(120, 185, 255, 0.1);
3821
+ background: color-mix(in srgb, var(--accent-lighter) 10%, transparent);
3823
3822
  }
3824
3823
 
3825
3824
  .tile.tile--selected .tile-title {
3826
- color: #78b9ff;
3825
+ color: var(--accent-lighter);
3827
3826
  }
3828
3827
 
3829
3828
  /* Read-only. `_buildChrome()`'s read-only branch emits no drag handle, no
@@ -3926,7 +3925,7 @@ body.twm-window-maximized .twm-resize-handle {
3926
3925
  .tile-menu-btn:hover,
3927
3926
  .tile-expand-btn:hover {
3928
3927
  background: var(--state-hover);
3929
- color: #78b9ff;
3928
+ color: var(--accent-lighter);
3930
3929
  }
3931
3930
 
3932
3931
  .tile-remove-btn:hover {
@@ -4014,7 +4013,7 @@ body.twm-window-maximized .twm-resize-handle {
4014
4013
  }
4015
4014
 
4016
4015
  .tile-menu-item:hover .material-symbols-outlined {
4017
- color: #78b9ff;
4016
+ color: var(--accent-lighter);
4018
4017
  }
4019
4018
 
4020
4019
  /* ── the content area ────────────────────────────────────────────────── */
@@ -4528,12 +4527,12 @@ body.twm-window-maximized .twm-resize-handle {
4528
4527
  }
4529
4528
  .twm-action-dropdown-option .twm-option-icon {
4530
4529
  font-size: 20px;
4531
- color: #78b9ff;
4530
+ color: var(--accent-lighter);
4532
4531
  flex-shrink: 0;
4533
4532
  }
4534
4533
  .twm-action-dropdown-option:hover .twm-option-icon,
4535
4534
  .twm-action-dropdown-option:focus .twm-option-icon {
4536
- color: #9ecfff;
4535
+ color: var(--accent-lightest);
4537
4536
  }
4538
4537
  .twm-action-dropdown-option .twm-option-label {
4539
4538
  font-weight: 500;
@@ -4692,7 +4691,7 @@ body.twm-window-maximized .twm-resize-handle {
4692
4691
  position: absolute;
4693
4692
  z-index: 5999;
4694
4693
  pointer-events: none;
4695
- background: rgba(23, 119, 179, 0.18);
4694
+ background: color-mix(in srgb, var(--accent-hover) 18%, transparent);
4696
4695
  border: 1px solid var(--accent-bright);
4697
4696
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
4698
4697
  transition: left 0.09s ease, top 0.09s ease, width 0.09s ease, height 0.09s ease;
@@ -4986,12 +4985,11 @@ button.twm-managed-window__btn svg rect {
4986
4985
  transition: background-color 0.1s, color 0.1s;
4987
4986
  }
4988
4987
  .twm-bar-windows__item:hover {
4989
- /* Solarized blue */
4990
- background: #268bd2;
4988
+ background: var(--accent-bright-hover);
4991
4989
  color: var(--text-bright);
4992
4990
  }
4993
4991
  .twm-bar-windows__item:active {
4994
- background: #1a6aa5;
4992
+ background: var(--accent-pressed);
4995
4993
  }
4996
4994
  /* Compounded for the Material Symbols tie — see `.twm-leaf__icon`. */
4997
4995
  .twm-bar-windows__item-icon,
@@ -5597,7 +5595,7 @@ button.twm-about-dialog__btn--close:focus {
5597
5595
  font-size: var(--font-size-sm);
5598
5596
  }
5599
5597
  .twm-autocomplete-field--notebook .twm-autocomplete-field__input-wrapper:focus-within {
5600
- border-color: #79b8f8;
5598
+ border-color: var(--accent-lighter);
5601
5599
  box-shadow: 0 0 3px color-mix(in srgb, var(--accent-light) 30%, transparent);
5602
5600
  }
5603
5601
  .twm-autocomplete-field--notebook .twm-autocomplete-field__input {
@@ -5915,8 +5913,8 @@ button.twm-about-dialog__btn--close:focus {
5915
5913
  color: var(--text-bright);
5916
5914
  }
5917
5915
  .twm-btn--primary:hover:not(:disabled) {
5918
- background: #1486cf;
5919
- border-color: #1486cf;
5916
+ background: var(--accent-bright-hover);
5917
+ border-color: var(--accent-bright-hover);
5920
5918
  }
5921
5919
  /* Context menu separator (matches Ecosim's .twm-context-menu look). */
5922
5920
  .twm-context-menu__separator {
@@ -6438,8 +6436,8 @@ button.twm-about-dialog__btn--close:focus {
6438
6436
  font-weight: 600;
6439
6437
  }
6440
6438
  .twm-managed-window--modal .twm-modal__actions .twm-btn--primary:hover:not(:disabled) {
6441
- background: #1683c4;
6442
- border-color: #1683c4;
6439
+ background: var(--accent-bright-hover);
6440
+ border-color: var(--accent-bright-hover);
6443
6441
  }
6444
6442
  .twm-managed-window--modal .twm-modal__actions .twm-btn--danger {
6445
6443
  background: #8b2a2a;
@@ -6705,7 +6703,7 @@ button.twm-about-dialog__btn--close:focus {
6705
6703
  font-size: 10px;
6706
6704
  }
6707
6705
  .twm-landing__pane--focused .twm-landing__pane-head {
6708
- background: rgba(17, 119, 179, 0.10);
6706
+ background: color-mix(in srgb, var(--accent-hover) 10%, transparent);
6709
6707
  }
6710
6708
  .twm-landing__pane--focused .twm-landing__pane-title {
6711
6709
  color: var(--ea-accent);
@@ -6757,7 +6755,7 @@ button.twm-about-dialog__btn--close:focus {
6757
6755
  so the same arrow-key affordance reads identically across modes. */
6758
6756
  .twm-row-cursor {
6759
6757
  box-shadow: inset 2px 0 0 var(--ea-accent);
6760
- background: rgba(17, 119, 179, 0.12) !important;
6758
+ background: color-mix(in srgb, var(--accent-hover) 12%, transparent) !important;
6761
6759
  }
6762
6760
  /* Quick-action strip — horizontal bar below the tables. Same chrome
6763
6761
  tone as `.twm-entity-header` so the page reads as a single stack:
@@ -6797,7 +6795,7 @@ button.twm-about-dialog__btn--close:focus {
6797
6795
  }
6798
6796
  .twm-action-btn:hover {
6799
6797
  border-color: var(--ea-accent);
6800
- background: rgba(17, 119, 179, 0.08);
6798
+ background: color-mix(in srgb, var(--accent-hover) 8%, transparent);
6801
6799
  }
6802
6800
  .twm-action-btn:disabled,
6803
6801
  .twm-action-btn[aria-disabled="true"] {
@@ -13606,9 +13604,9 @@ button.twm-alerts__ref:hover { text-decoration: underline; }
13606
13604
  display: none !important;
13607
13605
  }
13608
13606
  .twm-flow-lanes {
13609
- --twm-lanes-wire: #5b9bd5;
13607
+ --twm-lanes-wire: var(--accent-light);
13610
13608
  --twm-lanes-surface: #1b1b1c;
13611
- --twm-lanes-ring: #1683c4;
13609
+ --twm-lanes-ring: var(--accent-bright-hover);
13612
13610
  display: flex;
13613
13611
  flex-direction: column;
13614
13612
  box-sizing: border-box;
@@ -13731,7 +13729,7 @@ button.twm-alerts__ref:hover { text-decoration: underline; }
13731
13729
  border-left: 1px solid var(--surface-3);
13732
13730
  }
13733
13731
  .twm-flow-lanes__show-btn[aria-pressed="true"] {
13734
- background: #0f6fa8;
13732
+ background: var(--accent-pressed);
13735
13733
  color: var(--text-bright);
13736
13734
  }
13737
13735
  .twm-flow-lanes__show-btn:focus-visible,
@@ -13974,7 +13972,7 @@ button.twm-alerts__ref:hover { text-decoration: underline; }
13974
13972
  .twm-flow-lanes__node--selected .twm-flow-lanes__card,
13975
13973
  .twm-flow-lanes__card:focus-visible {
13976
13974
  filter: drop-shadow(0 0 1px var(--twm-lanes-ring)) drop-shadow(0 0 1px var(--twm-lanes-ring))
13977
- drop-shadow(0 0 5px rgba(22, 131, 196, 0.8));
13975
+ drop-shadow(0 0 5px color-mix(in srgb, var(--twm-lanes-ring) 80%, transparent));
13978
13976
  }
13979
13977
  .twm-flow-lanes__card:hover .twm-flow-lanes__body {
13980
13978
  filter: brightness(1.12);
@@ -14297,7 +14295,7 @@ button.twm-alerts__ref:hover { text-decoration: underline; }
14297
14295
  }
14298
14296
  .twm-flow-lanes__cell--new,
14299
14297
  .twm-flow-lanes__cell--changed {
14300
- background: rgba(22, 131, 196, 0.07);
14298
+ background: color-mix(in srgb, var(--twm-lanes-ring) 7%, transparent);
14301
14299
  }
14302
14300
  .twm-flow-lanes__error {
14303
14301
  flex: 0 0 auto;
@@ -14331,7 +14329,7 @@ button.twm-alerts__ref:hover { text-decoration: underline; }
14331
14329
  }
14332
14330
  .twm-flow-lanes__column--new,
14333
14331
  .twm-flow-lanes__column--changed {
14334
- background: rgba(22, 131, 196, 0.14);
14332
+ background: color-mix(in srgb, var(--twm-lanes-ring) 14%, transparent);
14335
14333
  }
14336
14334
  .twm-flow-lanes__column-name {
14337
14335
  font-family: var(--font-family-mono);
@@ -14344,7 +14342,7 @@ button.twm-alerts__ref:hover { text-decoration: underline; }
14344
14342
  }
14345
14343
  .twm-flow-lanes__column--new .twm-flow-lanes__column-type,
14346
14344
  .twm-flow-lanes__column--changed .twm-flow-lanes__column-type {
14347
- color: #9fd0f0;
14345
+ color: var(--accent-lightest);
14348
14346
  }
14349
14347
  .twm-flow-lanes__column-origin {
14350
14348
  margin-left: auto;
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
package/dist/tokens.css CHANGED
@@ -96,6 +96,11 @@
96
96
  sortable list's drop line and add button, a focused filter input. */
97
97
  --accent-light: color-mix(in srgb, var(--accent-bright) 70%, white); /* ≈ #4da2db, was #569cd6 */
98
98
  --accent-lighter: color-mix(in srgb, var(--accent-bright) 55%, white); /* ≈ #73b6e3, was #6eb3e9 */
99
+ /* BUG-0045: the other accent shades. A primary button's hover, the window
100
+ bar's hovered item; a pressed accent control; the lightest accent text. */
101
+ --accent-bright-hover: color-mix(in srgb, var(--accent-bright) 90%, white); /* ≈ #1987d1, was #1486cf / #1683c4 */
102
+ --accent-pressed: color-mix(in srgb, var(--accent-hover) 90%, black); /* ≈ #0f6ba8, was #1a6aa5 / #0f6fa8 */
103
+ --accent-lightest: color-mix(in srgb, var(--accent-bright) 36%, white); /* ≈ #a3cfed, was #9ecfff / #9fd0f0 */
99
104
 
100
105
  /* ── Semantic colors ───────────────────────────────────────── */
101
106
  --color-success: #3fb950;
@@ -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()`
@@ -105,11 +105,77 @@ What changed in the stylesheet:
105
105
  `flexdesk.css` now needs `tokens.css` (or your own definitions of these tokens).
106
106
 
107
107
  Not part of the accent, and still literals: the autocomplete's type colours
108
- (`stock` is `#569cd6` beside `#4ec9b0`, `#ce9178`, …), the notification
109
- border `#4fc1ff`, and links (`#3794ff`).
110
-
111
- Some accent blues in other shades are still literals, because no mix of an
112
- accent token gives their colour exactly. They include the selected tile
108
+ (`stock` is `#569cd6` beside `#4ec9b0`, `#ce9178`, …), the DataTable header
109
+ icons' type colours (`flow` is `#268bd2` beside `#6c71c4`, `#cb4b16`,
110
+ `#859900`), the notification border `#4fc1ff`, links (`#3794ff`, the about
111
+ dialog's link hover `#4da6ff`, and the breadcrumb and plot-series links), and the flow kit's categorical colours
112
+ (`--twm-flow-blue-*` beside `--twm-flow-indigo-*`, …). The (0.17.11) section
113
+ lists the rest.
114
+
115
+ 0.17.10 left the accent blues in other shades as literals: the selected tile
113
116
  (`#78b9ff`), the primary button's hover (`#1683c4`, `#1486cf`), the row
114
117
  cursor and snap preview (`rgba(17, 119, 179, a)`), and some flow-lanes and
115
- plot-window colours. To retheme them, override their rules.
118
+ plot-window colours. 0.17.11 derives them too (next section).
119
+
120
+ ## Retheming the accent shades (0.17.11)
121
+
122
+ The accent blues that 0.17.10 left as literals, in shades no mix of an
123
+ accent token gives exactly, are now derived from the accent tokens too, so
124
+ setting `--accent`, `--accent-hover` and `--accent-bright` on `:root`
125
+ rethemes the primary buttons' hover, the selected tile, the row cursor, the
126
+ window bar, the plot window, the spinner and the flow lanes as well.
127
+ `--accent` and `--accent-bright` stay two tokens.
128
+
129
+ Three tokens are new in `tokens.css`, each read by more than one rule:
130
+
131
+ - `--accent-bright-hover`: `color-mix(in srgb, var(--accent-bright) 90%, white)`,
132
+ about `#1987d1`. A primary button's hover (the modal's too) and the window
133
+ bar's hovered item.
134
+ - `--accent-pressed`: `color-mix(in srgb, var(--accent-hover) 90%, black)`,
135
+ about `#0f6ba8`. The window bar's pressed item and the flow lanes' pressed
136
+ show button.
137
+ - `--accent-lightest`: `color-mix(in srgb, var(--accent-bright) 36%, white)`,
138
+ about `#a3cfed`. The lightest accent text, after `--accent-light` and
139
+ `--accent-lighter`.
140
+
141
+ They are defined from the accent tokens in `tokens.css`. If you supply your
142
+ own token definitions instead of `tokens.css`, define these three as well:
143
+ the rules that read them have no fallback.
144
+
145
+ None of the old colours is an exact mix, so each moves to the nearest one,
146
+ by the ΔE (OKLab × 100) below. A tint's ΔE is measured over `--surface-0`
147
+ (`#1e1e1e`), where it is seen, and every colour is rounded to 8 bits per
148
+ channel first.
149
+
150
+ | Where | Was | Now reads | Default now | ΔE |
151
+ |---|---|---|---|---|
152
+ | `.twm-btn--primary:hover` | `#1486cf` | `--accent-bright-hover` | `#1987d1` | 0.4 |
153
+ | the modal's primary button hover | `#1683c4` | `--accent-bright-hover` | `#1987d1` | 2.2 |
154
+ | `.twm-bar-windows__item:hover` | `#268bd2` | `--accent-bright-hover` | `#1987d1` | 1.3 |
155
+ | `.twm-bar-windows__item:active` | `#1a6aa5` | `--accent-pressed` | `#0f6ba8` | 0.6 |
156
+ | the plot series and plot config checkboxes (`accent-color`), the plot config select's focus border | `#268bd2` | `color-mix(in srgb, var(--accent-bright) 87%, white)` | `#218bd3` | 0.3 |
157
+ | the plot window toolbar's active button, the computing spinner | `#4a90d9` | `color-mix(in srgb, var(--accent-bright) 80%, white)` | `#3395d6` | 2.1 |
158
+ | the active button's tint and hover tint, the plot table's hovered row | `rgba(74, 144, 217, 0.15 \| 0.25 \| 0.1)` | that 80% mix at the same alpha | | 0.6, 0.9, 0.5 |
159
+ | the selected tile's border and title, the tile header buttons' and tile menu's hover icons, the action dropdown's icons | `#78b9ff` | `--accent-lighter` | `#73b6e3` | 4.1 |
160
+ | the selected tile's ring and header tint | `rgba(120, 185, 255, 0.3 \| 0.1)` | `--accent-lighter` at the same alpha | | 1.6, 0.6 |
161
+ | the action dropdown's hovered or focused icon | `#9ecfff` | `--accent-lightest` | `#a3cfed` | 2.6 |
162
+ | the notebook autocomplete's focused border | `#79b8f8` | `--accent-lighter` | `#73b6e3` | 3.2 |
163
+ | `.twm-row-cursor`, the focused landing pane's head, `.twm-action-btn:hover` | `rgba(17, 119, 179, 0.12 \| 0.10 \| 0.08)` | `--accent-hover` at the same alpha | | 0.2, 0.2, 0.2 |
164
+ | `.twm-snap-preview` | `rgba(23, 119, 179, 0.18)` | `--accent-hover` at 18% | | 0.2 |
165
+ | the flow lanes' wire, `--twm-lanes-wire` | `#5b9bd5` | `--accent-light` | `#4da2db` | 2.0 |
166
+ | the flow lanes' ring, `--twm-lanes-ring` | `#1683c4` | `--accent-bright-hover` | `#1987d1` | 2.2 |
167
+ | the flow lanes' pressed show button | `#0f6fa8` | `--accent-pressed` | `#0f6ba8` | 1.2 |
168
+ | the selected card's glow, a new or changed cell and column | `rgba(22, 131, 196, 0.8 \| 0.07 \| 0.14)` | `--twm-lanes-ring` at the same alpha | | 1.7, 0.3, 0.4 |
169
+ | a new or changed column's type | `#9fd0f0` | `--accent-lightest` | `#a3cfed` | 0.6 |
170
+
171
+ The largest shift is the selected tile's light blue: `#78b9ff` is bluer
172
+ than any mix of `#007acc` with white, and `--accent-lighter` is the nearest.
173
+ `--twm-lanes-wire` and `--twm-lanes-ring` are set on `.twm-flow-lanes` and
174
+ read `--accent-light` and `--accent-bright-hover`, so they follow the accent
175
+ set on `:root` and can still be set per lanes view.
176
+
177
+ Still literals, because they are not the accent: the colours listed in the
178
+ (0.17.10) section above, the charts' series palette (`#268bd2` first in
179
+ `COLOR_PALETTE`, `#1f77b4` as the colour pickers' default), and the Monaco
180
+ editor theme's selection colours, which Monaco takes as hex values and not
181
+ as CSS.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flexdesk",
3
- "version": "0.17.10",
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) {