@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,201 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
Alert — inline notification banner.
|
|
3
|
+
|
|
4
|
+
Lightweight inline surface for notice/warning/success/critical
|
|
5
|
+
context. Sits between snackbar (modal-ish backend-error tunnel)
|
|
6
|
+
and a plain message: the host drops it inline in a chat thread,
|
|
7
|
+
a settings pane, or any content surface.
|
|
8
|
+
|
|
9
|
+
Severity is communicated by the icon glyph and copy ONLY — every
|
|
10
|
+
variant uses the same neutral surface and ink. `--md-sys-color-error*`
|
|
11
|
+
is forbidden in components (POLICIES.md rule 3a); status pastels
|
|
12
|
+
are not part of this design system.
|
|
13
|
+
|
|
14
|
+
Grid:
|
|
15
|
+
icon · body · action-slot · close
|
|
16
|
+
The icon column and close column are auto-sized; the body fills.
|
|
17
|
+
============================================ */
|
|
18
|
+
|
|
19
|
+
.alert {
|
|
20
|
+
/* Component-local vars — neutral surface, border, and ink for every
|
|
21
|
+
severity (matches the rest of the design system). Severity carries
|
|
22
|
+
hue ONLY on the icon and its chip — this is the targeted exception
|
|
23
|
+
to the "no hue for severity" rule, scoped tightly so the alert
|
|
24
|
+
reads as chrome with a glanceable severity marker, not a loud
|
|
25
|
+
full-banner tint. Defaults are info; .alert--{severity} swaps the
|
|
26
|
+
icon vars. */
|
|
27
|
+
--alert-bg: var(--md-sys-color-surface-container-high);
|
|
28
|
+
--alert-border: var(--md-sys-color-outline-variant);
|
|
29
|
+
--alert-ink: var(--md-sys-color-on-surface);
|
|
30
|
+
--alert-ink-soft: var(--md-sys-color-on-surface-variant);
|
|
31
|
+
--alert-icon-ink: var(--xm-alert-info-ink);
|
|
32
|
+
--alert-icon-chip: var(--xm-alert-info-chip);
|
|
33
|
+
|
|
34
|
+
position: relative;
|
|
35
|
+
display: grid;
|
|
36
|
+
grid-template-columns: auto 1fr auto auto;
|
|
37
|
+
align-items: center;
|
|
38
|
+
gap: var(--s-3);
|
|
39
|
+
width: 100%;
|
|
40
|
+
padding: var(--s-3) var(--s-3) var(--s-3) var(--s-4);
|
|
41
|
+
background: var(--alert-bg);
|
|
42
|
+
border: 1px solid var(--alert-border);
|
|
43
|
+
border-radius: var(--md-sys-shape-corner-medium);
|
|
44
|
+
color: var(--alert-ink);
|
|
45
|
+
font-family: var(--md-sys-typescale-body-medium-font);
|
|
46
|
+
animation: alert-in var(--md-sys-motion-duration-short3) var(--md-sys-motion-easing-standard) both;
|
|
47
|
+
}
|
|
48
|
+
.alert--static { animation: none; }
|
|
49
|
+
|
|
50
|
+
@keyframes alert-in {
|
|
51
|
+
from {
|
|
52
|
+
opacity: 0;
|
|
53
|
+
transform: translateY(4px);
|
|
54
|
+
}
|
|
55
|
+
to {
|
|
56
|
+
opacity: 1;
|
|
57
|
+
transform: translateY(0);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* Severity modifiers — swap ONLY the icon's ink and chip. Surface,
|
|
62
|
+
border, and body text stay neutral. */
|
|
63
|
+
.alert--info {
|
|
64
|
+
--alert-icon-ink: var(--xm-alert-info-ink);
|
|
65
|
+
--alert-icon-chip: var(--xm-alert-info-chip);
|
|
66
|
+
}
|
|
67
|
+
.alert--success {
|
|
68
|
+
--alert-icon-ink: var(--xm-alert-success-ink);
|
|
69
|
+
--alert-icon-chip: var(--xm-alert-success-chip);
|
|
70
|
+
}
|
|
71
|
+
.alert--warning {
|
|
72
|
+
--alert-icon-ink: var(--xm-alert-warning-ink);
|
|
73
|
+
--alert-icon-chip: var(--xm-alert-warning-chip);
|
|
74
|
+
}
|
|
75
|
+
.alert--critical {
|
|
76
|
+
--alert-icon-ink: var(--xm-alert-critical-ink);
|
|
77
|
+
--alert-icon-chip: var(--xm-alert-critical-chip);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* ── Prominent / banner modifier (Story 4.6) ───────────────────────────
|
|
81
|
+
A high-importance, persistent layout. Changes PROMINENCE / LAYOUT ONLY:
|
|
82
|
+
wider full-width feel, larger padding, a left severity accent rail, and a
|
|
83
|
+
larger icon chip — while the body surface stays NEUTRAL and the per-severity
|
|
84
|
+
hue stays scoped to the icon chip + ink + the rail (the same --alert-icon-*
|
|
85
|
+
mechanism, the sole sanctioned severity-hue exception). No new severity
|
|
86
|
+
hues, no --md-sys-color-error*, no full-banner tint. The contract (severity
|
|
87
|
+
model, slots, events) is unchanged. */
|
|
88
|
+
.alert--prominent {
|
|
89
|
+
padding: var(--s-5) var(--s-5) var(--s-5) var(--s-6);
|
|
90
|
+
border-radius: var(--md-sys-shape-corner-large);
|
|
91
|
+
/* The left severity rail — a hairline-respecting accent band drawn from the
|
|
92
|
+
per-severity chip, scoped to the edge so the body reads neutral. 3px is a
|
|
93
|
+
decorative rail, not a border (borders stay hairline; 2px is focus/drag). */
|
|
94
|
+
border-left: 3px solid var(--alert-icon-chip);
|
|
95
|
+
column-gap: var(--s-4);
|
|
96
|
+
}
|
|
97
|
+
.alert--prominent .alert__icon {
|
|
98
|
+
width: 40px;
|
|
99
|
+
height: 40px;
|
|
100
|
+
align-self: start;
|
|
101
|
+
}
|
|
102
|
+
.alert--prominent .alert__heading {
|
|
103
|
+
font-size: var(--md-sys-typescale-body-large-size);
|
|
104
|
+
}
|
|
105
|
+
.alert--prominent .alert__text {
|
|
106
|
+
font-size: var(--md-sys-typescale-body-medium-size);
|
|
107
|
+
}
|
|
108
|
+
/* In the banner layout the action drops below the body so the message has the
|
|
109
|
+
full row width — a persistent banner reads as a block, not a one-liner. */
|
|
110
|
+
.alert--prominent .alert__action {
|
|
111
|
+
grid-column: 2 / 3;
|
|
112
|
+
grid-row: 2;
|
|
113
|
+
justify-content: flex-start;
|
|
114
|
+
margin-top: var(--s-1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.alert__icon {
|
|
118
|
+
display: inline-flex;
|
|
119
|
+
align-items: center;
|
|
120
|
+
justify-content: center;
|
|
121
|
+
width: 32px;
|
|
122
|
+
height: 32px;
|
|
123
|
+
border-radius: var(--md-sys-shape-corner-small);
|
|
124
|
+
/* Per-severity tint chip behind the glyph — the ONLY hued region in
|
|
125
|
+
the alert. Sits on top of the neutral alert surface. */
|
|
126
|
+
background: color-mix(in oklab, var(--alert-icon-chip) 38%, transparent);
|
|
127
|
+
color: var(--alert-icon-ink);
|
|
128
|
+
flex-shrink: 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.alert__body {
|
|
132
|
+
min-width: 0;
|
|
133
|
+
display: flex;
|
|
134
|
+
flex-direction: column;
|
|
135
|
+
gap: var(--s-0-5);
|
|
136
|
+
}
|
|
137
|
+
.alert__heading {
|
|
138
|
+
font-size: var(--md-sys-typescale-body-medium-size);
|
|
139
|
+
font-weight: 600;
|
|
140
|
+
color: var(--alert-ink);
|
|
141
|
+
/* Heading wraps when narrow rather than truncating — alerts are read,
|
|
142
|
+
not glanced at like snackbars. */
|
|
143
|
+
text-wrap: pretty;
|
|
144
|
+
}
|
|
145
|
+
.alert__text {
|
|
146
|
+
font-size: var(--md-sys-typescale-body-medium-size);
|
|
147
|
+
line-height: 1.45;
|
|
148
|
+
color: var(--alert-ink-soft);
|
|
149
|
+
text-wrap: pretty;
|
|
150
|
+
}
|
|
151
|
+
.alert__text.is-empty { display: none; }
|
|
152
|
+
.alert__heading.is-empty { display: none; }
|
|
153
|
+
|
|
154
|
+
.alert__action {
|
|
155
|
+
display: inline-flex;
|
|
156
|
+
align-items: center;
|
|
157
|
+
flex-shrink: 0;
|
|
158
|
+
}
|
|
159
|
+
.alert__action.is-empty { display: none; }
|
|
160
|
+
|
|
161
|
+
.alert__close {
|
|
162
|
+
appearance: none;
|
|
163
|
+
border: none;
|
|
164
|
+
background: transparent;
|
|
165
|
+
color: var(--alert-ink-soft);
|
|
166
|
+
width: 28px;
|
|
167
|
+
height: 28px;
|
|
168
|
+
border-radius: var(--md-sys-shape-corner-small);
|
|
169
|
+
cursor: pointer;
|
|
170
|
+
display: inline-flex;
|
|
171
|
+
align-items: center;
|
|
172
|
+
justify-content: center;
|
|
173
|
+
transition:
|
|
174
|
+
background var(--md-sys-motion-duration-short3) var(--md-sys-motion-easing-standard),
|
|
175
|
+
color var(--md-sys-motion-duration-short3) var(--md-sys-motion-easing-standard);
|
|
176
|
+
flex-shrink: 0;
|
|
177
|
+
}
|
|
178
|
+
.alert__close:hover {
|
|
179
|
+
background: color-mix(in oklab, var(--alert-ink) 10%, transparent);
|
|
180
|
+
color: var(--alert-ink);
|
|
181
|
+
}
|
|
182
|
+
.alert__close:focus-visible {
|
|
183
|
+
outline: none;
|
|
184
|
+
box-shadow: var(--xm-state-focus-ring);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* Compact: when the row gets narrow, drop the action below the body
|
|
188
|
+
so nothing wraps awkwardly. The dismiss stays inline. The 520px literal
|
|
189
|
+
mirrors --xm-breakpoint-sm (styles/_layout.css) — @media can't read the
|
|
190
|
+
token (docs/adr/0001), so the literal is the source-of-truth's echo. */
|
|
191
|
+
@media (max-width: 520px) {
|
|
192
|
+
.alert {
|
|
193
|
+
grid-template-columns: auto 1fr auto;
|
|
194
|
+
grid-template-rows: auto auto;
|
|
195
|
+
row-gap: var(--s-2);
|
|
196
|
+
}
|
|
197
|
+
.alert__action {
|
|
198
|
+
grid-column: 2 / -1;
|
|
199
|
+
justify-content: flex-end;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
import type { TemplateResult } from "lit";
|
|
3
|
+
type AlertSeverity = "info" | "success" | "warning" | "critical";
|
|
4
|
+
type AlertVariant = "inline" | "banner";
|
|
5
|
+
declare class XmAlert extends LitElement {
|
|
6
|
+
severity: AlertSeverity;
|
|
7
|
+
heading: string;
|
|
8
|
+
dismissible: boolean;
|
|
9
|
+
static: boolean;
|
|
10
|
+
prominent: boolean;
|
|
11
|
+
variant: AlertVariant;
|
|
12
|
+
private _hasBodyText;
|
|
13
|
+
private _hasAction;
|
|
14
|
+
render(): TemplateResult;
|
|
15
|
+
private _onDefaultSlot;
|
|
16
|
+
private _onActionSlot;
|
|
17
|
+
private _onActionClick;
|
|
18
|
+
private _onDismiss;
|
|
19
|
+
}
|
|
20
|
+
declare global {
|
|
21
|
+
interface HTMLElementTagNameMap {
|
|
22
|
+
"xm-alert": XmAlert;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/*
|
|
2
|
+
alert/index.ts — inline notification banner.
|
|
3
|
+
|
|
4
|
+
<xm-alert> — lightweight banner for notice / warning / success /
|
|
5
|
+
critical context. Sits inline in any content surface.
|
|
6
|
+
|
|
7
|
+
Authoring:
|
|
8
|
+
<xm-alert severity="warning" heading="Rate limit approaching" dismissible>
|
|
9
|
+
You've used 80% of your monthly quota.
|
|
10
|
+
<xm-button slot="action" variant="ghost" size="sm">View usage</xm-button>
|
|
11
|
+
</xm-alert>
|
|
12
|
+
|
|
13
|
+
Properties:
|
|
14
|
+
severity "info" | "success" | "warning" | "critical" (default "info")
|
|
15
|
+
heading short title string (sentence case)
|
|
16
|
+
dismissible boolean — render the trailing × close button
|
|
17
|
+
static boolean — preview-only, suppresses entry animation
|
|
18
|
+
prominent boolean — high-importance persistent BANNER layout. Changes
|
|
19
|
+
PROMINENCE / LAYOUT ONLY (full-width feel, larger padding, a
|
|
20
|
+
left severity accent rail, stacked action). It does NOT change
|
|
21
|
+
the severity model, add severities, rename slots/events, or
|
|
22
|
+
fork the API — the per-severity --xm-alert-* hue stays scoped
|
|
23
|
+
to the icon chip + ink exactly as the inline alert. `variant`
|
|
24
|
+
is an alias: variant="banner" === prominent.
|
|
25
|
+
variant "inline" | "banner" (default "inline"); "banner" === prominent
|
|
26
|
+
|
|
27
|
+
Slots:
|
|
28
|
+
default — body copy
|
|
29
|
+
action — single trailing control (typically <xm-button variant="ghost">)
|
|
30
|
+
|
|
31
|
+
Events:
|
|
32
|
+
alert-dismiss — fired when × is clicked
|
|
33
|
+
alert-action — fired when any element in the action slot fires click
|
|
34
|
+
|
|
35
|
+
Shadow DOM. Loads components/alert/index.css inside the shadow root via
|
|
36
|
+
<link> using the same path-resolution pattern as <xm-button>. Severity
|
|
37
|
+
is communicated by the icon glyph only — every variant uses the same
|
|
38
|
+
neutral surface and ink (POLICIES.md rule 3a forbids `--md-sys-color-error*`).
|
|
39
|
+
*/
|
|
40
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
41
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
42
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
43
|
+
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;
|
|
44
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
45
|
+
};
|
|
46
|
+
import { LitElement, html, svg, nothing } from "lit";
|
|
47
|
+
import { customElement, property } from "lit/decorators.js";
|
|
48
|
+
// Resolve CSS path relative to the *built* file location:
|
|
49
|
+
// `lit/build/components/alert/index.js` — 3 levels up reach `lit/`,
|
|
50
|
+
// then into `components/`. Mirrors button/index.ts.
|
|
51
|
+
const ALERT_CSS = new URL("../alert/index.css", import.meta.url).href;
|
|
52
|
+
const SHELL = (paths, size = 18, strokeWidth = 1.8) => svg `
|
|
53
|
+
<svg width="${size}" height="${size}" viewBox="0 0 24 24"
|
|
54
|
+
fill="none" stroke="currentColor" stroke-width="${strokeWidth}"
|
|
55
|
+
stroke-linecap="round" stroke-linejoin="round" class="ds-icon">
|
|
56
|
+
${paths}
|
|
57
|
+
</svg>
|
|
58
|
+
`;
|
|
59
|
+
const SEVERITY_ICON = {
|
|
60
|
+
info: () => SHELL(svg `
|
|
61
|
+
<circle cx="12" cy="12" r="9" />
|
|
62
|
+
<path d="M12 11v5" />
|
|
63
|
+
<path d="M12 8h.01" />
|
|
64
|
+
`),
|
|
65
|
+
success: () => SHELL(svg `
|
|
66
|
+
<circle cx="12" cy="12" r="9" />
|
|
67
|
+
<polyline points="8.5 12.5 11 15 15.5 9.5" />
|
|
68
|
+
`),
|
|
69
|
+
warning: () => SHELL(svg `
|
|
70
|
+
<path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
|
|
71
|
+
<path d="M12 9v4" />
|
|
72
|
+
<path d="M12 17h.01" />
|
|
73
|
+
`, 18, 2),
|
|
74
|
+
critical: () => SHELL(svg `
|
|
75
|
+
<polygon points="7.86 2 16.14 2 22 7.86 22 16.14 16.14 22 7.86 22 2 16.14 2 7.86" />
|
|
76
|
+
<path d="M9 9l6 6M15 9l-6 6" />
|
|
77
|
+
`, 18, 2),
|
|
78
|
+
};
|
|
79
|
+
const CLOSE_ICON = () => SHELL(svg `
|
|
80
|
+
<path d="M6 6l12 12M18 6L6 18" />
|
|
81
|
+
`, 14, 2);
|
|
82
|
+
const SEVERITY_LABEL = {
|
|
83
|
+
info: "Information",
|
|
84
|
+
success: "Success",
|
|
85
|
+
warning: "Warning",
|
|
86
|
+
critical: "Critical",
|
|
87
|
+
};
|
|
88
|
+
let XmAlert = class XmAlert extends LitElement {
|
|
89
|
+
constructor() {
|
|
90
|
+
super(...arguments);
|
|
91
|
+
this.severity = "info";
|
|
92
|
+
this.heading = "";
|
|
93
|
+
this.dismissible = false;
|
|
94
|
+
this.static = false;
|
|
95
|
+
this.prominent = false;
|
|
96
|
+
this.variant = "inline";
|
|
97
|
+
this._hasBodyText = true;
|
|
98
|
+
this._hasAction = false;
|
|
99
|
+
this._onDefaultSlot = (e) => {
|
|
100
|
+
const slot = e.target;
|
|
101
|
+
const nodes = slot.assignedNodes({ flatten: true });
|
|
102
|
+
const has = nodes.some((n) => {
|
|
103
|
+
if (n.nodeType === Node.TEXT_NODE)
|
|
104
|
+
return !!n.textContent?.trim();
|
|
105
|
+
if (n.nodeType === Node.ELEMENT_NODE)
|
|
106
|
+
return true;
|
|
107
|
+
return false;
|
|
108
|
+
});
|
|
109
|
+
if (has !== this._hasBodyText) {
|
|
110
|
+
this._hasBodyText = has;
|
|
111
|
+
this.requestUpdate();
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
this._onActionSlot = (e) => {
|
|
115
|
+
const slot = e.target;
|
|
116
|
+
const has = slot.assignedElements().length > 0;
|
|
117
|
+
if (has !== this._hasAction) {
|
|
118
|
+
this._hasAction = has;
|
|
119
|
+
this.requestUpdate();
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
this._onActionClick = () => {
|
|
123
|
+
this.dispatchEvent(new CustomEvent("alert-action", { bubbles: true, composed: true }));
|
|
124
|
+
};
|
|
125
|
+
this._onDismiss = () => {
|
|
126
|
+
this.dispatchEvent(new CustomEvent("alert-dismiss", { bubbles: true, composed: true }));
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
render() {
|
|
130
|
+
const sev = this.severity === "success" || this.severity === "warning" || this.severity === "critical"
|
|
131
|
+
? this.severity
|
|
132
|
+
: "info";
|
|
133
|
+
const isProminent = this.prominent || this.variant === "banner";
|
|
134
|
+
const cls = `alert alert--${sev}${this.static ? " alert--static" : ""}${isProminent ? " alert--prominent" : ""}`;
|
|
135
|
+
return html `
|
|
136
|
+
<link rel="stylesheet" href="${ALERT_CSS}">
|
|
137
|
+
<style>
|
|
138
|
+
:host { display: block; }
|
|
139
|
+
:host([hidden]) { display: none; }
|
|
140
|
+
</style>
|
|
141
|
+
<div
|
|
142
|
+
class="${cls}"
|
|
143
|
+
role="${sev === "critical" || sev === "warning" ? "alert" : "status"}"
|
|
144
|
+
aria-label="${SEVERITY_LABEL[sev]}"
|
|
145
|
+
>
|
|
146
|
+
<span class="alert__icon" aria-hidden="true">${SEVERITY_ICON[sev]()}</span>
|
|
147
|
+
<div class="alert__body">
|
|
148
|
+
<span class="alert__heading ${this.heading ? "" : "is-empty"}">${this.heading}</span>
|
|
149
|
+
<span class="alert__text ${this._hasBodyText ? "" : "is-empty"}"
|
|
150
|
+
><slot @slotchange=${this._onDefaultSlot}></slot></span>
|
|
151
|
+
</div>
|
|
152
|
+
<span class="alert__action ${this._hasAction ? "" : "is-empty"}"
|
|
153
|
+
><slot
|
|
154
|
+
name="action"
|
|
155
|
+
@slotchange=${this._onActionSlot}
|
|
156
|
+
@click=${this._onActionClick}
|
|
157
|
+
></slot></span>
|
|
158
|
+
${this.dismissible
|
|
159
|
+
? html `
|
|
160
|
+
<button
|
|
161
|
+
type="button"
|
|
162
|
+
class="alert__close"
|
|
163
|
+
aria-label="Dismiss"
|
|
164
|
+
@click=${this._onDismiss}
|
|
165
|
+
>${CLOSE_ICON()}</button>`
|
|
166
|
+
: nothing}
|
|
167
|
+
</div>
|
|
168
|
+
`;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
__decorate([
|
|
172
|
+
property({ type: String })
|
|
173
|
+
], XmAlert.prototype, "severity", void 0);
|
|
174
|
+
__decorate([
|
|
175
|
+
property({ type: String })
|
|
176
|
+
], XmAlert.prototype, "heading", void 0);
|
|
177
|
+
__decorate([
|
|
178
|
+
property({ type: Boolean })
|
|
179
|
+
], XmAlert.prototype, "dismissible", void 0);
|
|
180
|
+
__decorate([
|
|
181
|
+
property({ type: Boolean })
|
|
182
|
+
], XmAlert.prototype, "static", void 0);
|
|
183
|
+
__decorate([
|
|
184
|
+
property({ type: Boolean })
|
|
185
|
+
], XmAlert.prototype, "prominent", void 0);
|
|
186
|
+
__decorate([
|
|
187
|
+
property({ type: String })
|
|
188
|
+
], XmAlert.prototype, "variant", void 0);
|
|
189
|
+
XmAlert = __decorate([
|
|
190
|
+
customElement("xm-alert")
|
|
191
|
+
], XmAlert);
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
<xm-app-bar> — screen header on the desk surface.
|
|
3
|
+
|
|
4
|
+
Surface / ink (AD-13): the app bar belongs to the DESK family —
|
|
5
|
+
background var(--md-sys-color-surface), title ink var(--md-sys-color-on-surface),
|
|
6
|
+
secondary var(--md-sys-color-on-surface-variant). NOT the inverse-surface ink
|
|
7
|
+
used by cards / drawers.
|
|
8
|
+
|
|
9
|
+
Hairline: a single 1px bottom border (--md-sys-color-outline-variant).
|
|
10
|
+
Never 2px.
|
|
11
|
+
|
|
12
|
+
BEM: block `app-bar`; elements `__leading` `__title` `__action`;
|
|
13
|
+
modifiers `--xs|--sm|--md|--lg`. Sticky is driven from :host([sticky]).
|
|
14
|
+
============================================ */
|
|
15
|
+
|
|
16
|
+
.app-bar {
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
gap: var(--s-3);
|
|
20
|
+
box-sizing: border-box;
|
|
21
|
+
width: 100%;
|
|
22
|
+
padding: 0 var(--s-4);
|
|
23
|
+
background: var(--md-sys-color-surface);
|
|
24
|
+
color: var(--md-sys-color-on-surface);
|
|
25
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* ---------- Sizes — one shared bar height per size ---------- */
|
|
29
|
+
.app-bar--xs { min-height: 40px; }
|
|
30
|
+
.app-bar--sm { min-height: 48px; }
|
|
31
|
+
.app-bar--md { min-height: 56px; }
|
|
32
|
+
.app-bar--lg { min-height: 64px; }
|
|
33
|
+
|
|
34
|
+
/* ---------- Leading ---------- */
|
|
35
|
+
.app-bar__leading {
|
|
36
|
+
display: inline-flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
gap: var(--s-2);
|
|
39
|
+
flex-shrink: 0;
|
|
40
|
+
}
|
|
41
|
+
.app-bar__leading.is-empty {
|
|
42
|
+
display: none;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* ---------- Title ---------- */
|
|
46
|
+
.app-bar__title {
|
|
47
|
+
flex: 1 1 auto;
|
|
48
|
+
min-width: 0;
|
|
49
|
+
display: flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
gap: var(--s-2);
|
|
52
|
+
font-family: var(--md-sys-typescale-title-medium-font);
|
|
53
|
+
font-size: var(--md-sys-typescale-title-medium-size);
|
|
54
|
+
line-height: var(--md-sys-typescale-title-medium-line-height);
|
|
55
|
+
font-weight: var(--md-sys-typescale-title-medium-weight);
|
|
56
|
+
letter-spacing: var(--md-sys-typescale-title-medium-tracking);
|
|
57
|
+
color: var(--md-sys-color-on-surface);
|
|
58
|
+
overflow: hidden;
|
|
59
|
+
white-space: nowrap;
|
|
60
|
+
text-overflow: ellipsis;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* ---------- Action (singular trailing region) ---------- */
|
|
64
|
+
.app-bar__action {
|
|
65
|
+
display: inline-flex;
|
|
66
|
+
align-items: center;
|
|
67
|
+
gap: var(--s-2);
|
|
68
|
+
flex-shrink: 0;
|
|
69
|
+
margin-inline-start: auto;
|
|
70
|
+
}
|
|
71
|
+
.app-bar__action.is-empty {
|
|
72
|
+
display: none;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* ---------- Sticky ---------- */
|
|
76
|
+
:host([sticky]) .app-bar {
|
|
77
|
+
position: sticky;
|
|
78
|
+
top: 0;
|
|
79
|
+
z-index: 1;
|
|
80
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
import type { TemplateResult } from "lit";
|
|
3
|
+
type AppBarSize = "xs" | "sm" | "md" | "lg";
|
|
4
|
+
declare class XmAppBar extends LitElement {
|
|
5
|
+
size: AppBarSize;
|
|
6
|
+
sticky: boolean;
|
|
7
|
+
private _mo;
|
|
8
|
+
render(): TemplateResult;
|
|
9
|
+
private _slotEmpty;
|
|
10
|
+
private _onSlot;
|
|
11
|
+
connectedCallback(): void;
|
|
12
|
+
disconnectedCallback(): void;
|
|
13
|
+
}
|
|
14
|
+
declare global {
|
|
15
|
+
interface HTMLElementTagNameMap {
|
|
16
|
+
"xm-app-bar": XmAppBar;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/*
|
|
2
|
+
app-bar/index.ts — <xm-app-bar>, the screen header.
|
|
3
|
+
|
|
4
|
+
<xm-app-bar> — a top bar with leading, title, and trailing-action regions.
|
|
5
|
+
Belongs to the DESK surface family (surface*), unlike the card / drawer
|
|
6
|
+
(inverse-surface). Title ink is on-surface — a mismatched inverse ink is a
|
|
7
|
+
dark-on-dark bug one theme hides (AD-13).
|
|
8
|
+
|
|
9
|
+
Authoring:
|
|
10
|
+
<xm-app-bar size="md" sticky>
|
|
11
|
+
<xm-button slot="leading" variant="ghost" icon-only>…</xm-button>
|
|
12
|
+
Routing pipeline
|
|
13
|
+
<xm-button slot="action" variant="ghost" size="sm">Share</xm-button>
|
|
14
|
+
<xm-button slot="action" variant="primary" size="sm">Run</xm-button>
|
|
15
|
+
</xm-app-bar>
|
|
16
|
+
|
|
17
|
+
Properties:
|
|
18
|
+
size "xs" | "sm" | "md" | "lg" (default "md") — shared bar height
|
|
19
|
+
sticky boolean (reflected) — position: sticky; top: 0
|
|
20
|
+
|
|
21
|
+
Slots:
|
|
22
|
+
leading leading region (menu toggle / brand)
|
|
23
|
+
default title (sentence case; tech identifiers stay caps)
|
|
24
|
+
action SINGULAR trailing-action region (multiple buttons as children)
|
|
25
|
+
|
|
26
|
+
Shadow DOM; Lit from lit; sibling CSS via the
|
|
27
|
+
built-file-relative new URL(...). BEM block `app-bar`.
|
|
28
|
+
*/
|
|
29
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
30
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
31
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
32
|
+
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;
|
|
33
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
34
|
+
};
|
|
35
|
+
import { LitElement, html } from "lit";
|
|
36
|
+
import { customElement, property } from "lit/decorators.js";
|
|
37
|
+
// Resolve CSS relative to the *built* file:
|
|
38
|
+
// lit/build/components/app-bar/index.js → ../app-bar/index.css.
|
|
39
|
+
const APP_BAR_CSS = new URL("../app-bar/index.css", import.meta.url).href;
|
|
40
|
+
let XmAppBar = class XmAppBar extends LitElement {
|
|
41
|
+
constructor() {
|
|
42
|
+
super(...arguments);
|
|
43
|
+
this.size = "md";
|
|
44
|
+
this.sticky = false;
|
|
45
|
+
this._mo = null;
|
|
46
|
+
this._onSlot = () => {
|
|
47
|
+
this.requestUpdate();
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
render() {
|
|
51
|
+
const size = ["xs", "sm", "md", "lg"].includes(this.size)
|
|
52
|
+
? this.size
|
|
53
|
+
: "md";
|
|
54
|
+
const leadingEmpty = this._slotEmpty("leading");
|
|
55
|
+
const actionEmpty = this._slotEmpty("action");
|
|
56
|
+
return html `
|
|
57
|
+
<link rel="stylesheet" href="${APP_BAR_CSS}" />
|
|
58
|
+
<style>
|
|
59
|
+
:host {
|
|
60
|
+
display: block;
|
|
61
|
+
}
|
|
62
|
+
:host([hidden]) {
|
|
63
|
+
display: none;
|
|
64
|
+
}
|
|
65
|
+
</style>
|
|
66
|
+
<header class="app-bar app-bar--${size}">
|
|
67
|
+
<div class="app-bar__leading ${leadingEmpty ? "is-empty" : ""}">
|
|
68
|
+
<slot name="leading" @slotchange=${this._onSlot}></slot>
|
|
69
|
+
</div>
|
|
70
|
+
<div class="app-bar__title">
|
|
71
|
+
<slot @slotchange=${this._onSlot}></slot>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="app-bar__action ${actionEmpty ? "is-empty" : ""}">
|
|
74
|
+
<slot name="action" @slotchange=${this._onSlot}></slot>
|
|
75
|
+
</div>
|
|
76
|
+
</header>
|
|
77
|
+
`;
|
|
78
|
+
}
|
|
79
|
+
_slotEmpty(name) {
|
|
80
|
+
for (const node of this.childNodes) {
|
|
81
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
82
|
+
const slotName = node.getAttribute("slot");
|
|
83
|
+
const target = name || null;
|
|
84
|
+
const matches = (target === null && !slotName) || slotName === target;
|
|
85
|
+
if (matches)
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
else if (node.nodeType === Node.TEXT_NODE && !name) {
|
|
89
|
+
if (node.textContent?.trim())
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
connectedCallback() {
|
|
96
|
+
super.connectedCallback();
|
|
97
|
+
if (!this._mo) {
|
|
98
|
+
this._mo = new MutationObserver(() => this.requestUpdate());
|
|
99
|
+
this._mo.observe(this, {
|
|
100
|
+
childList: true,
|
|
101
|
+
characterData: true,
|
|
102
|
+
subtree: true,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
disconnectedCallback() {
|
|
107
|
+
super.disconnectedCallback();
|
|
108
|
+
this._mo?.disconnect();
|
|
109
|
+
this._mo = null;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
__decorate([
|
|
113
|
+
property({ type: String })
|
|
114
|
+
], XmAppBar.prototype, "size", void 0);
|
|
115
|
+
__decorate([
|
|
116
|
+
property({ type: Boolean, reflect: true })
|
|
117
|
+
], XmAppBar.prototype, "sticky", void 0);
|
|
118
|
+
XmAppBar = __decorate([
|
|
119
|
+
customElement("xm-app-bar")
|
|
120
|
+
], XmAppBar);
|