@xmesh/system-design 0.0.1
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 +472 -0
- package/assets/brand-lockup-dark.svg +9 -0
- package/assets/brand-lockup-light.svg +9 -0
- package/assets/brand-mark.svg +9 -0
- package/colors_and_type.css +11 -0
- package/dist/lit/components/alert/index.css +201 -0
- package/dist/lit/components/alert/index.d.ts +25 -0
- package/dist/lit/components/alert/index.js +191 -0
- package/dist/lit/components/app-bar/index.css +80 -0
- package/dist/lit/components/app-bar/index.d.ts +19 -0
- package/dist/lit/components/app-bar/index.js +120 -0
- package/dist/lit/components/artifact/index.css +166 -0
- package/dist/lit/components/artifact/index.d.ts +37 -0
- package/dist/lit/components/artifact/index.js +294 -0
- package/dist/lit/components/autocomplete/index.css +171 -0
- package/dist/lit/components/autocomplete/index.d.ts +47 -0
- package/dist/lit/components/autocomplete/index.js +404 -0
- package/dist/lit/components/avatar/index.css +62 -0
- package/dist/lit/components/avatar/index.d.ts +19 -0
- package/dist/lit/components/avatar/index.js +112 -0
- package/dist/lit/components/avatar-group/index.css +60 -0
- package/dist/lit/components/avatar-group/index.d.ts +19 -0
- package/dist/lit/components/avatar-group/index.js +97 -0
- package/dist/lit/components/badge/index.css +72 -0
- package/dist/lit/components/badge/index.d.ts +18 -0
- package/dist/lit/components/badge/index.js +115 -0
- package/dist/lit/components/brand-mark/index.css +109 -0
- package/dist/lit/components/brand-mark/index.d.ts +24 -0
- package/dist/lit/components/brand-mark/index.js +116 -0
- package/dist/lit/components/breadcrumbs/index.css +91 -0
- package/dist/lit/components/breadcrumbs/index.d.ts +19 -0
- package/dist/lit/components/breadcrumbs/index.js +104 -0
- package/dist/lit/components/bubble/index.css +182 -0
- package/dist/lit/components/bubble/index.d.ts +72 -0
- package/dist/lit/components/bubble/index.js +617 -0
- package/dist/lit/components/button/index.css +342 -0
- package/dist/lit/components/button/index.d.ts +32 -0
- package/dist/lit/components/button/index.js +202 -0
- package/dist/lit/components/card/index.css +99 -0
- package/dist/lit/components/card/index.d.ts +20 -0
- package/dist/lit/components/card/index.js +133 -0
- package/dist/lit/components/chat/index.css +292 -0
- package/dist/lit/components/chat/index.d.ts +74 -0
- package/dist/lit/components/chat/index.js +589 -0
- package/dist/lit/components/checkbox/index.css +126 -0
- package/dist/lit/components/checkbox/index.d.ts +21 -0
- package/dist/lit/components/checkbox/index.js +138 -0
- package/dist/lit/components/chip/index.css +145 -0
- package/dist/lit/components/chip/index.d.ts +30 -0
- package/dist/lit/components/chip/index.js +230 -0
- package/dist/lit/components/chip-group/index.css +19 -0
- package/dist/lit/components/chip-group/index.d.ts +24 -0
- package/dist/lit/components/chip-group/index.js +171 -0
- package/dist/lit/components/code/index.css +42 -0
- package/dist/lit/components/code/index.d.ts +12 -0
- package/dist/lit/components/code/index.js +68 -0
- package/dist/lit/components/composer/index.css +548 -0
- package/dist/lit/components/composer/index.d.ts +67 -0
- package/dist/lit/components/composer/index.js +713 -0
- package/dist/lit/components/data-table/index.css +166 -0
- package/dist/lit/components/data-table/index.d.ts +55 -0
- package/dist/lit/components/data-table/index.js +390 -0
- package/dist/lit/components/dialog/index.css +124 -0
- package/dist/lit/components/dialog/index.d.ts +24 -0
- package/dist/lit/components/dialog/index.js +199 -0
- package/dist/lit/components/divider/index.css +27 -0
- package/dist/lit/components/divider/index.d.ts +13 -0
- package/dist/lit/components/divider/index.js +67 -0
- package/dist/lit/components/empty-state/index.css +69 -0
- package/dist/lit/components/empty-state/index.d.ts +21 -0
- package/dist/lit/components/empty-state/index.js +123 -0
- package/dist/lit/components/expansion-panel/index.css +120 -0
- package/dist/lit/components/expansion-panel/index.d.ts +22 -0
- package/dist/lit/components/expansion-panel/index.js +174 -0
- package/dist/lit/components/field/index.css +223 -0
- package/dist/lit/components/field/index.d.ts +106 -0
- package/dist/lit/components/field/index.js +388 -0
- package/dist/lit/components/file-input/index.css +257 -0
- package/dist/lit/components/file-input/index.d.ts +30 -0
- package/dist/lit/components/file-input/index.js +298 -0
- package/dist/lit/components/form/index.css +29 -0
- package/dist/lit/components/form/index.d.ts +38 -0
- package/dist/lit/components/form/index.js +192 -0
- package/dist/lit/components/grid/index.css +53 -0
- package/dist/lit/components/grid/index.d.ts +14 -0
- package/dist/lit/components/grid/index.js +82 -0
- package/dist/lit/components/kbd/index.css +35 -0
- package/dist/lit/components/kbd/index.d.ts +11 -0
- package/dist/lit/components/kbd/index.js +43 -0
- package/dist/lit/components/list/index.css +15 -0
- package/dist/lit/components/list/index.d.ts +28 -0
- package/dist/lit/components/list/index.js +188 -0
- package/dist/lit/components/list-item/index.css +119 -0
- package/dist/lit/components/list-item/index.d.ts +20 -0
- package/dist/lit/components/list-item/index.js +127 -0
- package/dist/lit/components/menu/index.css +94 -0
- package/dist/lit/components/menu/index.d.ts +47 -0
- package/dist/lit/components/menu/index.js +386 -0
- package/dist/lit/components/navigation-drawer/index.css +114 -0
- package/dist/lit/components/navigation-drawer/index.d.ts +29 -0
- package/dist/lit/components/navigation-drawer/index.js +218 -0
- package/dist/lit/components/overlay/index.css +171 -0
- package/dist/lit/components/overlay/index.d.ts +65 -0
- package/dist/lit/components/overlay/index.js +566 -0
- package/dist/lit/components/pagination/index.css +102 -0
- package/dist/lit/components/pagination/index.d.ts +22 -0
- package/dist/lit/components/pagination/index.js +184 -0
- package/dist/lit/components/primitives/index.css +504 -0
- package/dist/lit/components/primitives/index.d.ts +25 -0
- package/dist/lit/components/primitives/index.js +283 -0
- package/dist/lit/components/progress/index.css +143 -0
- package/dist/lit/components/progress/index.d.ts +23 -0
- package/dist/lit/components/progress/index.js +180 -0
- package/dist/lit/components/radio-group/index.css +178 -0
- package/dist/lit/components/radio-group/index.d.ts +35 -0
- package/dist/lit/components/radio-group/index.js +292 -0
- package/dist/lit/components/select/index.css +151 -0
- package/dist/lit/components/select/index.d.ts +50 -0
- package/dist/lit/components/select/index.js +390 -0
- package/dist/lit/components/sidebar-item/index.css +133 -0
- package/dist/lit/components/sidebar-item/index.d.ts +20 -0
- package/dist/lit/components/sidebar-item/index.js +105 -0
- package/dist/lit/components/skeleton/index.css +81 -0
- package/dist/lit/components/skeleton/index.d.ts +19 -0
- package/dist/lit/components/skeleton/index.js +119 -0
- package/dist/lit/components/slider/index.css +171 -0
- package/dist/lit/components/slider/index.d.ts +36 -0
- package/dist/lit/components/slider/index.js +302 -0
- package/dist/lit/components/snackbar/index.css +279 -0
- package/dist/lit/components/snackbar/index.d.ts +33 -0
- package/dist/lit/components/snackbar/index.js +195 -0
- package/dist/lit/components/stack/index.css +41 -0
- package/dist/lit/components/stack/index.d.ts +20 -0
- package/dist/lit/components/stack/index.js +103 -0
- package/dist/lit/components/switch/index.css +126 -0
- package/dist/lit/components/switch/index.d.ts +17 -0
- package/dist/lit/components/switch/index.js +116 -0
- package/dist/lit/components/table/index.css +85 -0
- package/dist/lit/components/table/index.d.ts +25 -0
- package/dist/lit/components/table/index.js +139 -0
- package/dist/lit/components/tabs/index.css +116 -0
- package/dist/lit/components/tabs/index.d.ts +49 -0
- package/dist/lit/components/tabs/index.js +320 -0
- package/dist/lit/components/text-field/index.css +90 -0
- package/dist/lit/components/text-field/index.d.ts +17 -0
- package/dist/lit/components/text-field/index.js +101 -0
- package/dist/lit/components/textarea/index.css +55 -0
- package/dist/lit/components/textarea/index.d.ts +26 -0
- package/dist/lit/components/textarea/index.js +124 -0
- package/dist/lit/components/tooltip/index.css +37 -0
- package/dist/lit/components/tooltip/index.d.ts +31 -0
- package/dist/lit/components/tooltip/index.js +196 -0
- package/dist/lit/components/validation/index.css +386 -0
- package/dist/lit/components/validation/index.d.ts +45 -0
- package/dist/lit/components/validation/index.js +318 -0
- package/dist/lit/index.d.ts +50 -0
- package/dist/lit/index.js +59 -0
- package/package.json +81 -0
- package/styles/README.md +346 -0
- package/styles/_elevation.css +24 -0
- package/styles/_fonts.css +6 -0
- package/styles/_layout.css +37 -0
- package/styles/_primitives.css +154 -0
- package/styles/_scroll.css +75 -0
- package/styles/_semantic.css +146 -0
- package/styles/_space.css +61 -0
- package/styles/_type.css +139 -0
- package/styles/_xmesh-extensions.css +232 -0
- package/styles/index.css +44 -0
- package/styles/md3/_color.css +102 -0
- package/styles/md3/_elevation.css +26 -0
- package/styles/md3/_motion.css +35 -0
- package/styles/md3/_shape.css +22 -0
- package/styles/md3/_state.css +22 -0
- package/styles/md3/_type.css +111 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/*
|
|
2
|
+
navigation-drawer/index.ts — <xm-navigation-drawer>, the side navigation panel.
|
|
3
|
+
|
|
4
|
+
<xm-navigation-drawer> — an edge-anchored navigation container that hosts
|
|
5
|
+
xm-sidebar-item children. Belongs to the INVERSE-SURFACE family (the card
|
|
6
|
+
stack), carries the directional drawer shadow, and supports modal (focus-
|
|
7
|
+
trapped, scrim, inert background) and persistent / collapsed-rail modes.
|
|
8
|
+
|
|
9
|
+
Authoring:
|
|
10
|
+
<xm-navigation-drawer open modal>
|
|
11
|
+
<xm-sidebar-item title="Overview" active></xm-sidebar-item>
|
|
12
|
+
<xm-sidebar-item title="Activity"></xm-sidebar-item>
|
|
13
|
+
</xm-navigation-drawer>
|
|
14
|
+
el.addEventListener("close", () => (el.open = false));
|
|
15
|
+
|
|
16
|
+
Properties:
|
|
17
|
+
open boolean (reflected) — declarative open state
|
|
18
|
+
collapsed boolean (reflected) — icon-rail mode (persistent)
|
|
19
|
+
modal boolean (reflected) — trap focus + inert background + scrim
|
|
20
|
+
label string — accessible name for the drawer region
|
|
21
|
+
|
|
22
|
+
Events:
|
|
23
|
+
close bare component dismiss signal (bubbles + composed, no detail).
|
|
24
|
+
|
|
25
|
+
── Overlay composition (AD-5 / AD-5a — the decision) ──────────────────────
|
|
26
|
+
xm-overlay's modal mode is a CENTERED card (margin:auto, max-width 560px); it
|
|
27
|
+
has no edge-placement mode, and AD-12 forbids restyling its shadow root. So
|
|
28
|
+
the drawer renders its OWN native <dialog> in EDGE layout and reuses the
|
|
29
|
+
IDENTICAL platform-native modal contract that xm-overlay itself codifies:
|
|
30
|
+
• showModal() → focus-trap + background `inert`, for free (AD-5)
|
|
31
|
+
• <dialog>::backdrop → the scrim
|
|
32
|
+
• opener capture + .focus() restore on close (NFR-14)
|
|
33
|
+
• innermost-only Esc: the native top layer routes Esc to the topmost modal;
|
|
34
|
+
we handle `cancel`, preventDefault, close, dispatch `close`, and
|
|
35
|
+
stopPropagation so one keypress never closes an enclosing layer (AD-5a)
|
|
36
|
+
• dialog tier: a modal <dialog> rides the native top layer above every
|
|
37
|
+
popover by spec — exactly what `dialog` being the highest tier means, so
|
|
38
|
+
no z-index is invented (AD-5a)
|
|
39
|
+
This is the same mechanism xm-overlay's own modal path uses — there is no
|
|
40
|
+
divergent focus-trap or positioning dependency (NFR-16). Persistent / rail
|
|
41
|
+
mode renders an <aside> with no showModal().
|
|
42
|
+
|
|
43
|
+
Shadow DOM; Lit from lit; sibling CSS via the built-
|
|
44
|
+
file-relative new URL(...). BEM block `nav-drawer` (differs from the element
|
|
45
|
+
name). Hosts light-DOM xm-sidebar-item children unchanged (AD-12); the panel
|
|
46
|
+
remaps the surface role tokens so those items read on inverse-surface.
|
|
47
|
+
*/
|
|
48
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
49
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
50
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
51
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
52
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
53
|
+
};
|
|
54
|
+
import { LitElement, html, nothing } from "lit";
|
|
55
|
+
import { customElement, property, query } from "lit/decorators.js";
|
|
56
|
+
// Resolve CSS relative to the *built* file:
|
|
57
|
+
// lit/build/components/navigation-drawer/index.js → ../...
|
|
58
|
+
const NAV_DRAWER_CSS = new URL("../navigation-drawer/index.css", import.meta.url).href;
|
|
59
|
+
let XmNavigationDrawer = class XmNavigationDrawer extends LitElement {
|
|
60
|
+
constructor() {
|
|
61
|
+
super(...arguments);
|
|
62
|
+
this.open = false;
|
|
63
|
+
this.collapsed = false;
|
|
64
|
+
this.modal = false;
|
|
65
|
+
this.label = "Navigation";
|
|
66
|
+
this._restoreFocusTo = null;
|
|
67
|
+
this._wasOpen = false;
|
|
68
|
+
// Native <dialog> emits `cancel` on Esc. We own the close (and its focus
|
|
69
|
+
// restore + event) and stopPropagation so one Esc never closes an enclosing
|
|
70
|
+
// layer — innermost-only (AD-5a).
|
|
71
|
+
this._onDialogCancel = (event) => {
|
|
72
|
+
event.preventDefault();
|
|
73
|
+
event.stopPropagation();
|
|
74
|
+
this.open = false;
|
|
75
|
+
};
|
|
76
|
+
this._onDialogClose = () => {
|
|
77
|
+
// Reached if the dialog closed outside our API; sync state.
|
|
78
|
+
if (this._wasOpen) {
|
|
79
|
+
this._wasOpen = false;
|
|
80
|
+
this._restoreFocus();
|
|
81
|
+
this.open = false;
|
|
82
|
+
this._emitClose();
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
// Backdrop click closes the modal drawer.
|
|
86
|
+
this._onDialogClick = (event) => {
|
|
87
|
+
if (event.target === this._dialog) {
|
|
88
|
+
this.open = false;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
static { this.shadowRootOptions = {
|
|
93
|
+
...LitElement.shadowRootOptions,
|
|
94
|
+
delegatesFocus: true,
|
|
95
|
+
}; }
|
|
96
|
+
render() {
|
|
97
|
+
const cls = [
|
|
98
|
+
"nav-drawer",
|
|
99
|
+
this.open ? "nav-drawer--open" : "",
|
|
100
|
+
this.collapsed ? "nav-drawer--collapsed" : "",
|
|
101
|
+
this.modal ? "nav-drawer--modal" : "nav-drawer--persistent",
|
|
102
|
+
]
|
|
103
|
+
.filter(Boolean)
|
|
104
|
+
.join(" ");
|
|
105
|
+
const panel = html `
|
|
106
|
+
<nav class="nav-drawer__panel" aria-label=${this.label}>
|
|
107
|
+
<slot></slot>
|
|
108
|
+
</nav>
|
|
109
|
+
`;
|
|
110
|
+
return html `
|
|
111
|
+
<link rel="stylesheet" href="${NAV_DRAWER_CSS}" />
|
|
112
|
+
<style>
|
|
113
|
+
:host {
|
|
114
|
+
display: contents;
|
|
115
|
+
}
|
|
116
|
+
:host([hidden]) {
|
|
117
|
+
display: none;
|
|
118
|
+
}
|
|
119
|
+
</style>
|
|
120
|
+
${this.modal
|
|
121
|
+
? html `
|
|
122
|
+
<dialog
|
|
123
|
+
class="${cls} nav-drawer__dialog"
|
|
124
|
+
aria-label=${this.label}
|
|
125
|
+
@cancel=${this._onDialogCancel}
|
|
126
|
+
@close=${this._onDialogClose}
|
|
127
|
+
@click=${this._onDialogClick}
|
|
128
|
+
>
|
|
129
|
+
${panel}
|
|
130
|
+
</dialog>
|
|
131
|
+
`
|
|
132
|
+
: html `
|
|
133
|
+
<aside class="${cls}" aria-hidden=${this.open ? nothing : "true"}>
|
|
134
|
+
${panel}
|
|
135
|
+
</aside>
|
|
136
|
+
`}
|
|
137
|
+
`;
|
|
138
|
+
}
|
|
139
|
+
updated(changed) {
|
|
140
|
+
if (changed.has("open") || changed.has("modal")) {
|
|
141
|
+
if (this.modal) {
|
|
142
|
+
if (this.open && !this._wasOpen)
|
|
143
|
+
this._activateModal();
|
|
144
|
+
else if (!this.open && this._wasOpen)
|
|
145
|
+
this._deactivateModal();
|
|
146
|
+
}
|
|
147
|
+
else if (!this.open && this._wasOpen) {
|
|
148
|
+
// Switched out of modal while open, or closed persistently.
|
|
149
|
+
this._wasOpen = false;
|
|
150
|
+
}
|
|
151
|
+
else if (this.open) {
|
|
152
|
+
this._wasOpen = true;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
disconnectedCallback() {
|
|
157
|
+
super.disconnectedCallback();
|
|
158
|
+
if (this._wasOpen)
|
|
159
|
+
this._deactivateModal(false);
|
|
160
|
+
}
|
|
161
|
+
// ── Modal activation (native <dialog> contract) ─────────────────────
|
|
162
|
+
_activateModal() {
|
|
163
|
+
this._wasOpen = true;
|
|
164
|
+
this._restoreFocusTo = this._deepActiveElement();
|
|
165
|
+
this.updateComplete.then(() => {
|
|
166
|
+
const dlg = this._dialog;
|
|
167
|
+
if (dlg && !dlg.open && this.open && this.modal) {
|
|
168
|
+
dlg.showModal(); // focus-trap + background inert, for free (AD-5)
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
_deactivateModal(dispatch = true) {
|
|
173
|
+
this._wasOpen = false;
|
|
174
|
+
const dlg = this._dialog;
|
|
175
|
+
if (dlg?.open)
|
|
176
|
+
dlg.close();
|
|
177
|
+
this._restoreFocus();
|
|
178
|
+
if (dispatch)
|
|
179
|
+
this._emitClose();
|
|
180
|
+
}
|
|
181
|
+
// ── close event ─────────────────────────────────────────────────────
|
|
182
|
+
_emitClose() {
|
|
183
|
+
this.dispatchEvent(new CustomEvent("close", { bubbles: true, composed: true }));
|
|
184
|
+
}
|
|
185
|
+
// ── Focus restore (cross-shadow-root) ───────────────────────────────
|
|
186
|
+
_restoreFocus() {
|
|
187
|
+
const target = this._restoreFocusTo;
|
|
188
|
+
this._restoreFocusTo = null;
|
|
189
|
+
if (target && typeof target.focus === "function" && target.isConnected) {
|
|
190
|
+
target.focus();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
_deepActiveElement() {
|
|
194
|
+
let active = document.activeElement;
|
|
195
|
+
while (active?.shadowRoot?.activeElement) {
|
|
196
|
+
active = active.shadowRoot.activeElement;
|
|
197
|
+
}
|
|
198
|
+
return active instanceof HTMLElement ? active : null;
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
__decorate([
|
|
202
|
+
property({ type: Boolean, reflect: true })
|
|
203
|
+
], XmNavigationDrawer.prototype, "open", void 0);
|
|
204
|
+
__decorate([
|
|
205
|
+
property({ type: Boolean, reflect: true })
|
|
206
|
+
], XmNavigationDrawer.prototype, "collapsed", void 0);
|
|
207
|
+
__decorate([
|
|
208
|
+
property({ type: Boolean, reflect: true })
|
|
209
|
+
], XmNavigationDrawer.prototype, "modal", void 0);
|
|
210
|
+
__decorate([
|
|
211
|
+
property({ type: String })
|
|
212
|
+
], XmNavigationDrawer.prototype, "label", void 0);
|
|
213
|
+
__decorate([
|
|
214
|
+
query(".nav-drawer__dialog")
|
|
215
|
+
], XmNavigationDrawer.prototype, "_dialog", void 0);
|
|
216
|
+
XmNavigationDrawer = __decorate([
|
|
217
|
+
customElement("xm-navigation-drawer")
|
|
218
|
+
], XmNavigationDrawer);
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
<xm-overlay> — platform-native overlay foundation.
|
|
3
|
+
|
|
4
|
+
The single surface that xm-dialog / xm-menu / xm-tooltip / xm-select /
|
|
5
|
+
xm-autocomplete compose. Modal mode is a native <dialog> (its ::backdrop
|
|
6
|
+
is the scrim); non-modal mode is a Popover-API element in the top layer,
|
|
7
|
+
anchored with CSS anchor() / @position-try.
|
|
8
|
+
|
|
9
|
+
Surface is the card tier — inverse-surface fill + inverse-on-surface ink
|
|
10
|
+
(AD-13). Elevation level3 for popovers/menus, level4–5 for modals
|
|
11
|
+
(DESIGN.md). Scrim is var(--md-sys-color-scrim) with alpha at this use
|
|
12
|
+
site. No blur / backdrop-filter; no gradient.
|
|
13
|
+
|
|
14
|
+
BEM block: `overlay`. Registered in scripts/check-bem.sh STRICT_BLOCKS.
|
|
15
|
+
Modifiers: --modal / --non-modal · --tier-tooltip / --tier-menu /
|
|
16
|
+
--tier-dialog. Elements: __dialog (native <dialog>), __popover (popover
|
|
17
|
+
element), __surface (the card chrome), __body (content region).
|
|
18
|
+
============================================ */
|
|
19
|
+
|
|
20
|
+
/* ---------- Native <dialog> (modal) ---------- */
|
|
21
|
+
/* The <dialog> element is the positioning + top-layer host; its visible card
|
|
22
|
+
is .overlay__surface inside it. The dialog box itself is transparent so the
|
|
23
|
+
surface owns the chrome. */
|
|
24
|
+
.overlay__dialog {
|
|
25
|
+
margin: auto;
|
|
26
|
+
padding: 0;
|
|
27
|
+
max-width: min(92vw, 560px);
|
|
28
|
+
max-height: 86vh;
|
|
29
|
+
border: none;
|
|
30
|
+
background: transparent;
|
|
31
|
+
color: var(--md-sys-color-inverse-on-surface);
|
|
32
|
+
overflow: visible;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* The scrim — flat token-alpha fill (DESIGN.md: scrim is pure black, alpha
|
|
36
|
+
applied at the callsite). No blur. */
|
|
37
|
+
.overlay__dialog::backdrop {
|
|
38
|
+
background: color-mix(in oklab, var(--md-sys-color-scrim) 48%, transparent);
|
|
39
|
+
animation: overlay-fade var(--md-sys-motion-duration-short4)
|
|
40
|
+
var(--md-sys-motion-easing-standard);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* ---------- Popover (non-modal, anchored) ---------- */
|
|
44
|
+
.overlay__popover {
|
|
45
|
+
margin: 0;
|
|
46
|
+
padding: 0;
|
|
47
|
+
border: none;
|
|
48
|
+
background: transparent;
|
|
49
|
+
color: var(--md-sys-color-inverse-on-surface);
|
|
50
|
+
overflow: visible;
|
|
51
|
+
/* Top-layer popover, positioned against the viewport. anchor() resolves
|
|
52
|
+
against the per-instance position-anchor set inline. @position-try flips
|
|
53
|
+
the popover when it would overflow the viewport. */
|
|
54
|
+
position: fixed;
|
|
55
|
+
position-try-fallbacks: flip-block, flip-inline;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* Native anchor() path — used when the anchor element lives in the SAME tree
|
|
59
|
+
scope as the popover (anchor-name is tree-scoped). data-placement is set by
|
|
60
|
+
the element at open time; inset: auto resets so only the chosen edges bind. */
|
|
61
|
+
.overlay__popover[data-placement^="bottom"] {
|
|
62
|
+
inset: auto;
|
|
63
|
+
top: anchor(bottom);
|
|
64
|
+
left: anchor(left);
|
|
65
|
+
margin-block-start: var(--s-1);
|
|
66
|
+
}
|
|
67
|
+
.overlay__popover[data-placement^="top"] {
|
|
68
|
+
inset: auto;
|
|
69
|
+
bottom: anchor(top);
|
|
70
|
+
left: anchor(left);
|
|
71
|
+
margin-block-end: var(--s-1);
|
|
72
|
+
}
|
|
73
|
+
.overlay__popover[data-placement^="right"] {
|
|
74
|
+
inset: auto;
|
|
75
|
+
left: anchor(right);
|
|
76
|
+
top: anchor(top);
|
|
77
|
+
margin-inline-start: var(--s-1);
|
|
78
|
+
}
|
|
79
|
+
.overlay__popover[data-placement^="left"] {
|
|
80
|
+
inset: auto;
|
|
81
|
+
right: anchor(left);
|
|
82
|
+
top: anchor(top);
|
|
83
|
+
margin-inline-end: var(--s-1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* Cross-root fallback path — when the consumer's trigger is in ANOTHER shadow
|
|
87
|
+
root, CSS anchor-name is NOT visible to this popover (tree-scoped). The
|
|
88
|
+
element computes a fixed top/left from the trigger's bounding rect (no
|
|
89
|
+
dependency) and pins them via these custom properties; anchor() is skipped.
|
|
90
|
+
This is the built-in, dependency-free fallback — Floating UI (the heavier
|
|
91
|
+
escape hatch) is only for deep/virtual cases, never the common cross-root
|
|
92
|
+
one. */
|
|
93
|
+
.overlay__popover.overlay__popover--pinned {
|
|
94
|
+
inset: auto;
|
|
95
|
+
top: var(--xm-overlay-pin-top, 0);
|
|
96
|
+
left: var(--xm-overlay-pin-left, 0);
|
|
97
|
+
position-try-fallbacks: none;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* ---------- The card surface (shared) ---------- */
|
|
101
|
+
.overlay__surface {
|
|
102
|
+
display: flex;
|
|
103
|
+
flex-direction: column;
|
|
104
|
+
min-width: 0;
|
|
105
|
+
box-sizing: border-box;
|
|
106
|
+
border: 1px solid var(--md-sys-color-outline-variant);
|
|
107
|
+
border-radius: var(--md-sys-shape-corner-large);
|
|
108
|
+
background: var(--md-sys-color-inverse-surface);
|
|
109
|
+
color: var(--md-sys-color-inverse-on-surface);
|
|
110
|
+
font:
|
|
111
|
+
var(--md-sys-typescale-body-large-weight)
|
|
112
|
+
var(--md-sys-typescale-body-large-size) /
|
|
113
|
+
var(--md-sys-typescale-body-large-line-height)
|
|
114
|
+
var(--md-sys-typescale-body-large-font);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* Elevation per tier: dialogs lift highest (level5), menus/select-listbox
|
|
118
|
+
sit at popover height (level3), tooltips are the quietest layer (level3
|
|
119
|
+
too — they never cover a menu, but read as a raised chip). */
|
|
120
|
+
.overlay--modal .overlay__surface {
|
|
121
|
+
box-shadow: var(--md-sys-elevation-level5);
|
|
122
|
+
min-width: min(86vw, 320px);
|
|
123
|
+
}
|
|
124
|
+
.overlay--non-modal.overlay--tier-menu .overlay__surface {
|
|
125
|
+
box-shadow: var(--md-sys-elevation-level3);
|
|
126
|
+
min-width: 180px;
|
|
127
|
+
}
|
|
128
|
+
.overlay--non-modal.overlay--tier-dialog .overlay__surface {
|
|
129
|
+
box-shadow: var(--md-sys-elevation-level3);
|
|
130
|
+
}
|
|
131
|
+
.overlay--tier-tooltip .overlay__surface {
|
|
132
|
+
box-shadow: var(--md-sys-elevation-level3);
|
|
133
|
+
border-radius: var(--md-sys-shape-corner-small);
|
|
134
|
+
max-width: 260px;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/* ---------- Content region ---------- */
|
|
138
|
+
.overlay__body {
|
|
139
|
+
flex: 1;
|
|
140
|
+
min-width: 0;
|
|
141
|
+
min-height: 0;
|
|
142
|
+
overflow: auto;
|
|
143
|
+
padding: var(--s-5);
|
|
144
|
+
}
|
|
145
|
+
.overlay--tier-tooltip .overlay__body,
|
|
146
|
+
.overlay--tier-menu .overlay__body {
|
|
147
|
+
padding: var(--s-2);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* ---------- Open transition (short4, standard) ---------- */
|
|
151
|
+
@keyframes overlay-fade {
|
|
152
|
+
from {
|
|
153
|
+
opacity: 0;
|
|
154
|
+
}
|
|
155
|
+
to {
|
|
156
|
+
opacity: 1;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
.overlay__dialog[open],
|
|
160
|
+
.overlay__popover:popover-open {
|
|
161
|
+
animation: overlay-fade var(--md-sys-motion-duration-short4)
|
|
162
|
+
var(--md-sys-motion-easing-standard);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
@media (prefers-reduced-motion: reduce) {
|
|
166
|
+
.overlay__dialog[open],
|
|
167
|
+
.overlay__popover:popover-open,
|
|
168
|
+
.overlay__dialog::backdrop {
|
|
169
|
+
animation: none;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
import type { PropertyValues, TemplateResult } from "lit";
|
|
3
|
+
export type OverlayTier = "tooltip" | "menu" | "dialog";
|
|
4
|
+
export type OverlayMode = "modal" | "non-modal";
|
|
5
|
+
export type OverlayPlacement = "top" | "bottom" | "left" | "right" | "top-start" | "top-end" | "bottom-start" | "bottom-end" | "left-start" | "left-end" | "right-start" | "right-end";
|
|
6
|
+
export type OverlayCloseReason = "escape" | "backdrop" | "api" | "ancestor-closed";
|
|
7
|
+
export interface OverlayCloseDetail {
|
|
8
|
+
tier: OverlayTier;
|
|
9
|
+
mode: OverlayMode;
|
|
10
|
+
reason: OverlayCloseReason;
|
|
11
|
+
}
|
|
12
|
+
export interface OverlayOpenDetail {
|
|
13
|
+
tier: OverlayTier;
|
|
14
|
+
mode: OverlayMode;
|
|
15
|
+
}
|
|
16
|
+
export declare class XmOverlay extends LitElement {
|
|
17
|
+
static shadowRootOptions: ShadowRootInit;
|
|
18
|
+
open: boolean;
|
|
19
|
+
mode: OverlayMode;
|
|
20
|
+
tier: OverlayTier;
|
|
21
|
+
placement: OverlayPlacement;
|
|
22
|
+
label: string;
|
|
23
|
+
/** Trigger element to anchor a non-modal overlay against (CSS anchor()).
|
|
24
|
+
A property, not an attribute — it is an element reference, possibly from
|
|
25
|
+
another shadow root. */
|
|
26
|
+
anchor: HTMLElement | null;
|
|
27
|
+
/** Element focus is restored to on close. Defaults to `anchor`, else the
|
|
28
|
+
document's deepest activeElement at open time. */
|
|
29
|
+
opener: HTMLElement | null;
|
|
30
|
+
private _dialog;
|
|
31
|
+
private _popover;
|
|
32
|
+
private readonly _id;
|
|
33
|
+
private readonly _anchorName;
|
|
34
|
+
private _restoreFocusTo;
|
|
35
|
+
private _scrollLocked;
|
|
36
|
+
private _wasOpen;
|
|
37
|
+
private _activationSeq;
|
|
38
|
+
render(): TemplateResult;
|
|
39
|
+
protected updated(changed: PropertyValues<this>): void;
|
|
40
|
+
disconnectedCallback(): void;
|
|
41
|
+
show(): void;
|
|
42
|
+
hide(reason?: OverlayCloseReason): void;
|
|
43
|
+
toggle(): void;
|
|
44
|
+
private _activate;
|
|
45
|
+
private _deactivate;
|
|
46
|
+
private _onKeydown;
|
|
47
|
+
private _onDialogCancel;
|
|
48
|
+
private _onDialogClose;
|
|
49
|
+
private _onDialogClick;
|
|
50
|
+
private _applyAnchor;
|
|
51
|
+
private _sameTreeScope;
|
|
52
|
+
private _reposition;
|
|
53
|
+
private _pinToAnchor;
|
|
54
|
+
private _lockScroll;
|
|
55
|
+
private _unlockScroll;
|
|
56
|
+
private _restoreFocus;
|
|
57
|
+
private _deepActiveElement;
|
|
58
|
+
private _ownsOpener;
|
|
59
|
+
private _tierZVar;
|
|
60
|
+
}
|
|
61
|
+
declare global {
|
|
62
|
+
interface HTMLElementTagNameMap {
|
|
63
|
+
"xm-overlay": XmOverlay;
|
|
64
|
+
}
|
|
65
|
+
}
|