@aceshooting/lyra-ui 2.0.0 → 2.1.0
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/README.md +87 -14
- package/custom-elements.json +11555 -5539
- package/dist/components/app-rail/app-rail-item.d.ts +30 -0
- package/dist/components/app-rail/app-rail-item.js +68 -0
- package/dist/components/app-rail/app-rail-item.styles.d.ts +1 -0
- package/dist/components/app-rail/app-rail-item.styles.js +65 -0
- package/dist/components/app-rail/app-rail.d.ts +21 -24
- package/dist/components/app-rail/app-rail.js +68 -132
- package/dist/components/app-rail/app-rail.styles.js +5 -2
- package/dist/components/attachment-chip/attachment-chip.d.ts +38 -11
- package/dist/components/attachment-chip/attachment-chip.js +73 -29
- package/dist/components/attachment-trigger/attachment-trigger.d.ts +13 -0
- package/dist/components/attachment-trigger/attachment-trigger.js +13 -1
- package/dist/components/attachment-trigger/attachment-trigger.styles.js +28 -6
- package/dist/components/chart/bar-chart.js +2 -18
- package/dist/components/chart/box-plot.d.ts +25 -1
- package/dist/components/chart/box-plot.js +136 -6
- package/dist/components/chart/box-plot.styles.js +13 -0
- package/dist/components/chart/bubble-chart.js +2 -18
- package/dist/components/chart/chart.d.ts +54 -10
- package/dist/components/chart/chart.js +182 -39
- package/dist/components/chart/doughnut-chart.js +2 -18
- package/dist/components/chart/histogram-bin.d.ts +5 -2
- package/dist/components/chart/histogram-bin.js +12 -4
- package/dist/components/chart/histogram.js +8 -16
- package/dist/components/chart/line-chart.js +2 -18
- package/dist/components/chart/lite-chart.d.ts +80 -3
- package/dist/components/chart/lite-chart.js +250 -53
- package/dist/components/chart/pie-chart.js +2 -18
- package/dist/components/chart/polar-area-chart.js +2 -18
- package/dist/components/chart/radar-chart.js +2 -18
- package/dist/components/chart/scatter-chart.js +2 -18
- package/dist/components/chat-composer/chat-composer.d.ts +42 -1
- package/dist/components/chat-composer/chat-composer.js +84 -3
- package/dist/components/chat-composer/chat-composer.styles.js +11 -1
- package/dist/components/chat-message/chat-message.d.ts +32 -4
- package/dist/components/chat-message/chat-message.js +57 -17
- package/dist/components/checkbox/checkbox.d.ts +66 -8
- package/dist/components/checkbox/checkbox.js +173 -50
- package/dist/components/checkbox/checkbox.styles.js +9 -2
- package/dist/components/chip/chip-group.js +8 -2
- package/dist/components/chip/chip.js +9 -2
- package/dist/components/citation-badge/citation-badge.js +21 -3
- package/dist/components/code-block/code-block.d.ts +38 -0
- package/dist/components/code-block/code-block.js +72 -7
- package/dist/components/code-block/code-loader.d.ts +44 -1
- package/dist/components/code-block/code-loader.js +63 -0
- package/dist/components/combobox/combobox.d.ts +46 -5
- package/dist/components/combobox/combobox.js +175 -35
- package/dist/components/combobox/combobox.styles.js +3 -3
- package/dist/components/context-meter/context-meter.js +17 -8
- package/dist/components/context-meter/context-meter.styles.js +0 -6
- package/dist/components/conversation-item/conversation-item.d.ts +13 -16
- package/dist/components/conversation-item/conversation-item.js +29 -20
- package/dist/components/date-picker/calendar-core.d.ts +24 -0
- package/dist/components/date-picker/calendar-core.js +51 -4
- package/dist/components/date-picker/date-input.d.ts +77 -10
- package/dist/components/date-picker/date-input.js +278 -73
- package/dist/components/date-picker/date-picker.d.ts +28 -4
- package/dist/components/date-picker/date-picker.js +95 -37
- package/dist/components/dialog/confirm.js +3 -6
- package/dist/components/dialog/dialog.d.ts +75 -31
- package/dist/components/dialog/dialog.js +147 -131
- package/dist/components/dialog/dialog.styles.js +46 -2
- package/dist/components/dock-panel/dock-panel.d.ts +8 -0
- package/dist/components/dock-panel/dock-panel.js +15 -1
- package/dist/components/dock-panel/dock-panel.styles.js +15 -1
- package/dist/components/document-preview/document-preview.d.ts +15 -7
- package/dist/components/document-preview/document-preview.js +29 -13
- package/dist/components/empty/empty.d.ts +1 -2
- package/dist/components/empty/empty.js +18 -12
- package/dist/components/empty/empty.styles.js +0 -3
- package/dist/components/export-button/export-button.d.ts +12 -0
- package/dist/components/export-button/export-button.js +33 -5
- package/dist/components/export-button/export-button.styles.js +5 -1
- package/dist/components/file-input/accept.js +2 -0
- package/dist/components/file-input/file-input.d.ts +10 -1
- package/dist/components/file-input/file-input.js +36 -5
- package/dist/components/flag/flag.d.ts +1 -1
- package/dist/components/flag/flag.js +15 -1
- package/dist/components/flag/flag.styles.js +1 -1
- package/dist/components/flag/language-map.d.ts +4 -1
- package/dist/components/flag/language-map.js +6 -3
- package/dist/components/gauge/gauge.d.ts +10 -1
- package/dist/components/gauge/gauge.js +28 -9
- package/dist/components/generation-status/generation-status.d.ts +13 -5
- package/dist/components/generation-status/generation-status.js +59 -5
- package/dist/components/graph/graph.d.ts +37 -3
- package/dist/components/graph/graph.js +132 -20
- package/dist/components/heatmap/calendar-grid.d.ts +15 -6
- package/dist/components/heatmap/calendar-grid.js +17 -6
- package/dist/components/heatmap/heatmap-scale.d.ts +5 -1
- package/dist/components/heatmap/heatmap-scale.js +8 -2
- package/dist/components/heatmap/heatmap.d.ts +166 -25
- package/dist/components/heatmap/heatmap.js +372 -89
- package/dist/components/json-viewer/json-viewer.d.ts +22 -5
- package/dist/components/json-viewer/json-viewer.js +115 -37
- package/dist/components/json-viewer/json-viewer.styles.js +2 -1
- package/dist/components/kbd/kbd.js +24 -7
- package/dist/components/live-region/live-region.d.ts +10 -1
- package/dist/components/live-region/live-region.js +42 -6
- package/dist/components/map/map.d.ts +24 -1
- package/dist/components/map/map.js +89 -24
- package/dist/components/markdown/markdown-loader.d.ts +24 -0
- package/dist/components/markdown/markdown-loader.js +35 -1
- package/dist/components/markdown/markdown.d.ts +46 -8
- package/dist/components/markdown/markdown.js +136 -31
- package/dist/components/markdown/markdown.styles.js +7 -1
- package/dist/components/media-card/media-card.d.ts +25 -17
- package/dist/components/media-card/media-card.js +43 -65
- package/dist/components/mention-popover/mention-popover.d.ts +4 -1
- package/dist/components/mention-popover/mention-popover.js +23 -5
- package/dist/components/menu/menu-item.d.ts +32 -3
- package/dist/components/menu/menu-item.js +94 -7
- package/dist/components/menu/menu-item.styles.js +8 -0
- package/dist/components/menu/menu.d.ts +25 -1
- package/dist/components/menu/menu.js +99 -3
- package/dist/components/menu/menu.styles.js +3 -2
- package/dist/components/model-select/model-select.d.ts +48 -3
- package/dist/components/model-select/model-select.js +120 -22
- package/dist/components/model-select/model-select.styles.js +17 -3
- package/dist/components/model-settings-panel/model-settings-panel.d.ts +25 -1
- package/dist/components/model-settings-panel/model-settings-panel.js +65 -1
- package/dist/components/playback/playback.d.ts +6 -2
- package/dist/components/playback/playback.js +72 -29
- package/dist/components/playback/playback.styles.js +6 -1
- package/dist/components/responsive-panel/responsive-panel.d.ts +29 -16
- package/dist/components/responsive-panel/responsive-panel.js +130 -132
- package/dist/components/responsive-panel/responsive-panel.styles.js +1 -1
- package/dist/components/result-card/result-card.d.ts +8 -2
- package/dist/components/result-card/result-card.js +9 -3
- package/dist/components/result-card/result-card.styles.js +0 -1
- package/dist/components/select/select.d.ts +59 -20
- package/dist/components/select/select.js +125 -43
- package/dist/components/skeleton/skeleton.d.ts +3 -0
- package/dist/components/skeleton/skeleton.js +7 -1
- package/dist/components/skeleton/skeleton.styles.js +5 -4
- package/dist/components/slider/slider.d.ts +57 -5
- package/dist/components/slider/slider.js +195 -80
- package/dist/components/source-card/source-card.d.ts +17 -5
- package/dist/components/source-card/source-card.js +55 -8
- package/dist/components/source-card/source-card.styles.js +1 -1
- package/dist/components/source-list/source-list.js +9 -1
- package/dist/components/sparkline/sparkline.js +34 -10
- package/dist/components/split/split.d.ts +141 -14
- package/dist/components/split/split.js +357 -52
- package/dist/components/split/split.styles.js +18 -1
- package/dist/components/stat/stat.d.ts +10 -4
- package/dist/components/stat/stat.js +46 -11
- package/dist/components/stream-status/stream-status.d.ts +27 -11
- package/dist/components/stream-status/stream-status.js +95 -13
- package/dist/components/streaming-text/streaming-text.d.ts +9 -3
- package/dist/components/streaming-text/streaming-text.js +25 -10
- package/dist/components/streaming-text/streaming-text.styles.js +3 -2
- package/dist/components/switch/switch.d.ts +52 -7
- package/dist/components/switch/switch.js +111 -40
- package/dist/components/switch/switch.styles.js +1 -1
- package/dist/components/table/table.d.ts +18 -3
- package/dist/components/table/table.js +88 -20
- package/dist/components/tabs/tabs.d.ts +23 -0
- package/dist/components/tabs/tabs.js +76 -12
- package/dist/components/tabs/tabs.styles.js +11 -0
- package/dist/components/thinking-panel/thinking-panel.d.ts +3 -1
- package/dist/components/thinking-panel/thinking-panel.js +34 -9
- package/dist/components/thinking-panel/thinking-panel.styles.js +6 -0
- package/dist/components/time-range/time-range.d.ts +59 -2
- package/dist/components/time-range/time-range.js +163 -67
- package/dist/components/toast/toast-item.d.ts +12 -3
- package/dist/components/toast/toast-item.js +90 -53
- package/dist/components/toast/toast-item.styles.js +59 -28
- package/dist/components/toast/toast.js +17 -5
- package/dist/components/toast/toaster.js +10 -1
- package/dist/components/tool-approval-dialog/tool-approval-dialog.d.ts +11 -10
- package/dist/components/tool-approval-dialog/tool-approval-dialog.js +50 -124
- package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.js +1 -1
- package/dist/components/tool-call-chip/tool-call-chip.d.ts +18 -2
- package/dist/components/tool-call-chip/tool-call-chip.js +86 -25
- package/dist/components/tool-param-form/tool-param-form.d.ts +89 -28
- package/dist/components/tool-param-form/tool-param-form.js +350 -106
- package/dist/components/tool-result-dialog/tool-result-dialog.d.ts +27 -7
- package/dist/components/tool-result-dialog/tool-result-dialog.js +80 -115
- package/dist/components/tool-result-dialog/tool-result-dialog.styles.js +1 -1
- package/dist/components/tool-result-view/registry.d.ts +0 -8
- package/dist/components/tool-result-view/registry.js +7 -2
- package/dist/components/tool-result-view/tool-result-view.d.ts +6 -3
- package/dist/components/tool-result-view/tool-result-view.js +18 -4
- package/dist/components/tool-select-dialog/tool-select-dialog.d.ts +11 -11
- package/dist/components/tool-select-dialog/tool-select-dialog.js +44 -133
- package/dist/components/tool-select-dialog/tool-select-dialog.styles.js +13 -4
- package/dist/components/tree/tree-node.d.ts +4 -1
- package/dist/components/tree/tree-node.js +4 -1
- package/dist/components/tree/tree.d.ts +17 -8
- package/dist/components/tree/tree.js +21 -20
- package/dist/components/typing-indicator/typing-indicator.d.ts +12 -2
- package/dist/components/typing-indicator/typing-indicator.js +16 -4
- package/dist/components/typing-indicator/typing-indicator.styles.js +2 -2
- package/dist/components/virtual-list/virtual-list.d.ts +56 -19
- package/dist/components/virtual-list/virtual-list.js +184 -38
- package/dist/components/virtual-list/virtual-list.styles.js +12 -0
- package/dist/components/widget/widget.d.ts +5 -4
- package/dist/components/widget/widget.js +54 -120
- package/dist/components/widget/widget.styles.js +8 -2
- package/dist/components/word-cloud/word-cloud-layout.d.ts +7 -0
- package/dist/components/word-cloud/word-cloud-layout.js +20 -6
- package/dist/components/word-cloud/word-cloud.d.ts +22 -2
- package/dist/components/word-cloud/word-cloud.js +45 -6
- package/dist/components/word-cloud/word-cloud.styles.js +13 -1
- package/dist/internal/anchored-validity.d.ts +1 -0
- package/dist/internal/anchored-validity.js +86 -0
- package/dist/internal/form-associated.d.ts +7 -0
- package/dist/internal/form-associated.js +43 -4
- package/dist/internal/numbers.d.ts +12 -0
- package/dist/internal/numbers.js +26 -0
- package/dist/internal/overlay-manager.d.ts +51 -0
- package/dist/internal/overlay-manager.js +523 -0
- package/dist/internal/positioner.js +27 -3
- package/dist/internal/safe-url.d.ts +12 -0
- package/dist/internal/safe-url.js +41 -0
- package/dist/internal/tokens.styles.js +17 -11
- package/dist/lyra.d.ts +5 -2
- package/dist/lyra.js +3 -1
- package/llms-full.txt +970 -426
- package/llms.txt +5 -4
- package/package.json +19 -2
|
@@ -7,7 +7,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
import { html, nothing } from 'lit';
|
|
8
8
|
import { property, state, query } from 'lit/decorators.js';
|
|
9
9
|
import { LyraElement } from '../../internal/lyra-element.js';
|
|
10
|
+
import { activateOverlay } from '../../internal/overlay-manager.js';
|
|
10
11
|
import { defineElement } from '../../internal/prefix.js';
|
|
12
|
+
import { lockScroll } from '../../internal/scroll-lock.js';
|
|
11
13
|
import { isRtl } from '../../internal/rtl.js';
|
|
12
14
|
import { styles } from './split.styles.js';
|
|
13
15
|
const KEYBOARD_STEP = 2;
|
|
@@ -32,16 +34,48 @@ const NO_MAX_PX = 1_000_000;
|
|
|
32
34
|
* icon-only/collapsed UI of its own; slotted content is expected to adapt to
|
|
33
35
|
* its own clamped width (e.g. via its own container query).
|
|
34
36
|
*
|
|
37
|
+
* `collapseState` is a public accessor with force/auto semantics mirroring
|
|
38
|
+
* `<lyra-app-rail>`'s `mode`: it's normally derived automatically from the
|
|
39
|
+
* measured container width (via a `ResizeObserver` on `[part="base"]`)
|
|
40
|
+
* whenever it crosses `railBreakpoint`/`floatBreakpoint`, but assigning it a
|
|
41
|
+
* concrete `'wide'`/`'rail'`/`'floating'` value pins it there and stops that
|
|
42
|
+
* automatic tracking — useful for a consumer-driven toggle (e.g. a button
|
|
43
|
+
* that forces `'floating'` regardless of width). Assigning the write-only
|
|
44
|
+
* `'auto'` sentinel releases the pin and immediately re-derives the state
|
|
45
|
+
* from the current measured width, resuming automatic tracking. `'auto'` is
|
|
46
|
+
* never a value this getter returns.
|
|
47
|
+
*
|
|
48
|
+
* The `'floating'` state is a hidden-by-default drawer, gated by `open`
|
|
49
|
+
* (mirrors `<lyra-app-rail>`'s mobile overlay): while `collapseState` is
|
|
50
|
+
* `'floating'` and `open` is `false` (the default), the collapsing panel
|
|
51
|
+
* renders nothing — `hidden`, out of the accessibility tree, not just
|
|
52
|
+
* visually hidden — instead of the always-visible overlay card this state
|
|
53
|
+
* rendered before `open` existed. Setting `open = true` reveals it as a
|
|
54
|
+
* focus-trapped floating panel with a `[part="backdrop"]` scrim; Escape or a
|
|
55
|
+
* backdrop click set `open` back to `false`. `open` is preserved (not reset)
|
|
56
|
+
* while `collapseState` isn't `'floating'`, but no drawer chrome renders
|
|
57
|
+
* until it is again — except that leaving `'floating'` while `open` is
|
|
58
|
+
* `true` (a breakpoint crossing back to `'wide'`/`'rail'`, or a forced
|
|
59
|
+
* reassignment) also closes it, the same way `<lyra-app-rail>` closes its
|
|
60
|
+
* mobile overlay when leaving `'mobile'` while open.
|
|
61
|
+
*
|
|
35
62
|
* @customElement lyra-split
|
|
36
63
|
* @event lyra-resize - `detail: { sizes }`, fired on every drag step/release
|
|
37
64
|
* and every keyboard step.
|
|
38
65
|
* @event lyra-split-collapse-change - `detail: { state }` (`SplitCollapseChangeDetail`),
|
|
39
66
|
* fired whenever the responsive `collapseState` actually transitions between
|
|
40
|
-
* `'wide'`/`'rail'`/`'floating'
|
|
41
|
-
*
|
|
67
|
+
* `'wide'`/`'rail'`/`'floating'` — whether from a breakpoint crossing or an
|
|
68
|
+
* explicit `collapseState` assignment. Only relevant when `collapse` isn't
|
|
69
|
+
* `"none"`; never fires otherwise. Not fired for a redundant reassignment
|
|
70
|
+
* to the state already in effect.
|
|
71
|
+
* @event lyra-split-constraints-invalid - `detail: SplitConstraintIssueDetail`,
|
|
72
|
+
* fired once when the configured panel minimums/maximums cannot describe a
|
|
73
|
+
* layout that fits the track. The splitter rejects that infeasible set for
|
|
74
|
+
* interaction and falls back to a normalized percent minimum.
|
|
42
75
|
* @slot - Panels to arrange side by side (or stacked, when `orientation="vertical"`); each direct child becomes one resizable panel.
|
|
43
76
|
* @csspart base - The flex layout wrapper (`position: relative`, so the `'floating'` collapse state can anchor to it).
|
|
44
77
|
* @csspart divider - Each divider between two panels; carries `aria-disabled="true"` and is drag/keyboard-inert while its adjacent panel is collapsed (`'rail'`/`'floating'`).
|
|
78
|
+
* @csspart backdrop - The `'floating'` drawer's scrim. Only rendered while `collapseState === 'floating'` and `open`.
|
|
45
79
|
*/
|
|
46
80
|
export class LyraSplit extends LyraElement {
|
|
47
81
|
constructor() {
|
|
@@ -73,10 +107,26 @@ export class LyraSplit extends LyraElement {
|
|
|
73
107
|
/** Container width (px) below which the collapsing pane instead becomes an
|
|
74
108
|
* absolutely-positioned overlay above the other pane (`'floating'` state). */
|
|
75
109
|
this.floatBreakpoint = 400;
|
|
110
|
+
/** Whether the `'floating'` collapse state's drawer is shown. Only
|
|
111
|
+
* meaningful while `collapseState` is `'floating'` — the value is
|
|
112
|
+
* preserved (not reset) while another state is active, but no drawer
|
|
113
|
+
* chrome renders until `collapseState` is `'floating'` again. Defaults to
|
|
114
|
+
* `false`: the collapsing pane renders nothing while floating until a
|
|
115
|
+
* consumer opts in by setting this (or it's forced open programmatically)
|
|
116
|
+
* — see the class doc. */
|
|
117
|
+
this.open = false;
|
|
76
118
|
this.panelCount = 0;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
119
|
+
this._collapseState = 'wide';
|
|
120
|
+
// Whether ResizeObserver-driven measurement is currently ignored because a
|
|
121
|
+
// consumer forced a specific collapseState -- see the accessor doc.
|
|
122
|
+
this._forced = false;
|
|
123
|
+
// Derived from collapseState === 'floating' && open -- tracked as its own
|
|
124
|
+
// field (rather than recomputed inline everywhere) so willUpdate can detect
|
|
125
|
+
// the specific false->true/true->false transition regardless of which of
|
|
126
|
+
// the two source properties changed (mirrors lyra-app-rail's
|
|
127
|
+
// `overlayActive`).
|
|
128
|
+
this.overlayActive = false;
|
|
129
|
+
this.justOpened = false;
|
|
80
130
|
// Keyed by pointerId so an interrupted or concurrent (multi-touch) drag on
|
|
81
131
|
// one divider never reads or clobbers another pointer's drag state.
|
|
82
132
|
this.drags = new Map();
|
|
@@ -86,10 +136,14 @@ export class LyraSplit extends LyraElement {
|
|
|
86
136
|
// lyra-split-authored styles cleared instead of carrying them around stale
|
|
87
137
|
// forever.
|
|
88
138
|
this.stylizedPanels = new Set();
|
|
139
|
+
this.constraintIssueKey = '';
|
|
89
140
|
this.onSlotChange = () => {
|
|
90
141
|
this.panelCount = this.children.length;
|
|
91
142
|
this.ensureSizes();
|
|
92
143
|
};
|
|
144
|
+
this.onBackdropClick = () => {
|
|
145
|
+
this.overlayHandle?.dismissBackdrop();
|
|
146
|
+
};
|
|
93
147
|
this.onPointerDown = (e, index) => {
|
|
94
148
|
// The divider adjacent to a currently rail/floating-collapsed pane isn't
|
|
95
149
|
// draggable — mirrors the `[part="divider"][aria-disabled="true"]`
|
|
@@ -98,11 +152,12 @@ export class LyraSplit extends LyraElement {
|
|
|
98
152
|
// wouldn't stop).
|
|
99
153
|
if (this.isDividerDisabled(index))
|
|
100
154
|
return;
|
|
101
|
-
|
|
155
|
+
// The divider that dispatched this is itself a child of [part="base"],
|
|
156
|
+
// so baseEl is guaranteed to be already rendered here.
|
|
102
157
|
this.drags.set(e.pointerId, {
|
|
103
158
|
index,
|
|
104
159
|
startPos: this.orientation === 'vertical' ? e.clientY : e.clientX,
|
|
105
|
-
base,
|
|
160
|
+
base: this.baseEl,
|
|
106
161
|
appliedDelta: 0,
|
|
107
162
|
});
|
|
108
163
|
e.target.setPointerCapture(e.pointerId);
|
|
@@ -194,6 +249,19 @@ export class LyraSplit extends LyraElement {
|
|
|
194
249
|
};
|
|
195
250
|
}
|
|
196
251
|
static { this.styles = [LyraElement.styles, styles]; }
|
|
252
|
+
// `collapseState` needs a custom accessor (force/auto semantics -- see the
|
|
253
|
+
// class doc) rather than the usual @property()-generated one -- registered
|
|
254
|
+
// here, alongside the decorator-declared properties below, the same way
|
|
255
|
+
// lyra-app-rail's `mode` is. `reflect: true` still applies: LitElement's
|
|
256
|
+
// update loop reflects any `reflect`-flagged property to its attribute
|
|
257
|
+
// generically by reading `this[name]` after render, regardless of whether
|
|
258
|
+
// that property has a custom or auto-generated accessor. `attribute` is
|
|
259
|
+
// spelled out (unlike `mode`, a single word whose Lit-default lowercase
|
|
260
|
+
// form already reads fine) so the reflected attribute stays kebab-case,
|
|
261
|
+
// matching this file's other attribute-reflected properties.
|
|
262
|
+
static { this.properties = {
|
|
263
|
+
collapseState: { reflect: true, attribute: 'collapse-state', noAccessor: true },
|
|
264
|
+
}; }
|
|
197
265
|
connectedCallback() {
|
|
198
266
|
super.connectedCallback();
|
|
199
267
|
this.panelCount = this.children.length;
|
|
@@ -206,6 +274,23 @@ export class LyraSplit extends LyraElement {
|
|
|
206
274
|
// own ResizeObserver.
|
|
207
275
|
if (this.collapse !== 'none')
|
|
208
276
|
this.armCollapseObserver();
|
|
277
|
+
// A reconnect (e.g. a drag-and-drop reparent keeping this same element
|
|
278
|
+
// instance) fires disconnectedCallback then connectedCallback
|
|
279
|
+
// synchronously with no update in between, so willUpdate never reruns to
|
|
280
|
+
// notice the floating drawer is still active -- restore its shared
|
|
281
|
+
// registration and scroll lock. Mirrors lyra-app-rail's identical
|
|
282
|
+
// reconnect handling for its mobile overlay.
|
|
283
|
+
if (this.hasUpdated && this.overlayActive) {
|
|
284
|
+
if (this.overlayHandle?.isActive()) {
|
|
285
|
+
this.overlayHandle.resume();
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
this.activateFloatingOverlay();
|
|
289
|
+
}
|
|
290
|
+
if (!this.releaseScrollLock)
|
|
291
|
+
this.releaseScrollLock = lockScroll(this.ownerDocument);
|
|
292
|
+
queueMicrotask(() => this.overlayHandle?.focusInitial());
|
|
293
|
+
}
|
|
209
294
|
}
|
|
210
295
|
disconnectedCallback() {
|
|
211
296
|
super.disconnectedCallback();
|
|
@@ -216,11 +301,106 @@ export class LyraSplit extends LyraElement {
|
|
|
216
301
|
window.removeEventListener('pointercancel', this.onPointerUp);
|
|
217
302
|
window.removeEventListener('lostpointercapture', this.onPointerUp);
|
|
218
303
|
this.collapseResizeObserver?.disconnect();
|
|
304
|
+
this.releaseScrollLock?.();
|
|
305
|
+
this.releaseScrollLock = undefined;
|
|
306
|
+
this.overlayHandle?.suspend();
|
|
219
307
|
}
|
|
220
308
|
firstUpdated() {
|
|
221
309
|
if (this.collapse !== 'none')
|
|
222
310
|
this.armCollapseObserver();
|
|
223
311
|
}
|
|
312
|
+
/** Both branches here derive one reactive property from another with no
|
|
313
|
+
* DOM measurement involved, so they belong in `willUpdate()` rather than
|
|
314
|
+
* `updated()`: setting a reactive property from `updated()`/
|
|
315
|
+
* `firstUpdated()` schedules a *second* update on top of the one that
|
|
316
|
+
* just finished, which Lit's dev-mode console flags ("scheduled an
|
|
317
|
+
* update ... after an update completed"). Both of these were real and
|
|
318
|
+
* reproducible in normal usage, not just test artifacts --
|
|
319
|
+
* `ensureSizes()` on any direct `sizes` assignment whose length doesn't
|
|
320
|
+
* match `panelCount` (e.g. a consumer correcting a stale layout), and the
|
|
321
|
+
* `collapseState` reset on every `collapse` -> `'none'` transition. The
|
|
322
|
+
* `collapse !== 'none'` re-arm path in `syncCollapseObserver()` still
|
|
323
|
+
* needs a freshly measured container width and stays in `updated()` --
|
|
324
|
+
* see `armCollapseObserver()`'s own doc comment for why *that* one is the
|
|
325
|
+
* documented exception instead (mirrors virtual-list.ts's
|
|
326
|
+
* `attachContainerListeners()`). */
|
|
327
|
+
willUpdate(changed) {
|
|
328
|
+
if (changed.has('sizes') || this.sizes.length === 0)
|
|
329
|
+
this.ensureSizes();
|
|
330
|
+
if (changed.has('collapse') && this.collapse === 'none') {
|
|
331
|
+
this.resetCollapseState();
|
|
332
|
+
}
|
|
333
|
+
if (changed.has('open') || changed.has('collapseState')) {
|
|
334
|
+
const next = this._collapseState === 'floating' && this.open;
|
|
335
|
+
if (next !== this.overlayActive) {
|
|
336
|
+
this.overlayActive = next;
|
|
337
|
+
if (next) {
|
|
338
|
+
this.justOpened = true;
|
|
339
|
+
this.activateFloatingOverlay();
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
this.deactivateFloatingOverlay();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* The collapsing pane's current responsive state. Always one of the three
|
|
349
|
+
* real states — never `'auto'` — reflecting either the live measured
|
|
350
|
+
* width or, once forced, whatever was last assigned. See the class doc for
|
|
351
|
+
* the full force/auto contract (mirrors `<lyra-app-rail>`'s `mode`).
|
|
352
|
+
*/
|
|
353
|
+
get collapseState() {
|
|
354
|
+
return this._collapseState;
|
|
355
|
+
}
|
|
356
|
+
set collapseState(next) {
|
|
357
|
+
if (next === 'auto') {
|
|
358
|
+
this._forced = false;
|
|
359
|
+
this.updateCollapseState(this.currentMeasuredWidth());
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
this._forced = true;
|
|
363
|
+
this.applyCollapseStateChange(next);
|
|
364
|
+
}
|
|
365
|
+
/** The container width (px) `[part="base"]` is measured at right now --
|
|
366
|
+
* used to re-derive `collapseState` immediately when a forced value is
|
|
367
|
+
* released back to `'auto'`, outside of the `ResizeObserver`'s own
|
|
368
|
+
* (async, entry-driven) callback. Mirrors the synchronous initial read
|
|
369
|
+
* `armCollapseObserver()` already does when it (re-)arms. */
|
|
370
|
+
currentMeasuredWidth() {
|
|
371
|
+
return this.baseEl?.clientWidth ?? 0;
|
|
372
|
+
}
|
|
373
|
+
/** Resets `collapseState` back to its `'wide'` default and releases any
|
|
374
|
+
* force, bypassing the public accessor's pin/event-emit side effects --
|
|
375
|
+
* used only for the `collapse -> 'none'` housekeeping reset in
|
|
376
|
+
* `willUpdate()`, which is purely internal bookkeeping (the feature is
|
|
377
|
+
* off entirely), not a state transition a consumer forced or observed. */
|
|
378
|
+
resetCollapseState() {
|
|
379
|
+
this._forced = false;
|
|
380
|
+
if (this._collapseState === 'wide')
|
|
381
|
+
return;
|
|
382
|
+
const old = this._collapseState;
|
|
383
|
+
this._collapseState = 'wide';
|
|
384
|
+
this.requestUpdate('collapseState', old);
|
|
385
|
+
}
|
|
386
|
+
/** Applies an actual `collapseState` transition (no-op if `next` matches
|
|
387
|
+
* the current value): updates the field, triggers Lit's reflect/re-render
|
|
388
|
+
* via `requestUpdate`, and fires `lyra-split-collapse-change`. Shared by
|
|
389
|
+
* both the `ResizeObserver`-driven measurement path (`updateCollapseState`)
|
|
390
|
+
* and the accessor's forced-value assignment, so the two never duplicate
|
|
391
|
+
* this logic. Leaving `'floating'` while `open` is still `true` also
|
|
392
|
+
* closes the drawer -- mirrors `<lyra-app-rail>` closing its mobile
|
|
393
|
+
* overlay when `mode` leaves `'mobile'` while open. */
|
|
394
|
+
applyCollapseStateChange(next) {
|
|
395
|
+
if (next === this._collapseState)
|
|
396
|
+
return;
|
|
397
|
+
const old = this._collapseState;
|
|
398
|
+
this._collapseState = next;
|
|
399
|
+
this.requestUpdate('collapseState', old);
|
|
400
|
+
this.emit('lyra-split-collapse-change', { state: next });
|
|
401
|
+
if (next !== 'floating' && this.open)
|
|
402
|
+
this.open = false;
|
|
403
|
+
}
|
|
224
404
|
get storageFullKey() {
|
|
225
405
|
return this.storageKey ? `lyra-split:${this.storageKey}:${this.panelCount}` : undefined;
|
|
226
406
|
}
|
|
@@ -298,10 +478,9 @@ export class LyraSplit extends LyraElement {
|
|
|
298
478
|
* container resize between calls is always picked up — same live read
|
|
299
479
|
* `onPointerMove` already does via `drag.base.clientWidth/clientHeight`. */
|
|
300
480
|
getContainerSize() {
|
|
301
|
-
|
|
302
|
-
if (!base)
|
|
481
|
+
if (!this.baseEl)
|
|
303
482
|
return 0;
|
|
304
|
-
return this.orientation === 'vertical' ?
|
|
483
|
+
return this.orientation === 'vertical' ? this.baseEl.clientHeight : this.baseEl.clientWidth;
|
|
305
484
|
}
|
|
306
485
|
/** The physical panel index `collapse: 'start' | 'end'` resolves to, or
|
|
307
486
|
* `-1` when collapse is off (`'none'`) or there are fewer than 2 panels
|
|
@@ -338,16 +517,19 @@ export class LyraSplit extends LyraElement {
|
|
|
338
517
|
return index === adjacent;
|
|
339
518
|
}
|
|
340
519
|
/** Classifies a measured container width into the collapsing pane's
|
|
341
|
-
* responsive state and, only on an actual transition,
|
|
342
|
-
* `
|
|
520
|
+
* responsive state and, only on an actual transition, applies it (via the
|
|
521
|
+
* same `applyCollapseStateChange()` the accessor's forced-value path
|
|
522
|
+
* uses). Gated behind `_forced` so a pinned `collapseState` is never
|
|
523
|
+
* silently overwritten by a subsequent resize — this is also what the
|
|
524
|
+
* accessor's `'auto'` release calls (with the current measured width) to
|
|
525
|
+
* re-derive the state without duplicating the width-vs-breakpoint logic. */
|
|
343
526
|
updateCollapseState(width) {
|
|
344
527
|
if (this.collapse === 'none')
|
|
345
528
|
return;
|
|
346
|
-
|
|
347
|
-
if (next === this.collapseState)
|
|
529
|
+
if (this._forced)
|
|
348
530
|
return;
|
|
349
|
-
this.
|
|
350
|
-
this.
|
|
531
|
+
const next = width < this.floatBreakpoint ? 'floating' : width < this.railBreakpoint ? 'rail' : 'wide';
|
|
532
|
+
this.applyCollapseStateChange(next);
|
|
351
533
|
}
|
|
352
534
|
/** Creates (idempotently) and (re-)observes `[part="base"]` with the
|
|
353
535
|
* collapse-state `ResizeObserver` — a no-op until `baseEl` exists (see
|
|
@@ -370,32 +552,90 @@ export class LyraSplit extends LyraElement {
|
|
|
370
552
|
}
|
|
371
553
|
}
|
|
372
554
|
/** Reacts to a live `collapse` property change (as opposed to the
|
|
373
|
-
* connect/first-render arming above): turns the observer on/off
|
|
374
|
-
*
|
|
375
|
-
*
|
|
555
|
+
* connect/first-render arming above): turns the observer on/off. The
|
|
556
|
+
* `collapseState` reset for the `'none'` case lives in `willUpdate()`
|
|
557
|
+
* instead (see its doc comment) so no stale rail/floating styling
|
|
558
|
+
* survives switching collapse off, without the extra render pass a
|
|
559
|
+
* property set here would cost. */
|
|
376
560
|
syncCollapseObserver() {
|
|
377
561
|
if (this.collapse === 'none') {
|
|
378
562
|
this.collapseResizeObserver?.disconnect();
|
|
379
|
-
this.collapseState = 'wide';
|
|
380
563
|
return;
|
|
381
564
|
}
|
|
382
565
|
this.armCollapseObserver();
|
|
383
566
|
}
|
|
384
|
-
/**
|
|
385
|
-
*
|
|
386
|
-
*
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
567
|
+
/** The safe fallback for a shared percent minimum. A value above the
|
|
568
|
+
* available share is still useful as a consumer intent, but it cannot be
|
|
569
|
+
* honored for every panel at once, so reduce it proportionally. */
|
|
570
|
+
normalizedDefaultMin() {
|
|
571
|
+
if (this.panelCount <= 0)
|
|
572
|
+
return 0;
|
|
573
|
+
const requested = Number.isFinite(this.min) ? Math.max(0, this.min) : 0;
|
|
574
|
+
// Leave one keyboard step of aggregate slack after rejecting an
|
|
575
|
+
// infeasible configuration, so an equal starting layout remains
|
|
576
|
+
// resizable instead of merely becoming a different frozen layout.
|
|
577
|
+
const available = Math.max(0, 100 - KEYBOARD_STEP);
|
|
578
|
+
return Math.min(requested, available / this.panelCount);
|
|
579
|
+
}
|
|
580
|
+
/** Resolves all panel bounds together so aggregate feasibility is checked
|
|
581
|
+
* before an individual divider is asked to clamp a pair. An invalid set is
|
|
582
|
+
* rejected as a whole: interaction uses the safe shared minimum instead of
|
|
583
|
+
* exposing a divider whose minimum is greater than its maximum. */
|
|
584
|
+
resolveConstraintBounds(containerSize) {
|
|
585
|
+
const fallbackMin = this.normalizedDefaultMin();
|
|
586
|
+
const fallback = Array.from({ length: this.panelCount }, () => ({ min: fallbackMin, max: Infinity }));
|
|
587
|
+
if (this.panelCount <= 0)
|
|
588
|
+
return { bounds: [], usePanelConstraints: true };
|
|
589
|
+
const bounds = [];
|
|
590
|
+
let minimumTotal = 0;
|
|
591
|
+
let maximumTotal = 0;
|
|
592
|
+
let hasUnboundedMaximum = false;
|
|
593
|
+
let issueReason;
|
|
594
|
+
for (let index = 0; index < this.panelCount; index++) {
|
|
595
|
+
const constraint = this.panelConstraints[index];
|
|
596
|
+
let min = Number.isFinite(this.min) ? Math.max(0, this.min) : 0;
|
|
597
|
+
let max = Infinity;
|
|
598
|
+
if (constraint) {
|
|
599
|
+
if (constraint.minPx != null && Number.isFinite(constraint.minPx) && constraint.minPx >= 0 && containerSize > 0) {
|
|
600
|
+
min = (constraint.minPx / containerSize) * 100;
|
|
601
|
+
}
|
|
602
|
+
if (constraint.maxPx != null && Number.isFinite(constraint.maxPx) && constraint.maxPx >= 0 && containerSize > 0) {
|
|
603
|
+
max = (constraint.maxPx / containerSize) * 100;
|
|
604
|
+
}
|
|
605
|
+
if ((constraint.minPx != null && (!Number.isFinite(constraint.minPx) || constraint.minPx < 0)) ||
|
|
606
|
+
(constraint.maxPx != null && (!Number.isFinite(constraint.maxPx) || constraint.maxPx < 0))) {
|
|
607
|
+
issueReason ??= 'minimum-exceeds-maximum';
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
if (min > max)
|
|
611
|
+
issueReason ??= 'minimum-exceeds-maximum';
|
|
612
|
+
bounds.push({ min, max });
|
|
613
|
+
minimumTotal += min;
|
|
614
|
+
if (Number.isFinite(max))
|
|
615
|
+
maximumTotal += max;
|
|
616
|
+
else
|
|
617
|
+
hasUnboundedMaximum = true;
|
|
397
618
|
}
|
|
398
|
-
|
|
619
|
+
if (issueReason == null && minimumTotal > 100 + 1e-9)
|
|
620
|
+
issueReason = 'minimum-total';
|
|
621
|
+
if (issueReason == null && !hasUnboundedMaximum && maximumTotal < 100 - 1e-9)
|
|
622
|
+
issueReason = 'maximum-total';
|
|
623
|
+
if (issueReason == null)
|
|
624
|
+
return { bounds, usePanelConstraints: true };
|
|
625
|
+
return {
|
|
626
|
+
bounds: fallback,
|
|
627
|
+
usePanelConstraints: false,
|
|
628
|
+
issue: {
|
|
629
|
+
reason: issueReason,
|
|
630
|
+
panelCount: this.panelCount,
|
|
631
|
+
minimumTotal,
|
|
632
|
+
maximumTotal: hasUnboundedMaximum ? null : maximumTotal,
|
|
633
|
+
containerSize,
|
|
634
|
+
},
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
percentBounds(index, containerSize) {
|
|
638
|
+
return this.resolveConstraintBounds(containerSize).bounds[index] ?? { min: 0, max: Infinity };
|
|
399
639
|
}
|
|
400
640
|
clampPair(sizes, i, delta, containerSize = 0) {
|
|
401
641
|
const next = [...sizes];
|
|
@@ -424,11 +664,43 @@ export class LyraSplit extends LyraElement {
|
|
|
424
664
|
if (commit)
|
|
425
665
|
this.persist();
|
|
426
666
|
}
|
|
667
|
+
/** The collapsing pane's light-DOM element itself — the `'floating'`
|
|
668
|
+
* drawer's focus-trap/backdrop target. There's no separate shadow-DOM
|
|
669
|
+
* panel to trap focus within (unlike `<lyra-app-rail>`'s `[part="panel"]`):
|
|
670
|
+
* the slotted panel *is* the floating drawer, just repositioned via
|
|
671
|
+
* inline styles in `updated()`. */
|
|
672
|
+
get floatingPanelEl() {
|
|
673
|
+
const index = this.collapsingIndex;
|
|
674
|
+
return index === -1 ? null : (this.children[index] ?? null);
|
|
675
|
+
}
|
|
676
|
+
activateFloatingOverlay() {
|
|
677
|
+
this.releaseScrollLock ??= lockScroll(this.ownerDocument);
|
|
678
|
+
this.overlayHandle = activateOverlay({
|
|
679
|
+
host: this,
|
|
680
|
+
panel: () => this.floatingPanelEl,
|
|
681
|
+
onEscape: () => (this.open = false),
|
|
682
|
+
onBackdrop: () => (this.open = false),
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
deactivateFloatingOverlay() {
|
|
686
|
+
this.releaseScrollLock?.();
|
|
687
|
+
this.releaseScrollLock = undefined;
|
|
688
|
+
this.overlayHandle?.deactivate();
|
|
689
|
+
this.overlayHandle = undefined;
|
|
690
|
+
}
|
|
427
691
|
updated(changed) {
|
|
428
|
-
if (changed.has('sizes') || this.sizes.length === 0)
|
|
429
|
-
this.ensureSizes();
|
|
430
692
|
if (changed.has('collapse'))
|
|
431
693
|
this.syncCollapseObserver();
|
|
694
|
+
const constraintResolution = this.resolveConstraintBounds(this.getContainerSize());
|
|
695
|
+
const issue = constraintResolution.issue;
|
|
696
|
+
const issueKey = issue
|
|
697
|
+
? `${issue.reason}:${issue.panelCount}:${issue.minimumTotal}:${issue.maximumTotal ?? 'unbounded'}`
|
|
698
|
+
: '';
|
|
699
|
+
if (issueKey !== this.constraintIssueKey) {
|
|
700
|
+
this.constraintIssueKey = issueKey;
|
|
701
|
+
if (issue)
|
|
702
|
+
this.emit('lyra-split-constraints-invalid', issue);
|
|
703
|
+
}
|
|
432
704
|
const panels = [...this.children];
|
|
433
705
|
const current = new Set(panels);
|
|
434
706
|
// A panel that dropped out of the slot (removed from the light DOM)
|
|
@@ -445,6 +717,7 @@ export class LyraSplit extends LyraElement {
|
|
|
445
717
|
stale.style.removeProperty('inset-inline-start');
|
|
446
718
|
stale.style.removeProperty('inset-inline-end');
|
|
447
719
|
stale.style.removeProperty('inline-size');
|
|
720
|
+
stale.hidden = false;
|
|
448
721
|
delete stale.dataset.collapseState;
|
|
449
722
|
}
|
|
450
723
|
}
|
|
@@ -456,7 +729,7 @@ export class LyraSplit extends LyraElement {
|
|
|
456
729
|
const collapsingIndex = this.collapseActive ? this.collapsingIndex : -1;
|
|
457
730
|
panels.forEach((panel, i) => {
|
|
458
731
|
const percent = this.sizes[i] ?? 0;
|
|
459
|
-
const constraint = this.panelConstraints[i];
|
|
732
|
+
const constraint = constraintResolution.usePanelConstraints ? this.panelConstraints[i] : undefined;
|
|
460
733
|
// Collapse-only inline styles are always cleared first, then
|
|
461
734
|
// re-applied below only for the panel(s) that need them this pass —
|
|
462
735
|
// simpler than diffing which of the 5 properties changed.
|
|
@@ -465,17 +738,30 @@ export class LyraSplit extends LyraElement {
|
|
|
465
738
|
panel.style.removeProperty('inset-inline-start');
|
|
466
739
|
panel.style.removeProperty('inset-inline-end');
|
|
467
740
|
panel.style.removeProperty('inline-size');
|
|
741
|
+
panel.hidden = false;
|
|
468
742
|
if (collapsingIndex === i && this.collapseState === 'rail') {
|
|
469
743
|
// Fixed rail width instead of the normal clamp()/percent flex-basis.
|
|
470
744
|
panel.style.flex = `0 0 ${this.railWidth}`;
|
|
471
745
|
}
|
|
746
|
+
else if (collapsingIndex === i && this.collapseState === 'floating' && !this.open) {
|
|
747
|
+
// Hidden-by-default drawer (see the class doc): rendering nothing
|
|
748
|
+
// (not just visually collapsed, so it's out of the accessibility
|
|
749
|
+
// tree too) until a consumer sets `open`. The pane still vacates the
|
|
750
|
+
// flex flow the same way the open/floating branch below does, via
|
|
751
|
+
// `hidden`'s UA `display: none` rather than `position: absolute`, so
|
|
752
|
+
// the other pane below still grows to fill the space regardless of
|
|
753
|
+
// which of the two branches applied.
|
|
754
|
+
panel.style.flex = 'none';
|
|
755
|
+
panel.hidden = true;
|
|
756
|
+
}
|
|
472
757
|
else if (collapsingIndex === i && this.collapseState === 'floating') {
|
|
473
|
-
// Lifted out of the flex flow entirely as
|
|
474
|
-
// to its own logical start/end edge,
|
|
475
|
-
// extent — [part="base"] carries
|
|
476
|
-
//
|
|
477
|
-
// percent width (i.e. what
|
|
478
|
-
// state), so there's no visual
|
|
758
|
+
// `this.open` is true here. Lifted out of the flex flow entirely as
|
|
759
|
+
// an overlay card, anchored to its own logical start/end edge,
|
|
760
|
+
// spanning the full cross-axis extent — [part="base"] carries
|
|
761
|
+
// `position: relative` for this to anchor against (see
|
|
762
|
+
// split.styles.ts). Sized at its own normal percent width (i.e. what
|
|
763
|
+
// it would render at in the `'wide'` state), so there's no visual
|
|
764
|
+
// size jump the moment it un-floats.
|
|
479
765
|
panel.style.flex = 'none';
|
|
480
766
|
panel.style.position = 'absolute';
|
|
481
767
|
panel.style.setProperty('inset-block', '0');
|
|
@@ -519,14 +805,28 @@ export class LyraSplit extends LyraElement {
|
|
|
519
805
|
else {
|
|
520
806
|
this.removeAttribute('data-collapse-state');
|
|
521
807
|
}
|
|
808
|
+
// Runs after this render (not willUpdate) so the floating panel's
|
|
809
|
+
// repositioned geometry above has already landed before the focus call
|
|
810
|
+
// below can rely on it -- mirrors lyra-app-rail's/lyra-dialog's identical
|
|
811
|
+
// ordering rationale for their own overlay's initial focus.
|
|
812
|
+
if (this.justOpened) {
|
|
813
|
+
this.justOpened = false;
|
|
814
|
+
this.overlayHandle?.focusInitial();
|
|
815
|
+
}
|
|
522
816
|
}
|
|
523
817
|
render() {
|
|
524
818
|
const dividers = [];
|
|
819
|
+
const bounds = this.resolveConstraintBounds(this.getContainerSize()).bounds;
|
|
525
820
|
for (let i = 0; i < this.panelCount - 1; i++) {
|
|
526
|
-
// The achievable
|
|
527
|
-
//
|
|
528
|
-
//
|
|
529
|
-
|
|
821
|
+
// The achievable range is bounded by both adjacent panels, not the
|
|
822
|
+
// whole track — pushing past it would starve the partner even though
|
|
823
|
+
// this pair still has room. Keep max >= min for imperfect persisted or
|
|
824
|
+
// consumer-supplied starting sizes.
|
|
825
|
+
const pairTotal = (this.sizes[i] ?? 0) + (this.sizes[i + 1] ?? 0);
|
|
826
|
+
const a = bounds[i] ?? { min: 0, max: Infinity };
|
|
827
|
+
const b = bounds[i + 1] ?? { min: 0, max: Infinity };
|
|
828
|
+
const valueMin = Math.max(a.min, pairTotal - b.max);
|
|
829
|
+
const valueMax = Math.max(valueMin, Math.min(a.max, pairTotal - b.min));
|
|
530
830
|
const disabled = this.isDividerDisabled(i);
|
|
531
831
|
dividers.push(html `<div
|
|
532
832
|
part="divider"
|
|
@@ -534,7 +834,7 @@ export class LyraSplit extends LyraElement {
|
|
|
534
834
|
aria-label="Resize divider between panel ${i + 1} and panel ${i + 2}"
|
|
535
835
|
aria-orientation=${this.orientation === 'vertical' ? 'horizontal' : 'vertical'}
|
|
536
836
|
aria-valuenow=${Math.round(this.sizes[i] ?? 0)}
|
|
537
|
-
aria-valuemin=${
|
|
837
|
+
aria-valuemin=${Math.round(valueMin)}
|
|
538
838
|
aria-valuemax=${Math.round(valueMax)}
|
|
539
839
|
aria-disabled=${disabled ? 'true' : nothing}
|
|
540
840
|
tabindex=${disabled ? '-1' : '0'}
|
|
@@ -543,7 +843,12 @@ export class LyraSplit extends LyraElement {
|
|
|
543
843
|
@keydown=${(e) => this.onDividerKeyDown(e, i)}
|
|
544
844
|
></div>`);
|
|
545
845
|
}
|
|
546
|
-
|
|
846
|
+
const showBackdrop = this.collapseState === 'floating' && this.open;
|
|
847
|
+
return html `<div part="base">
|
|
848
|
+
<slot @slotchange=${this.onSlotChange}></slot>
|
|
849
|
+
${dividers}
|
|
850
|
+
${showBackdrop ? html `<div part="backdrop" @click=${this.onBackdropClick}></div>` : nothing}
|
|
851
|
+
</div>`;
|
|
547
852
|
}
|
|
548
853
|
}
|
|
549
854
|
__decorate([
|
|
@@ -574,11 +879,11 @@ __decorate([
|
|
|
574
879
|
property({ type: Number, attribute: 'float-breakpoint' })
|
|
575
880
|
], LyraSplit.prototype, "floatBreakpoint", void 0);
|
|
576
881
|
__decorate([
|
|
577
|
-
|
|
578
|
-
], LyraSplit.prototype, "
|
|
882
|
+
property({ type: Boolean, reflect: true })
|
|
883
|
+
], LyraSplit.prototype, "open", void 0);
|
|
579
884
|
__decorate([
|
|
580
885
|
state()
|
|
581
|
-
], LyraSplit.prototype, "
|
|
886
|
+
], LyraSplit.prototype, "panelCount", void 0);
|
|
582
887
|
__decorate([
|
|
583
888
|
query('[part="base"]')
|
|
584
889
|
], LyraSplit.prototype, "baseEl", void 0);
|
|
@@ -4,6 +4,10 @@ export const styles = css `
|
|
|
4
4
|
display: block;
|
|
5
5
|
inline-size: 100%;
|
|
6
6
|
block-size: 100%;
|
|
7
|
+
/* Component-specific -- not a shared design token, so a consumer can
|
|
8
|
+
retheme it without a raw literal leaking into the public API (same
|
|
9
|
+
rationale as lyra-app-rail's --lyra-app-rail-overlay-color). */
|
|
10
|
+
--lyra-split-overlay-color: rgb(0 0 0 / 0.5);
|
|
7
11
|
}
|
|
8
12
|
[part='base'] {
|
|
9
13
|
display: flex;
|
|
@@ -64,13 +68,26 @@ export const styles = css `
|
|
|
64
68
|
/* The 'floating' collapse state's overlay "card" look -- geometry
|
|
65
69
|
(position/inset-*/inline-size) is set inline by split.ts's updated(),
|
|
66
70
|
matching how the ordinary flex/order styling is applied; only the
|
|
67
|
-
visual/stacking treatment lives here.
|
|
71
|
+
visual/stacking treatment lives here. z-index is above [part="backdrop"]
|
|
72
|
+
(below), so the drawer renders on top of its own scrim. */
|
|
68
73
|
::slotted([data-collapse-state='floating']) {
|
|
69
74
|
z-index: 1;
|
|
70
75
|
background: var(--lyra-color-surface);
|
|
71
76
|
border-radius: var(--lyra-radius);
|
|
72
77
|
box-shadow: var(--lyra-shadow);
|
|
73
78
|
}
|
|
79
|
+
/* The 'floating' drawer's scrim -- only rendered while collapseState is
|
|
80
|
+
'floating' and open (see split.ts's render()). Scoped to [part="base"]
|
|
81
|
+
(position: absolute against its position: relative ancestor) rather than
|
|
82
|
+
a viewport-fixed overlay like lyra-app-rail's mobile backdrop: the
|
|
83
|
+
floating panel itself is only ever positioned relative to this
|
|
84
|
+
component's own box, never the full page. */
|
|
85
|
+
[part='backdrop'] {
|
|
86
|
+
position: absolute;
|
|
87
|
+
inset: 0;
|
|
88
|
+
z-index: 0;
|
|
89
|
+
background: var(--lyra-split-overlay-color);
|
|
90
|
+
}
|
|
74
91
|
/* Rail-clamped content can easily overflow the fixed rail-width — clip it
|
|
75
92
|
rather than letting it blow out the layout; the panel's own content is
|
|
76
93
|
expected to adapt to the narrower width itself (e.g. via a container
|