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/CHANGELOG.md +37 -0
- package/css/base.css +31 -33
- package/css/flexdesk.css +38 -40
- package/css/overrides.css +7 -7
- package/css/tokens.css +5 -0
- package/dist/charts.js +1 -1
- package/dist/{chunk-2YTM3GJE.js → chunk-75FLVAUT.js} +14 -3
- package/dist/{chunk-2YTM3GJE.js.map → chunk-75FLVAUT.js.map} +2 -2
- package/dist/flexdesk.css +38 -40
- package/dist/tiles.js +1 -1
- package/dist/tokens.css +5 -0
- package/docs/components/charts.md +37 -1
- package/docs/concepts/styling.md +72 -6
- package/package.json +1 -1
- package/src/charts/plot_grid.js +21 -3
- package/src/charts/plot_popout_window.js +7 -0
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:
|
|
3003
|
-
background:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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.
|
|
3812
|
-
`--accent-bright` is
|
|
3813
|
-
|
|
3814
|
-
`--accent-lighter`
|
|
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:
|
|
3818
|
-
box-shadow: 0 0 0 2px
|
|
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:
|
|
3821
|
+
background: color-mix(in srgb, var(--accent-lighter) 10%, transparent);
|
|
3823
3822
|
}
|
|
3824
3823
|
|
|
3825
3824
|
.tile.tile--selected .tile-title {
|
|
3826
|
-
color:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
5919
|
-
border-color:
|
|
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:
|
|
6442
|
-
border-color:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
13607
|
+
--twm-lanes-wire: var(--accent-light);
|
|
13610
13608
|
--twm-lanes-surface: #1b1b1c;
|
|
13611
|
-
--twm-lanes-ring:
|
|
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:
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
14345
|
+
color: var(--accent-lightest);
|
|
14348
14346
|
}
|
|
14349
14347
|
.twm-flow-lanes__column-origin {
|
|
14350
14348
|
margin-left: auto;
|
package/dist/tiles.js
CHANGED
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
|
|
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/concepts/styling.md
CHANGED
|
@@ -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
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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.
|
|
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
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) {
|