flexdesk 0.17.14 → 0.17.16
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 +39 -0
- package/css/base.css +237 -8
- package/css/flexdesk.css +344 -17
- package/css/overrides.css +6 -0
- package/dist/charts.js +104 -34
- package/dist/charts.js.map +3 -3
- package/dist/{chunk-2PM6ZMNS.js → chunk-2Z2ENHR3.js} +2 -2
- package/dist/{chunk-IG74S3ZH.js → chunk-7JI7HHSY.js} +2 -2
- package/dist/{chunk-TLIV6QSU.js → chunk-7OL6F2AM.js} +3 -3
- package/dist/{chunk-MADMGRX2.js → chunk-PGZ4EHSE.js} +156 -9
- package/dist/chunk-PGZ4EHSE.js.map +7 -0
- package/dist/{chunk-ZMH3OC7H.js → chunk-XJNADRGR.js} +4 -2
- package/dist/chunk-XJNADRGR.js.map +7 -0
- package/dist/flexdesk.css +344 -17
- package/dist/flow.js +3 -3
- package/dist/tiles.js +84 -9
- package/dist/tiles.js.map +2 -2
- package/dist/widgets.js +448 -5
- package/dist/widgets.js.map +4 -4
- package/dist/wm.js +110 -19
- package/dist/wm.js.map +2 -2
- package/docs/README.md +1 -1
- package/docs/components/charts.md +70 -3
- package/docs/components/forms-and-dialogs.md +80 -2
- package/docs/components/menus.md +85 -1
- package/docs/components/shell.md +52 -0
- package/docs/components/slide-out-panel.md +15 -0
- package/docs/components/tiles.md +37 -0
- package/package.json +1 -1
- package/src/charts/plot_config_editor.js +125 -29
- package/src/tiles/tile_base.js +48 -2
- package/src/tiles/tile_grid.js +42 -1
- package/src/tiling/kind_taxonomy.js +45 -0
- package/src/tiling/tile_breadcrumb.js +12 -4
- package/src/tiling/wm.js +86 -11
- package/src/ui/components/category_picker.js +556 -0
- package/src/ui/components/computing_status_window.js +3 -0
- package/src/ui/components/confirm_dialog.js +3 -0
- package/src/ui/components/managed_window.js +204 -9
- package/src/ui/components/modal.js +11 -2
- package/src/ui/components/wizard.js +4 -1
- package/widgets.js +2 -1
- package/dist/chunk-MADMGRX2.js.map +0 -7
- package/dist/chunk-ZMH3OC7H.js.map +0 -7
- /package/dist/{chunk-2PM6ZMNS.js.map → chunk-2Z2ENHR3.js.map} +0 -0
- /package/dist/{chunk-IG74S3ZH.js.map → chunk-7JI7HHSY.js.map} +0 -0
- /package/dist/{chunk-TLIV6QSU.js.map → chunk-7OL6F2AM.js.map} +0 -0
package/css/flexdesk.css
CHANGED
|
@@ -3869,20 +3869,25 @@ body.twm-window-maximized .twm-resize-handle {
|
|
|
3869
3869
|
}
|
|
3870
3870
|
|
|
3871
3871
|
/* Selected — set by `_onConfigClick()` while the config dialog is open, and
|
|
3872
|
-
cleared from every sibling by the same handler.
|
|
3872
|
+
cleared from every sibling by the same handler. `--configuring` is the same
|
|
3873
|
+
look set by the embedder through `setConfiguring()` (0.17.16), one per open
|
|
3874
|
+
config window, and no handler clears it. It reads `--accent-lighter`
|
|
3873
3875
|
(BUG-0045): `--accent-bright` is a border colour on this background and not
|
|
3874
3876
|
a text one. It was #78b9ff, which no mix of `--accent-bright` with white
|
|
3875
3877
|
gives exactly; `--accent-lighter` (about #73b6e3) is the nearest. */
|
|
3876
|
-
.tile.tile--selected
|
|
3878
|
+
.tile.tile--selected,
|
|
3879
|
+
.tile.tile--configuring {
|
|
3877
3880
|
border-color: var(--accent-lighter);
|
|
3878
3881
|
box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-lighter) 30%, transparent), var(--shadow-md);
|
|
3879
3882
|
}
|
|
3880
3883
|
|
|
3881
|
-
.tile.tile--selected .tile-header
|
|
3884
|
+
.tile.tile--selected .tile-header,
|
|
3885
|
+
.tile.tile--configuring .tile-header {
|
|
3882
3886
|
background: color-mix(in srgb, var(--accent-lighter) 10%, transparent);
|
|
3883
3887
|
}
|
|
3884
3888
|
|
|
3885
|
-
.tile.tile--selected .tile-title
|
|
3889
|
+
.tile.tile--selected .tile-title,
|
|
3890
|
+
.tile.tile--configuring .tile-title {
|
|
3886
3891
|
color: var(--accent-lighter);
|
|
3887
3892
|
}
|
|
3888
3893
|
|
|
@@ -4609,6 +4614,215 @@ body.twm-window-maximized .twm-resize-handle {
|
|
|
4609
4614
|
text-overflow: ellipsis;
|
|
4610
4615
|
}
|
|
4611
4616
|
|
|
4617
|
+
/* ══ CATEGORY PICKER (STORY-0126) ══════════════════════════════════════
|
|
4618
|
+
`CategoryPicker` (category_picker.js): a search field over three columns —
|
|
4619
|
+
categories, the active category's items, the highlighted item's detail.
|
|
4620
|
+
Placed by `ActionDropdown.position` (inline top/left/max-height), so it is
|
|
4621
|
+
`position: fixed` on --z-over-windows: over the managed window it opened
|
|
4622
|
+
from. It shrinks to the inline max-height and its columns scroll inside.
|
|
4623
|
+
A category's colour arrives as `--twm-category-picker-color` on its rows. */
|
|
4624
|
+
.twm-category-picker {
|
|
4625
|
+
position: fixed;
|
|
4626
|
+
z-index: var(--z-over-windows);
|
|
4627
|
+
display: flex;
|
|
4628
|
+
flex-direction: column;
|
|
4629
|
+
box-sizing: border-box;
|
|
4630
|
+
width: 640px;
|
|
4631
|
+
max-width: calc(100vw - 2 * var(--space-md));
|
|
4632
|
+
height: 440px;
|
|
4633
|
+
background: var(--surface-1);
|
|
4634
|
+
border: 1px solid var(--border-strong);
|
|
4635
|
+
border-radius: var(--radius-lg);
|
|
4636
|
+
box-shadow: var(--shadow-lg);
|
|
4637
|
+
color: var(--text-primary);
|
|
4638
|
+
font-family: inherit;
|
|
4639
|
+
font-size: var(--font-size-md);
|
|
4640
|
+
overflow: hidden;
|
|
4641
|
+
}
|
|
4642
|
+
.twm-category-picker[hidden] {
|
|
4643
|
+
display: none;
|
|
4644
|
+
}
|
|
4645
|
+
.twm-category-picker__search {
|
|
4646
|
+
flex: none;
|
|
4647
|
+
padding: var(--space-md);
|
|
4648
|
+
border-bottom: 1px solid var(--border-default);
|
|
4649
|
+
}
|
|
4650
|
+
.twm-category-picker__input {
|
|
4651
|
+
width: 100%;
|
|
4652
|
+
box-sizing: border-box;
|
|
4653
|
+
padding: var(--space-sm) var(--space-md);
|
|
4654
|
+
background: var(--surface-0);
|
|
4655
|
+
border: 1px solid var(--border-strong);
|
|
4656
|
+
border-radius: var(--radius-sm);
|
|
4657
|
+
color: var(--text-primary);
|
|
4658
|
+
font-family: inherit;
|
|
4659
|
+
font-size: var(--font-size-md);
|
|
4660
|
+
}
|
|
4661
|
+
.twm-category-picker__input:focus {
|
|
4662
|
+
outline: none;
|
|
4663
|
+
border-color: var(--accent-bright);
|
|
4664
|
+
}
|
|
4665
|
+
.twm-category-picker__body {
|
|
4666
|
+
flex: 1 1 auto;
|
|
4667
|
+
min-height: 0;
|
|
4668
|
+
display: flex;
|
|
4669
|
+
}
|
|
4670
|
+
.twm-category-picker__column {
|
|
4671
|
+
flex: none;
|
|
4672
|
+
box-sizing: border-box;
|
|
4673
|
+
padding: var(--space-sm) 0;
|
|
4674
|
+
border-right: 1px solid var(--border-default);
|
|
4675
|
+
overflow-y: auto;
|
|
4676
|
+
}
|
|
4677
|
+
.twm-category-picker__categories {
|
|
4678
|
+
width: 170px;
|
|
4679
|
+
}
|
|
4680
|
+
.twm-category-picker__items {
|
|
4681
|
+
width: 220px;
|
|
4682
|
+
}
|
|
4683
|
+
.twm-category-picker__category,
|
|
4684
|
+
.twm-category-picker__item {
|
|
4685
|
+
display: flex;
|
|
4686
|
+
align-items: center;
|
|
4687
|
+
gap: var(--space-md);
|
|
4688
|
+
width: 100%;
|
|
4689
|
+
box-sizing: border-box;
|
|
4690
|
+
padding: var(--space-sm) var(--space-lg);
|
|
4691
|
+
background: transparent;
|
|
4692
|
+
border: none;
|
|
4693
|
+
color: var(--text-primary);
|
|
4694
|
+
font-family: inherit;
|
|
4695
|
+
font-size: var(--font-size-md);
|
|
4696
|
+
text-align: left;
|
|
4697
|
+
cursor: pointer;
|
|
4698
|
+
}
|
|
4699
|
+
.twm-category-picker__category:hover,
|
|
4700
|
+
.twm-category-picker__item:hover {
|
|
4701
|
+
background: var(--state-hover);
|
|
4702
|
+
}
|
|
4703
|
+
.twm-category-picker__category--active,
|
|
4704
|
+
.twm-category-picker__item--highlighted,
|
|
4705
|
+
.twm-category-picker__category--active:hover,
|
|
4706
|
+
.twm-category-picker__item--highlighted:hover {
|
|
4707
|
+
background: var(--state-selected);
|
|
4708
|
+
}
|
|
4709
|
+
/* The column the arrow keys move in marks its row with an accent edge. */
|
|
4710
|
+
.twm-category-picker__column--current .twm-category-picker__category--active,
|
|
4711
|
+
.twm-category-picker__column--current .twm-category-picker__item--highlighted {
|
|
4712
|
+
box-shadow: inset 2px 0 0 var(--accent-bright);
|
|
4713
|
+
}
|
|
4714
|
+
.twm-category-picker__category--empty {
|
|
4715
|
+
color: var(--text-dim);
|
|
4716
|
+
}
|
|
4717
|
+
.twm-category-picker__dot {
|
|
4718
|
+
flex: none;
|
|
4719
|
+
width: 8px;
|
|
4720
|
+
height: 8px;
|
|
4721
|
+
border-radius: var(--radius-circle);
|
|
4722
|
+
/* background-color, not the shorthand: the colour is the caller's, and a
|
|
4723
|
+
url() in the shorthand would be fetched */
|
|
4724
|
+
background-color: var(--twm-category-picker-color, var(--text-muted));
|
|
4725
|
+
}
|
|
4726
|
+
.twm-category-picker__category-label,
|
|
4727
|
+
.twm-category-picker__item-label {
|
|
4728
|
+
flex: 1 1 auto;
|
|
4729
|
+
min-width: 0;
|
|
4730
|
+
overflow: hidden;
|
|
4731
|
+
white-space: nowrap;
|
|
4732
|
+
text-overflow: ellipsis;
|
|
4733
|
+
}
|
|
4734
|
+
.twm-category-picker__count {
|
|
4735
|
+
flex: none;
|
|
4736
|
+
color: var(--text-muted);
|
|
4737
|
+
font-size: var(--font-size-xs);
|
|
4738
|
+
font-variant-numeric: tabular-nums;
|
|
4739
|
+
}
|
|
4740
|
+
.twm-category-picker__badge,
|
|
4741
|
+
.twm-category-picker__item .twm-category-picker__icon {
|
|
4742
|
+
flex: none;
|
|
4743
|
+
display: flex;
|
|
4744
|
+
align-items: center;
|
|
4745
|
+
justify-content: center;
|
|
4746
|
+
width: 22px;
|
|
4747
|
+
height: 22px;
|
|
4748
|
+
box-sizing: border-box;
|
|
4749
|
+
color: var(--twm-category-picker-color, var(--text-muted));
|
|
4750
|
+
}
|
|
4751
|
+
.twm-category-picker__badge {
|
|
4752
|
+
border: 1px solid var(--twm-category-picker-color, var(--text-muted));
|
|
4753
|
+
border-radius: var(--radius-sm);
|
|
4754
|
+
font-family: var(--font-family-mono);
|
|
4755
|
+
font-size: var(--font-size-xs);
|
|
4756
|
+
font-weight: var(--font-weight-semibold);
|
|
4757
|
+
overflow: hidden;
|
|
4758
|
+
white-space: nowrap;
|
|
4759
|
+
}
|
|
4760
|
+
.twm-category-picker__item .twm-category-picker__icon {
|
|
4761
|
+
font-size: 18px;
|
|
4762
|
+
}
|
|
4763
|
+
/* One category's hits while a query is set: role="group", named by its header. */
|
|
4764
|
+
.twm-category-picker__hits {
|
|
4765
|
+
display: flex;
|
|
4766
|
+
flex-direction: column;
|
|
4767
|
+
}
|
|
4768
|
+
.twm-category-picker__group {
|
|
4769
|
+
display: flex;
|
|
4770
|
+
align-items: center;
|
|
4771
|
+
gap: var(--space-sm);
|
|
4772
|
+
padding: var(--space-md) var(--space-lg) var(--space-xs);
|
|
4773
|
+
color: var(--text-muted);
|
|
4774
|
+
font-size: var(--font-size-xs);
|
|
4775
|
+
text-transform: uppercase;
|
|
4776
|
+
letter-spacing: 0.06em;
|
|
4777
|
+
}
|
|
4778
|
+
.twm-category-picker__empty,
|
|
4779
|
+
.twm-category-picker__detail-empty {
|
|
4780
|
+
padding: var(--space-md) var(--space-lg);
|
|
4781
|
+
color: var(--text-muted);
|
|
4782
|
+
}
|
|
4783
|
+
.twm-category-picker__detail {
|
|
4784
|
+
flex: 1 1 auto;
|
|
4785
|
+
min-width: 0;
|
|
4786
|
+
display: flex;
|
|
4787
|
+
flex-direction: column;
|
|
4788
|
+
gap: var(--space-md);
|
|
4789
|
+
padding: var(--space-xl);
|
|
4790
|
+
overflow-y: auto;
|
|
4791
|
+
}
|
|
4792
|
+
.twm-category-picker__title {
|
|
4793
|
+
color: var(--text-bright);
|
|
4794
|
+
font-size: var(--font-size-xl);
|
|
4795
|
+
font-weight: var(--font-weight-semibold);
|
|
4796
|
+
}
|
|
4797
|
+
.twm-category-picker__description {
|
|
4798
|
+
color: var(--text-secondary);
|
|
4799
|
+
line-height: var(--line-height-normal);
|
|
4800
|
+
}
|
|
4801
|
+
.twm-category-picker__preview-label {
|
|
4802
|
+
margin-top: var(--space-sm);
|
|
4803
|
+
color: var(--text-muted);
|
|
4804
|
+
font-size: var(--font-size-xs);
|
|
4805
|
+
text-transform: uppercase;
|
|
4806
|
+
letter-spacing: 0.06em;
|
|
4807
|
+
}
|
|
4808
|
+
.twm-category-picker__preview {
|
|
4809
|
+
margin: 0;
|
|
4810
|
+
padding: var(--space-md);
|
|
4811
|
+
background: var(--surface-0);
|
|
4812
|
+
border: 1px solid var(--border-default);
|
|
4813
|
+
border-radius: var(--radius-sm);
|
|
4814
|
+
color: var(--text-body);
|
|
4815
|
+
font-family: var(--font-family-mono);
|
|
4816
|
+
font-size: var(--font-size-sm);
|
|
4817
|
+
white-space: pre-wrap;
|
|
4818
|
+
overflow-wrap: anywhere;
|
|
4819
|
+
}
|
|
4820
|
+
.twm-category-picker__add {
|
|
4821
|
+
margin-top: auto;
|
|
4822
|
+
align-self: flex-end;
|
|
4823
|
+
}
|
|
4824
|
+
/* ══ end CATEGORY PICKER ══ */
|
|
4825
|
+
|
|
4612
4826
|
/* ── from code.css ───────────────────────── */
|
|
4613
4827
|
/* Code language tabs */
|
|
4614
4828
|
.twm-code-tabs-header {
|
|
@@ -4825,6 +5039,12 @@ body.twm-window-maximized .twm-resize-handle {
|
|
|
4825
5039
|
.twm-managed-window:focus-within {
|
|
4826
5040
|
border-color: #505050;
|
|
4827
5041
|
}
|
|
5042
|
+
/* BUG-0053. The frame is focusable (tabindex -1) so a press on it keeps focus
|
|
5043
|
+
in the window; the focus-within border above already shows that, so the
|
|
5044
|
+
frame draws no outline of its own. */
|
|
5045
|
+
.twm-managed-window:focus {
|
|
5046
|
+
outline: none;
|
|
5047
|
+
}
|
|
4828
5048
|
/* Minimize animation class — targets taskbar item via CSS custom properties */
|
|
4829
5049
|
.twm-managed-window--minimizing {
|
|
4830
5050
|
transform: translate(var(--mw-target-x, 0px), var(--mw-target-y, 100vh)) scale(var(--mw-target-scale, 0.1));
|
|
@@ -5748,14 +5968,23 @@ button.twm-about-dialog__btn--close:focus {
|
|
|
5748
5968
|
min-width: min(400px, var(--twm-slide-out-host-w, 400px));
|
|
5749
5969
|
max-width: var(--twm-slide-out-host-w, none);
|
|
5750
5970
|
|
|
5751
|
-
|
|
5752
|
-
|
|
5971
|
+
/* Never drawn outside its final box (BUG-0051). While open the panel
|
|
5972
|
+
* is `position: fixed` at its host's right edge, where the host's
|
|
5973
|
+
* `overflow: hidden` no longer clips it, and its slide from
|
|
5974
|
+
* translateX(100%) was drawn across whatever lies right of the host (a
|
|
5975
|
+
* dock panel). It does not move any more: a clip alone carries the
|
|
5976
|
+
* motion, revealing it from its right edge leftwards, so no frame can
|
|
5977
|
+
* paint outside its box (a transform and a clip animated together can
|
|
5978
|
+
* drift apart, the transform often running on the compositor). The left
|
|
5979
|
+
* inset of -16px keeps the box shadow. */
|
|
5980
|
+
clip-path: inset(0 0 0 100%);
|
|
5981
|
+
transition: clip-path 200ms ease-out, visibility 0s linear 200ms;
|
|
5753
5982
|
visibility: hidden;
|
|
5754
5983
|
}
|
|
5755
5984
|
.twm-slide-out-panel--open {
|
|
5756
|
-
|
|
5985
|
+
clip-path: inset(0 0 0 -16px);
|
|
5757
5986
|
visibility: visible;
|
|
5758
|
-
transition:
|
|
5987
|
+
transition: clip-path 200ms ease-out, visibility 0s linear 0s;
|
|
5759
5988
|
}
|
|
5760
5989
|
/* Header bar */
|
|
5761
5990
|
.twm-slide-out-panel__header {
|
|
@@ -6438,8 +6667,18 @@ button.twm-about-dialog__btn--close:focus {
|
|
|
6438
6667
|
align-items: flex-start;
|
|
6439
6668
|
column-gap: 10px;
|
|
6440
6669
|
}
|
|
6670
|
+
/* Each field's basis is its label alone (the first field's: the form's 130px
|
|
6671
|
+
* label column): the controls lend the row no intrinsic width
|
|
6672
|
+
* (`contain: inline-size`), so the free space is shared equally and every
|
|
6673
|
+
* input of a row is equally wide (BUG-0050). Until 0.17.14 each field was an
|
|
6674
|
+
* equal share of the row (`flex: 1 1 0`), so the first input, whose share
|
|
6675
|
+
* also held the 130px column, was the narrowest by far. */
|
|
6441
6676
|
.twm-modal__field-group > .twm-modal__row {
|
|
6442
|
-
flex: 1 1
|
|
6677
|
+
flex: 1 1 auto;
|
|
6678
|
+
min-width: 0;
|
|
6679
|
+
}
|
|
6680
|
+
.twm-modal__field-group > .twm-modal__row > :not(:first-child) {
|
|
6681
|
+
contain: inline-size;
|
|
6443
6682
|
min-width: 0;
|
|
6444
6683
|
}
|
|
6445
6684
|
.twm-managed-window--modal .twm-modal__fields > .twm-modal__field-group {
|
|
@@ -9054,6 +9293,12 @@ tr:hover .twm-row-action { opacity: 1; }
|
|
|
9054
9293
|
padding: 2px 8px;
|
|
9055
9294
|
line-height: 22px;
|
|
9056
9295
|
}
|
|
9296
|
+
/* A path of one segment is not drawn (STORY-0124): the tile's title already
|
|
9297
|
+
* names it, so the strip and its slot take no room until a second level. */
|
|
9298
|
+
.twm-page-shell__breadcrumb .twm-tile-breadcrumb[hidden],
|
|
9299
|
+
.twm-page-shell__breadcrumb:has(> .twm-tile-breadcrumb[hidden]) {
|
|
9300
|
+
display: none;
|
|
9301
|
+
}
|
|
9057
9302
|
.twm-page-shell__content {
|
|
9058
9303
|
flex: 1 1 auto;
|
|
9059
9304
|
min-height: 0;
|
|
@@ -11059,6 +11304,11 @@ button.twm-alerts__ref:hover { text-decoration: underline; }
|
|
|
11059
11304
|
flex: 1 1 140px;
|
|
11060
11305
|
min-width: 0;
|
|
11061
11306
|
}
|
|
11307
|
+
/* A picker has no helper text: its empty helper takes no room (it held a
|
|
11308
|
+
* 28px gap under every picker, and the series header line off its centre). */
|
|
11309
|
+
.twm-plot-config__picker > .twm-autocomplete-field__helper:empty {
|
|
11310
|
+
display: none;
|
|
11311
|
+
}
|
|
11062
11312
|
.twm-plot-config__axes,
|
|
11063
11313
|
.twm-plot-config__list,
|
|
11064
11314
|
.twm-plot-config__series-list {
|
|
@@ -11092,37 +11342,114 @@ button.twm-alerts__ref:hover { text-decoration: underline; }
|
|
|
11092
11342
|
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
|
11093
11343
|
gap: var(--space-xs) var(--space-md);
|
|
11094
11344
|
}
|
|
11345
|
+
/* A series is one block (BUG-0052): a header line (picker, legend label,
|
|
11346
|
+
* remove) over its fields, one per line on the editor's 96px label column —
|
|
11347
|
+
* colour, width, dash, stack group, the embedder's `extraSeriesFields` (their
|
|
11348
|
+
* box is `display: contents`, so its rows are rows of the column) and the
|
|
11349
|
+
* overlay. Until 0.17.15 all of it was one wrapped line. */
|
|
11095
11350
|
.twm-plot-config__series {
|
|
11096
11351
|
display: flex;
|
|
11097
11352
|
flex-direction: column;
|
|
11098
|
-
gap: var(--space-
|
|
11099
|
-
padding: var(--space-
|
|
11353
|
+
gap: var(--space-xs);
|
|
11354
|
+
padding: var(--space-sm) 0;
|
|
11100
11355
|
border-top: 1px solid var(--border-subtle);
|
|
11356
|
+
min-width: 0;
|
|
11357
|
+
}
|
|
11358
|
+
.twm-plot-config__series-head {
|
|
11359
|
+
display: flex;
|
|
11360
|
+
flex-wrap: wrap;
|
|
11361
|
+
align-items: center;
|
|
11362
|
+
gap: var(--space-xs);
|
|
11363
|
+
min-width: 0;
|
|
11364
|
+
}
|
|
11365
|
+
.twm-plot-config__series-head > .twm-plot-config__picker {
|
|
11366
|
+
flex: 2 1 140px;
|
|
11367
|
+
}
|
|
11368
|
+
.twm-plot-config__series-head > .twm-plot-config__input {
|
|
11369
|
+
flex: 1 1 100px;
|
|
11370
|
+
}
|
|
11371
|
+
.twm-plot-config__series-fields {
|
|
11372
|
+
display: flex;
|
|
11373
|
+
flex-direction: column;
|
|
11374
|
+
gap: var(--space-xxs);
|
|
11375
|
+
min-width: 0;
|
|
11376
|
+
}
|
|
11377
|
+
.twm-plot-config__series-extra {
|
|
11378
|
+
display: contents;
|
|
11101
11379
|
}
|
|
11102
|
-
.twm-plot-config__series-main,
|
|
11103
11380
|
.twm-plot-config__row {
|
|
11104
11381
|
display: flex;
|
|
11105
11382
|
flex-wrap: wrap;
|
|
11106
11383
|
align-items: center;
|
|
11107
11384
|
gap: var(--space-xs);
|
|
11108
11385
|
}
|
|
11109
|
-
.twm-plot-config__series-main > .twm-plot-config__input:not(.twm-plot-config__input--number):not(.twm-plot-config__input--select),
|
|
11110
11386
|
.twm-plot-config__row > .twm-plot-config__input:not(.twm-plot-config__input--number):not(.twm-plot-config__input--select) {
|
|
11111
11387
|
flex: 1 1 100px;
|
|
11112
11388
|
}
|
|
11113
|
-
.twm-plot-config__series-main > .twm-plot-config__input--select,
|
|
11114
11389
|
.twm-plot-config__row > .twm-plot-config__input--select {
|
|
11115
11390
|
flex: 0 1 auto;
|
|
11116
11391
|
}
|
|
11117
|
-
|
|
11118
|
-
|
|
11392
|
+
/* On a field line a colour is the swatch and a hex box sized to `#rrggbb`. */
|
|
11393
|
+
.twm-plot-config__field > .twm-color-field {
|
|
11394
|
+
flex: 0 1 auto;
|
|
11395
|
+
flex-wrap: nowrap;
|
|
11396
|
+
}
|
|
11397
|
+
.twm-plot-config__field > .twm-color-field > .twm-color-field__hex {
|
|
11398
|
+
flex: 0 1 auto;
|
|
11399
|
+
width: 10ch;
|
|
11119
11400
|
}
|
|
11401
|
+
/* A colour's hex box is a well like the editor's other inputs. */
|
|
11402
|
+
.twm-plot-config .twm-color-field__hex {
|
|
11403
|
+
box-sizing: border-box;
|
|
11404
|
+
padding: var(--space-xxs) var(--space-xs);
|
|
11405
|
+
background: var(--surface-3);
|
|
11406
|
+
color: var(--text-primary);
|
|
11407
|
+
border: 1px solid var(--border-strong);
|
|
11408
|
+
border-radius: var(--radius-sm);
|
|
11409
|
+
}
|
|
11410
|
+
/* ... and still shows a hex that is not a colour (the well's border above
|
|
11411
|
+
* would otherwise hide ColorField's invalid border). */
|
|
11412
|
+
.twm-plot-config .twm-color-field--invalid .twm-color-field__hex {
|
|
11413
|
+
border-color: var(--color-danger);
|
|
11414
|
+
}
|
|
11415
|
+
/* A field line's select keeps to its options, as the number boxes do. */
|
|
11416
|
+
.twm-plot-config__series-fields .twm-plot-config__field > .twm-plot-config__input--select {
|
|
11417
|
+
flex: 0 1 auto;
|
|
11418
|
+
max-width: 100%;
|
|
11419
|
+
}
|
|
11420
|
+
/* An embedder's control drawn by a field spec's `render(slot)`. */
|
|
11421
|
+
.twm-plot-config__slot {
|
|
11422
|
+
flex: 1 1 auto;
|
|
11423
|
+
min-width: 0;
|
|
11424
|
+
}
|
|
11425
|
+
/* A field's hint, under its control: past the label column. Only a field
|
|
11426
|
+
* with a hint wraps, so a number box never drops under its label. */
|
|
11427
|
+
.twm-plot-config__field--hinted {
|
|
11428
|
+
flex-wrap: wrap;
|
|
11429
|
+
}
|
|
11430
|
+
.twm-plot-config__hint {
|
|
11431
|
+
box-sizing: border-box;
|
|
11432
|
+
flex: 1 0 100%;
|
|
11433
|
+
padding-left: calc(96px + var(--space-sm));
|
|
11434
|
+
font-size: var(--font-size-xs);
|
|
11435
|
+
color: var(--text-muted);
|
|
11436
|
+
}
|
|
11437
|
+
/* The overlay's own fields, under its source: set in by a rule, their label
|
|
11438
|
+
* column shortened by as much, so their controls stay on the series' column. */
|
|
11120
11439
|
.twm-plot-config__overlay,
|
|
11121
11440
|
.twm-plot-config__overlay-fields {
|
|
11122
11441
|
display: flex;
|
|
11123
11442
|
flex-direction: column;
|
|
11124
11443
|
gap: var(--space-xxs);
|
|
11125
|
-
|
|
11444
|
+
min-width: 0;
|
|
11445
|
+
}
|
|
11446
|
+
.twm-plot-config__overlay-fields:not(:empty) {
|
|
11447
|
+
margin-left: var(--space-sm);
|
|
11448
|
+
padding-left: calc(var(--space-sm) - 1px);
|
|
11449
|
+
border-left: 1px solid var(--border-subtle);
|
|
11450
|
+
}
|
|
11451
|
+
.twm-plot-config__overlay-fields > .twm-plot-config__field > .twm-plot-config__label {
|
|
11452
|
+
flex-basis: calc(96px - 2 * var(--space-sm));
|
|
11126
11453
|
}
|
|
11127
11454
|
.twm-plot-config__overlay-source {
|
|
11128
11455
|
color: var(--text-muted);
|
package/css/overrides.css
CHANGED
|
@@ -2262,6 +2262,12 @@ tr:hover .twm-row-action { opacity: 1; }
|
|
|
2262
2262
|
padding: 2px 8px;
|
|
2263
2263
|
line-height: 22px;
|
|
2264
2264
|
}
|
|
2265
|
+
/* A path of one segment is not drawn (STORY-0124): the tile's title already
|
|
2266
|
+
* names it, so the strip and its slot take no room until a second level. */
|
|
2267
|
+
.twm-page-shell__breadcrumb .twm-tile-breadcrumb[hidden],
|
|
2268
|
+
.twm-page-shell__breadcrumb:has(> .twm-tile-breadcrumb[hidden]) {
|
|
2269
|
+
display: none;
|
|
2270
|
+
}
|
|
2265
2271
|
.twm-page-shell__content {
|
|
2266
2272
|
flex: 1 1 auto;
|
|
2267
2273
|
min-height: 0;
|
package/dist/charts.js
CHANGED
|
@@ -55,17 +55,17 @@ import {
|
|
|
55
55
|
strideIndices,
|
|
56
56
|
uid,
|
|
57
57
|
updateChart
|
|
58
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-7OL6F2AM.js";
|
|
59
59
|
import {
|
|
60
60
|
AutocompleteField,
|
|
61
61
|
createRafResizeObserver
|
|
62
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-2Z2ENHR3.js";
|
|
63
63
|
import {
|
|
64
64
|
ColorField,
|
|
65
65
|
ManagedWindow,
|
|
66
66
|
ToggleField,
|
|
67
67
|
selectBox
|
|
68
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-PGZ4EHSE.js";
|
|
69
69
|
import "./chunk-HNU6MM2B.js";
|
|
70
70
|
import "./chunk-N7HC3ADV.js";
|
|
71
71
|
import "./chunk-WUFLGETN.js";
|
|
@@ -758,10 +758,27 @@ function removeButton(title, onClick) {
|
|
|
758
758
|
b.appendChild(el("span", "material-symbols-outlined", "close"));
|
|
759
759
|
return b;
|
|
760
760
|
}
|
|
761
|
-
|
|
761
|
+
var hintCount = 0;
|
|
762
|
+
function field(label, control, { hint = null } = {}) {
|
|
762
763
|
const single = control.matches("input, select, textarea");
|
|
763
|
-
const
|
|
764
|
-
|
|
764
|
+
const hinted = hint != null && hint !== "";
|
|
765
|
+
if (!hinted) {
|
|
766
|
+
const row2 = el(single ? "label" : "div", "twm-plot-config__field");
|
|
767
|
+
row2.append(el("span", "twm-plot-config__label", label), control);
|
|
768
|
+
return row2;
|
|
769
|
+
}
|
|
770
|
+
const row = el("div", "twm-plot-config__field twm-plot-config__field--hinted");
|
|
771
|
+
const text = el(single ? "label" : "span", "twm-plot-config__label", label);
|
|
772
|
+
const note = el("span", "twm-plot-config__hint", hint);
|
|
773
|
+
note.id = `twm-plot-config-hint-${++hintCount}`;
|
|
774
|
+
if (single) {
|
|
775
|
+
if (!control.id) control.id = `${note.id}-control`;
|
|
776
|
+
text.htmlFor = control.id;
|
|
777
|
+
}
|
|
778
|
+
const target = single ? control : control.querySelector("input, select, textarea") ?? control;
|
|
779
|
+
const describedBy = target.getAttribute("aria-describedby");
|
|
780
|
+
target.setAttribute("aria-describedby", describedBy ? `${describedBy} ${note.id}` : note.id);
|
|
781
|
+
row.append(text, control, note);
|
|
765
782
|
return row;
|
|
766
783
|
}
|
|
767
784
|
function textInput(value, onSet, { placeholder = "", label = null } = {}) {
|
|
@@ -920,7 +937,7 @@ function renderPlotConfigEditor(container, config, options = {}) {
|
|
|
920
937
|
node.classList.add("twm-plot-config__picker");
|
|
921
938
|
return node;
|
|
922
939
|
}
|
|
923
|
-
function colorField(value, onSet, label) {
|
|
940
|
+
function colorField(value, onSet, label, keep = track) {
|
|
924
941
|
const f = new ColorField({
|
|
925
942
|
value: value ?? "",
|
|
926
943
|
label,
|
|
@@ -931,7 +948,7 @@ function renderPlotConfigEditor(container, config, options = {}) {
|
|
|
931
948
|
}
|
|
932
949
|
}
|
|
933
950
|
});
|
|
934
|
-
|
|
951
|
+
keep(() => f.dispose());
|
|
935
952
|
return f.render();
|
|
936
953
|
}
|
|
937
954
|
function toggle(label, checked, onSet) {
|
|
@@ -1154,60 +1171,113 @@ function renderPlotConfigEditor(container, config, options = {}) {
|
|
|
1154
1171
|
}
|
|
1155
1172
|
function seriesRow(sp, axis, s, overlaySources) {
|
|
1156
1173
|
const row = el("div", "twm-plot-config__series");
|
|
1157
|
-
const
|
|
1158
|
-
|
|
1174
|
+
const head = el("div", "twm-plot-config__series-head");
|
|
1175
|
+
head.append(
|
|
1159
1176
|
seriesPicker(s, "variable", "series", sp, "Series"),
|
|
1160
1177
|
textInput(s.label, (v) => {
|
|
1161
1178
|
s.label = v;
|
|
1162
1179
|
notify();
|
|
1163
1180
|
}, { placeholder: "Legend label", label: "Legend label" }),
|
|
1164
|
-
|
|
1181
|
+
removeButton("Remove series", () => {
|
|
1182
|
+
const i = axis.series.indexOf(s);
|
|
1183
|
+
if (i >= 0) axis.series.splice(i, 1);
|
|
1184
|
+
renderBody();
|
|
1185
|
+
notify();
|
|
1186
|
+
})
|
|
1187
|
+
);
|
|
1188
|
+
const fields = el("div", "twm-plot-config__series-fields");
|
|
1189
|
+
fields.append(
|
|
1190
|
+
field("Colour", colorField(s.color, (v) => {
|
|
1165
1191
|
if (v) s.color = v;
|
|
1166
1192
|
else delete s.color;
|
|
1167
|
-
}, "Colour"),
|
|
1168
|
-
numberInput(
|
|
1193
|
+
}, "Colour")),
|
|
1194
|
+
field("Width", numberInput(
|
|
1169
1195
|
s.lineWidth ?? 2,
|
|
1170
1196
|
(v) => {
|
|
1171
1197
|
s.lineWidth = v ?? 2;
|
|
1172
1198
|
notify();
|
|
1173
1199
|
},
|
|
1174
1200
|
{ placeholder: "2", label: "Line width", min: 1, max: 10, step: 1 }
|
|
1175
|
-
),
|
|
1176
|
-
select(DASHES, s.lineStyle ?? "solid", (v) => {
|
|
1201
|
+
)),
|
|
1202
|
+
field("Dash", select(DASHES, s.lineStyle ?? "solid", (v) => {
|
|
1177
1203
|
s.lineStyle = v;
|
|
1178
1204
|
notify();
|
|
1179
|
-
}, { label: "Dash" }),
|
|
1180
|
-
textInput(
|
|
1205
|
+
}, { label: "Dash" })),
|
|
1206
|
+
field("Stack group", textInput(
|
|
1181
1207
|
s.stackGroup,
|
|
1182
1208
|
(v) => {
|
|
1183
1209
|
s.stackGroup = v || null;
|
|
1184
1210
|
notify();
|
|
1185
1211
|
},
|
|
1186
|
-
{ placeholder: "
|
|
1187
|
-
)
|
|
1188
|
-
removeButton("Remove series", () => {
|
|
1189
|
-
const i = axis.series.indexOf(s);
|
|
1190
|
-
if (i >= 0) axis.series.splice(i, 1);
|
|
1191
|
-
renderBody();
|
|
1192
|
-
notify();
|
|
1193
|
-
})
|
|
1212
|
+
{ placeholder: "None", label: "Stack group" }
|
|
1213
|
+
))
|
|
1194
1214
|
);
|
|
1195
|
-
|
|
1196
|
-
if (opts.
|
|
1197
|
-
|
|
1198
|
-
row.appendChild(extra);
|
|
1199
|
-
const handle = opts.extraSeriesFields(extra, s, { subplot: sp, axis, onChange: notify });
|
|
1200
|
-
if (typeof handle?.dispose === "function") track(() => handle.dispose());
|
|
1201
|
-
}
|
|
1202
|
-
if (opts.overlaySources) row.appendChild(overlayEditor(s, overlaySources));
|
|
1215
|
+
if (opts.extraSeriesFields) fields.appendChild(extraFields(sp, axis, s));
|
|
1216
|
+
if (opts.overlaySources) fields.appendChild(overlayEditor(s, overlaySources));
|
|
1217
|
+
row.append(head, fields);
|
|
1203
1218
|
return row;
|
|
1204
1219
|
}
|
|
1220
|
+
const warn = (...args) => opts.logger?.warn ? opts.logger.warn(...args) : console.warn(...args);
|
|
1221
|
+
function extraFields(sp, axis, s) {
|
|
1222
|
+
const extra = el("div", "twm-plot-config__series-extra");
|
|
1223
|
+
const result = opts.extraSeriesFields(extra, s, { subplot: sp, axis, onChange: notify, field });
|
|
1224
|
+
if (!Array.isArray(result)) {
|
|
1225
|
+
if (typeof result?.dispose === "function") track(() => result.dispose());
|
|
1226
|
+
return extra;
|
|
1227
|
+
}
|
|
1228
|
+
for (const spec of result) {
|
|
1229
|
+
const label = spec?.label;
|
|
1230
|
+
let control = null;
|
|
1231
|
+
if (typeof label !== "string") {
|
|
1232
|
+
warn("[plot config editor] an extraSeriesFields spec has no label; skipped", spec);
|
|
1233
|
+
continue;
|
|
1234
|
+
}
|
|
1235
|
+
if (spec.control != null) {
|
|
1236
|
+
if (!(spec.control instanceof Element)) {
|
|
1237
|
+
warn("[plot config editor] an extraSeriesFields spec control is not an Element; skipped", spec);
|
|
1238
|
+
continue;
|
|
1239
|
+
}
|
|
1240
|
+
control = spec.control;
|
|
1241
|
+
} else if (typeof spec.render === "function") {
|
|
1242
|
+
control = el("div", "twm-plot-config__slot");
|
|
1243
|
+
try {
|
|
1244
|
+
const handle = spec.render(control);
|
|
1245
|
+
if (typeof handle?.dispose === "function") track(() => handle.dispose());
|
|
1246
|
+
} catch (error) {
|
|
1247
|
+
warn("[plot config editor] an extraSeriesFields spec failed to render; skipped", error);
|
|
1248
|
+
continue;
|
|
1249
|
+
}
|
|
1250
|
+
} else {
|
|
1251
|
+
warn("[plot config editor] an extraSeriesFields spec has neither control nor render; skipped", spec);
|
|
1252
|
+
continue;
|
|
1253
|
+
}
|
|
1254
|
+
extra.appendChild(field(label, control, { hint: spec.hint }));
|
|
1255
|
+
}
|
|
1256
|
+
return extra;
|
|
1257
|
+
}
|
|
1205
1258
|
function overlayEditor(s, overlaySources) {
|
|
1206
1259
|
const wrap = el("div", "twm-plot-config__overlay");
|
|
1207
1260
|
const head = el("div", "twm-plot-config__overlay-source", "Loading overlay sources\u2026");
|
|
1208
1261
|
const details = el("div", "twm-plot-config__overlay-fields");
|
|
1209
1262
|
wrap.append(head, details);
|
|
1263
|
+
let detailDisposers = [];
|
|
1264
|
+
const disposeDetails = () => {
|
|
1265
|
+
const fns = detailDisposers;
|
|
1266
|
+
detailDisposers = [];
|
|
1267
|
+
for (const fn of fns) {
|
|
1268
|
+
try {
|
|
1269
|
+
fn();
|
|
1270
|
+
} catch (error) {
|
|
1271
|
+
opts.logger?.warn?.("[plot config editor] a field failed to dispose", error);
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
};
|
|
1275
|
+
track(disposeDetails);
|
|
1276
|
+
const keep = (fn) => {
|
|
1277
|
+
detailDisposers.push(fn);
|
|
1278
|
+
};
|
|
1210
1279
|
const drawDetails = () => {
|
|
1280
|
+
disposeDetails();
|
|
1211
1281
|
details.replaceChildren();
|
|
1212
1282
|
const ov = s.overlay;
|
|
1213
1283
|
if (!ov) return;
|
|
@@ -1223,7 +1293,7 @@ function renderPlotConfigEditor(container, config, options = {}) {
|
|
|
1223
1293
|
field("Colour", colorField(ov.color, (v) => {
|
|
1224
1294
|
if (v) ov.color = v;
|
|
1225
1295
|
else delete ov.color;
|
|
1226
|
-
}, "Overlay colour")),
|
|
1296
|
+
}, "Overlay colour", keep)),
|
|
1227
1297
|
field("Dash", select(DASHES, ov.lineStyle ?? "dashed", (v) => {
|
|
1228
1298
|
ov.lineStyle = v;
|
|
1229
1299
|
notify();
|