@antadesign/anta 0.3.2 → 0.3.4
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/dist/anta_helpers.d.ts +97 -0
- package/dist/anta_helpers.js +70 -0
- package/dist/calendar-core.d.ts +126 -0
- package/dist/calendar-core.js +289 -0
- package/dist/components/Button.d.ts +9 -1
- package/dist/components/Button.js +5 -2
- package/dist/components/Calendar.d.ts +85 -0
- package/dist/components/Calendar.js +226 -0
- package/dist/components/Checkbox.d.ts +16 -11
- package/dist/components/Checkbox.js +13 -7
- package/dist/components/Expander.d.ts +7 -1
- package/dist/components/Expander.js +4 -2
- package/dist/components/Input.d.ts +13 -5
- package/dist/components/Input.js +14 -6
- package/dist/components/InputDate.d.ts +89 -0
- package/dist/components/InputDate.js +273 -0
- package/dist/components/InputDate.module.css +1 -0
- package/dist/components/Menu.d.ts +14 -1
- package/dist/components/Menu.js +6 -2
- package/dist/components/MenuItem.d.ts +48 -7
- package/dist/components/MenuItem.js +72 -14
- package/dist/components/MenuSeparator.d.ts +13 -2
- package/dist/components/MenuSeparator.js +12 -2
- package/dist/components/Progress.d.ts +4 -1
- package/dist/components/Progress.js +4 -3
- package/dist/components/RadioGroup.d.ts +12 -10
- package/dist/components/RadioGroup.js +5 -5
- package/dist/components/Select.d.ts +319 -0
- package/dist/components/Select.js +341 -0
- package/dist/components/Select.module.css +1 -0
- package/dist/components/Tab.d.ts +3 -0
- package/dist/components/Tab.js +2 -1
- package/dist/components/TabPanel.d.ts +21 -13
- package/dist/components/TabPanel.js +13 -1
- package/dist/components/Tabs.d.ts +75 -40
- package/dist/components/Tabs.js +20 -88
- package/dist/components/Tag.d.ts +10 -9
- package/dist/components/Tag.js +2 -2
- package/dist/components/Text.d.ts +6 -5
- package/dist/components/Title.d.ts +4 -2
- package/dist/components/Tooltip.d.ts +4 -1
- package/dist/components/Tooltip.js +5 -0
- package/dist/elements/a-button.css +1 -1
- package/dist/elements/a-calendar.css +1 -0
- package/dist/elements/a-calendar.d.ts +76 -0
- package/dist/elements/a-calendar.js +190 -0
- package/dist/elements/a-checkbox.css +1 -1
- package/dist/elements/a-checkbox.d.ts +1 -2
- package/dist/elements/a-checkbox.js +5 -5
- package/dist/elements/a-expander.css +1 -1
- package/dist/elements/a-expander.d.ts +16 -5
- package/dist/elements/a-expander.js +59 -8
- package/dist/elements/a-icon.shapes.css +1 -1
- package/dist/elements/a-icon.shapes.d.ts +6 -1
- package/dist/elements/a-icon.shapes.js +9 -0
- package/dist/elements/a-input.css +1 -1
- package/dist/elements/a-input.d.ts +6 -0
- package/dist/elements/a-input.js +28 -8
- package/dist/elements/a-menu-group.css +1 -1
- package/dist/elements/a-menu-item.css +1 -1
- package/dist/elements/a-menu-item.d.ts +22 -10
- package/dist/elements/a-menu-item.js +17 -0
- package/dist/elements/a-menu-separator.css +1 -1
- package/dist/elements/a-menu.css +1 -1
- package/dist/elements/a-menu.d.ts +61 -20
- package/dist/elements/a-menu.js +337 -58
- package/dist/elements/a-progress.css +1 -1
- package/dist/elements/a-radio-group.d.ts +1 -3
- package/dist/elements/a-radio-group.js +13 -13
- package/dist/elements/a-radio.css +1 -1
- package/dist/elements/a-radio.d.ts +3 -11
- package/dist/elements/a-radio.js +3 -34
- package/dist/elements/a-tab.css +1 -1
- package/dist/elements/a-tab.d.ts +3 -11
- package/dist/elements/a-tab.js +3 -34
- package/dist/elements/a-tabpanel.css +1 -1
- package/dist/elements/a-tabpanel.d.ts +17 -0
- package/dist/elements/a-tabpanel.js +66 -0
- package/dist/elements/a-tabs.css +1 -1
- package/dist/elements/a-tabs.d.ts +1 -4
- package/dist/elements/a-tabs.js +14 -14
- package/dist/elements/a-tag.css +1 -1
- package/dist/elements/a-text.d.ts +14 -13
- package/dist/elements/a-text.js +53 -23
- package/dist/elements/a-tooltip.css +1 -1
- package/dist/elements/a-tooltip.d.ts +1 -11
- package/dist/elements/a-tooltip.js +20 -19
- package/dist/elements/index.d.ts +2 -1
- package/dist/elements/index.js +6 -1
- package/dist/general_types.d.ts +158 -42
- package/dist/index.d.ts +10 -4
- package/dist/index.js +38 -3
- package/dist/jsx-runtime.d.ts +16 -7
- package/dist/jsx-runtime.js +6 -0
- package/dist/reset.css +1 -1
- package/package.json +4 -2
package/dist/elements/a-menu.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HTMLElementBase } from "../anta_helpers";
|
|
1
|
+
import { HTMLElementBase, anchorRect, setMenuPresence } from "../anta_helpers";
|
|
2
2
|
import { AMenuItemElement } from "./a-menu-item";
|
|
3
3
|
import "./a-menu.css";
|
|
4
4
|
const MARGIN = 4;
|
|
@@ -42,11 +42,13 @@ function trackPosition(el, onEscape) {
|
|
|
42
42
|
const vw = doc.documentElement.clientWidth;
|
|
43
43
|
const vh = doc.documentElement.clientHeight;
|
|
44
44
|
const rootMargin = `${-rect.top}px ${-(vw - rect.right)}px ${-(vh - rect.bottom)}px ${-rect.left}px`;
|
|
45
|
+
const win = doc.defaultView;
|
|
46
|
+
const root = win && win !== win.top ? doc.documentElement : null;
|
|
45
47
|
const io = new IntersectionObserver(
|
|
46
48
|
([entry]) => {
|
|
47
49
|
if (!entry.isIntersecting) onEscape();
|
|
48
50
|
},
|
|
49
|
-
{ root
|
|
51
|
+
{ root, rootMargin, threshold: ANCHOR_VISIBLE_RATIO }
|
|
50
52
|
);
|
|
51
53
|
io.observe(el);
|
|
52
54
|
return () => io.disconnect();
|
|
@@ -62,6 +64,18 @@ function pathHitsMenus(e, primaryClick = false) {
|
|
|
62
64
|
}
|
|
63
65
|
return false;
|
|
64
66
|
}
|
|
67
|
+
function nodeHitsMenus(node) {
|
|
68
|
+
for (const m of openStack) {
|
|
69
|
+
if (m.contains(node) || m.surface.contains(node)) return true;
|
|
70
|
+
const anchor = m.triggerAnchor;
|
|
71
|
+
if (anchor && anchor.contains(node)) return true;
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
setMenuPresence({
|
|
76
|
+
isOpen: () => openStack.length > 0,
|
|
77
|
+
contains: nodeHitsMenus
|
|
78
|
+
});
|
|
65
79
|
function onDocPointerDown(e) {
|
|
66
80
|
if (!openStack.length) return;
|
|
67
81
|
if (!pathHitsMenus(e, e.button === 0)) dismiss(e);
|
|
@@ -111,6 +125,8 @@ class AMenuElement extends HTMLElementBase {
|
|
|
111
125
|
static observedAttributes = ["placement", "context", "coord", "offset", "nohover", "state"];
|
|
112
126
|
/** Shadow-internal popover surface — the only thing we ever mutate. */
|
|
113
127
|
surface;
|
|
128
|
+
/** The scrolling body inside the surface (holds the items). */
|
|
129
|
+
scrollEl;
|
|
114
130
|
listening = false;
|
|
115
131
|
_shown = false;
|
|
116
132
|
teardown;
|
|
@@ -118,14 +134,32 @@ class AMenuElement extends HTMLElementBase {
|
|
|
118
134
|
* but stays visible until the consumer flips `state`. The flag lets the
|
|
119
135
|
* `closeAll` backstop skip a duplicate emit. Cleared on every show. */
|
|
120
136
|
_dismissNotified = false;
|
|
137
|
+
// Custom-state carrier — exposes the menu's own open state as `:state(open)`
|
|
138
|
+
// (off-DOM, like `a-menu-item`'s `:state(active)`). Never used to mutate light
|
|
139
|
+
// DOM; see `reflectOpen`.
|
|
140
|
+
internals;
|
|
121
141
|
// Submenu hover-intent timers.
|
|
122
142
|
openTimer;
|
|
123
143
|
closeTimer;
|
|
124
144
|
// Typeahead state (root navigation).
|
|
125
145
|
typeBuffer = "";
|
|
126
146
|
typeTimer;
|
|
147
|
+
// Combobox (filter) state — engaged when a `[data-menu-search]` field is slotted
|
|
148
|
+
// in (e.g. `Select` with `filter`). Focus stays in that field; ArrowUp/Down move
|
|
149
|
+
// `activeItem` (a cursor, not DOM focus) and REPORT it via the `activedescendant`
|
|
150
|
+
// event, which the reactive layer reflects onto the field's `aria-activedescendant`.
|
|
151
|
+
activeItem = null;
|
|
152
|
+
comboObserver;
|
|
153
|
+
// The vertical side chosen at open (true = flipped above the anchor). A re-anchor
|
|
154
|
+
// (filtering changes height) keeps this side rather than re-deciding — a shrunk
|
|
155
|
+
// menu shouldn't hop back under the trigger.
|
|
156
|
+
_flippedTop = null;
|
|
127
157
|
constructor() {
|
|
128
158
|
super();
|
|
159
|
+
try {
|
|
160
|
+
this.internals = this.attachInternals?.();
|
|
161
|
+
} catch {
|
|
162
|
+
}
|
|
129
163
|
const shadow = this.attachShadow({ mode: "open" });
|
|
130
164
|
const style = document.createElement("style");
|
|
131
165
|
style.textContent = `
|
|
@@ -136,16 +170,13 @@ class AMenuElement extends HTMLElementBase {
|
|
|
136
170
|
left: 0;
|
|
137
171
|
top: 0;
|
|
138
172
|
margin: 0;
|
|
173
|
+
padding: 0;
|
|
139
174
|
box-sizing: border-box;
|
|
140
175
|
flex-direction: column;
|
|
141
|
-
|
|
142
|
-
min-width: var(--menu-min-width, 88px);
|
|
176
|
+
min-width: max(var(--menu-min-width, 88px), var(--_anchor-width, 0px));
|
|
143
177
|
max-width: calc(100vw - ${2 * MARGIN}px);
|
|
144
178
|
max-height: calc(100dvh - ${2 * MARGIN}px);
|
|
145
|
-
overflow
|
|
146
|
-
overscroll-behavior: contain;
|
|
147
|
-
scrollbar-width: thin;
|
|
148
|
-
padding: var(--menu-padding, 4px);
|
|
179
|
+
overflow: hidden;
|
|
149
180
|
background: var(--menu-bg, Canvas);
|
|
150
181
|
color: var(--text-2, CanvasText);
|
|
151
182
|
border: var(--menu-border, 1px solid);
|
|
@@ -184,19 +215,58 @@ class AMenuElement extends HTMLElementBase {
|
|
|
184
215
|
@starting-style {
|
|
185
216
|
.container:popover-open { opacity: 0; translate: 0 -4px; }
|
|
186
217
|
}
|
|
218
|
+
/* The scrolling body \u2014 only the items scroll; the header / footer slots stay
|
|
219
|
+
fixed above and below it. min-height:0 lets it shrink so its own scrollbar
|
|
220
|
+
engages instead of the flex item overflowing. The 1px inter-item gap that
|
|
221
|
+
used to live on the container lives here now. */
|
|
222
|
+
.scroll {
|
|
223
|
+
flex: 1 1 auto;
|
|
224
|
+
min-height: 0;
|
|
225
|
+
display: flex;
|
|
226
|
+
flex-direction: column;
|
|
227
|
+
gap: 1px;
|
|
228
|
+
overflow-y: auto;
|
|
229
|
+
overscroll-behavior: contain;
|
|
230
|
+
scrollbar-width: thin;
|
|
231
|
+
/* The menu's padding lives here (not on the clipping container) so an item's
|
|
232
|
+
focus outline has room inside the scroll box instead of being clipped at
|
|
233
|
+
the edge; scroll-padding keeps a focused row off the edge when scrolled. */
|
|
234
|
+
padding: var(--menu-padding, 4px);
|
|
235
|
+
scroll-padding-block: var(--menu-padding, 4px);
|
|
236
|
+
/* Soft scroll edges: fade the content into the top / bottom only on the side
|
|
237
|
+
with more to scroll (the --fade-* vars, set from JS, are 0 otherwise). The
|
|
238
|
+
top also clears a hard --gap-top band (fully transparent, ahead of the ramp)
|
|
239
|
+
so a row scrolling up disappears cleanly under a pinned header / filter field
|
|
240
|
+
before the fade begins, instead of bleeding right against it. */
|
|
241
|
+
--fade-top: 0px;
|
|
242
|
+
--fade-bottom: 0px;
|
|
243
|
+
--gap-top: 0px;
|
|
244
|
+
-webkit-mask-image: linear-gradient(to bottom, transparent 0, transparent var(--gap-top), #000 var(--fade-top), #000 calc(100% - var(--fade-bottom)), transparent 100%);
|
|
245
|
+
mask-image: linear-gradient(to bottom, transparent 0, transparent var(--gap-top), #000 var(--fade-top), #000 calc(100% - var(--fade-bottom)), transparent 100%);
|
|
246
|
+
}
|
|
187
247
|
`;
|
|
188
248
|
this.surface = document.createElement("div");
|
|
189
249
|
this.surface.className = "container";
|
|
190
250
|
this.surface.setAttribute("part", "menu");
|
|
191
251
|
this.surface.setAttribute("popover", "manual");
|
|
192
|
-
|
|
252
|
+
const header = document.createElement("slot");
|
|
253
|
+
header.setAttribute("name", "header");
|
|
254
|
+
const scroll = document.createElement("div");
|
|
255
|
+
scroll.className = "scroll";
|
|
256
|
+
scroll.setAttribute("part", "scroll");
|
|
257
|
+
scroll.append(document.createElement("slot"));
|
|
258
|
+
scroll.addEventListener("scroll", () => this.updateScrollFade(), { passive: true });
|
|
259
|
+
this.scrollEl = scroll;
|
|
260
|
+
const footer = document.createElement("slot");
|
|
261
|
+
footer.setAttribute("name", "footer");
|
|
262
|
+
this.surface.append(header, scroll, footer);
|
|
193
263
|
this.surface.addEventListener("pointerenter", (e) => {
|
|
194
264
|
if (e.pointerType !== "mouse") return;
|
|
195
265
|
if (this.isSubmenu) this.cancelCloseTimer();
|
|
196
266
|
});
|
|
197
267
|
this.surface.addEventListener("pointerleave", (e) => {
|
|
198
268
|
if (e.pointerType !== "mouse") return;
|
|
199
|
-
if (this.isSubmenu && this
|
|
269
|
+
if (this.isSubmenu && this.#isHover) this.scheduleClose();
|
|
200
270
|
});
|
|
201
271
|
this.surface.addEventListener("click", this.onSurfaceClick);
|
|
202
272
|
shadow.append(style, this.surface);
|
|
@@ -248,22 +318,22 @@ class AMenuElement extends HTMLElementBase {
|
|
|
248
318
|
get isSubmenu() {
|
|
249
319
|
return !!this.closest("a-menu-item");
|
|
250
320
|
}
|
|
251
|
-
get isContext() {
|
|
321
|
+
get #isContext() {
|
|
252
322
|
return this.hasAttribute("context");
|
|
253
323
|
}
|
|
254
|
-
get isCoord() {
|
|
324
|
+
get #isCoord() {
|
|
255
325
|
return this.hasAttribute("coord");
|
|
256
326
|
}
|
|
257
327
|
// Submenus open on hover by default; `nohover` opts out (click-only). Root
|
|
258
328
|
// menus never consult this — it's read only on the submenu paths.
|
|
259
|
-
get isHover() {
|
|
329
|
+
get #isHover() {
|
|
260
330
|
return !this.hasAttribute("nohover");
|
|
261
331
|
}
|
|
262
|
-
get offset() {
|
|
332
|
+
get #offset() {
|
|
263
333
|
const n = parseInt(this.getAttribute("offset") ?? "", 10);
|
|
264
334
|
return Number.isFinite(n) ? n : MARGIN;
|
|
265
335
|
}
|
|
266
|
-
get placement() {
|
|
336
|
+
get #placement() {
|
|
267
337
|
const p = this.getAttribute("placement");
|
|
268
338
|
if (p === "bottom-end" || p === "top-start" || p === "top-end" || p === "bottom" || p === "top")
|
|
269
339
|
return p;
|
|
@@ -274,8 +344,32 @@ class AMenuElement extends HTMLElementBase {
|
|
|
274
344
|
get triggerAnchor() {
|
|
275
345
|
return this.closest("a-menu-item") ?? this.previousElementSibling;
|
|
276
346
|
}
|
|
347
|
+
/** The focusable element to hand focus back to — the anchor itself if it's
|
|
348
|
+
* focusable, else the first focusable within it. Positioning still uses the
|
|
349
|
+
* anchor's box; this only picks the focus target. A custom `renderTrigger`
|
|
350
|
+
* that returns a single focusable element (the documented contract) resolves
|
|
351
|
+
* to that element; if it wrongly returns a non-focusable node with none
|
|
352
|
+
* inside, this is null and `focusTrigger` warns. */
|
|
353
|
+
get #triggerFocusable() {
|
|
354
|
+
const a = this.triggerAnchor;
|
|
355
|
+
if (!a) return null;
|
|
356
|
+
const sel = 'a-input, a-button, button, [href], input:not([type="hidden"]), select, textarea, [tabindex]:not([tabindex="-1"])';
|
|
357
|
+
return a.matches(sel) ? a : a.querySelector(sel);
|
|
358
|
+
}
|
|
359
|
+
/** Return focus to the trigger. Focuses the first focusable in the anchor; if
|
|
360
|
+
* there is none (a mis-authored trigger — e.g. `renderTrigger` returning a
|
|
361
|
+
* fragment or a non-focusable node), warns instead of silently focusing air. */
|
|
362
|
+
focusTrigger() {
|
|
363
|
+
const f = this.#triggerFocusable;
|
|
364
|
+
if (f) f.focus();
|
|
365
|
+
else if (this.triggerAnchor) {
|
|
366
|
+
console.warn(
|
|
367
|
+
"[a-menu] trigger has no focusable element \u2014 a menu trigger (or `Select` renderTrigger) must be a single focusable element."
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
277
371
|
/** For a submenu: the menu that contains its anchor item. */
|
|
278
|
-
get ownerMenu() {
|
|
372
|
+
get #ownerMenu() {
|
|
279
373
|
if (!this.isSubmenu) return null;
|
|
280
374
|
return this.triggerAnchor?.closest("a-menu") ?? null;
|
|
281
375
|
}
|
|
@@ -308,13 +402,119 @@ class AMenuElement extends HTMLElementBase {
|
|
|
308
402
|
* used to trap Tab within the open menu. Submenu contents are excluded
|
|
309
403
|
* (their nearest `a-menu` is the submenu). */
|
|
310
404
|
focusables() {
|
|
311
|
-
const sel = 'a-menu-item, a[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
405
|
+
const sel = 'a-menu-item, a[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]), a-input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]), [data-menu-search]';
|
|
312
406
|
return Array.from(this.querySelectorAll(sel)).filter(
|
|
313
407
|
(el) => el.closest("a-menu") === this && !el.hasAttribute("disabled") && this.isVisible(el)
|
|
314
408
|
);
|
|
315
409
|
}
|
|
316
|
-
|
|
317
|
-
|
|
410
|
+
/** On open, seat initial focus like a native `<select>` / macOS menu: a menu
|
|
411
|
+
* carrying a current value opens *at* that value, not at the top. Brings the
|
|
412
|
+
* first selected row into view and, when opened via keyboard, focuses it;
|
|
413
|
+
* with no selected row it focuses the first item (keyboard) and does nothing
|
|
414
|
+
* otherwise. "Selected" = a `selected` row (tint) or `aria-checked` /
|
|
415
|
+
* `aria-selected="true"` (checkable rows) — the first such visible item of
|
|
416
|
+
* THIS menu, so a leaf buried in a closed submenu (not visible) is skipped and
|
|
417
|
+
* a multi-select lands on its topmost checked row. */
|
|
418
|
+
seatInitialFocus(viaKeyboard) {
|
|
419
|
+
const items = this.focusableItems();
|
|
420
|
+
const selected = items.find(
|
|
421
|
+
(el) => el.hasAttribute("selected") || el.getAttribute("aria-checked") === "true" || el.getAttribute("aria-selected") === "true"
|
|
422
|
+
);
|
|
423
|
+
if (selected) this.scrollItemIntoView(selected);
|
|
424
|
+
if (viaKeyboard) (selected ?? items[0])?.focus({ preventScroll: true });
|
|
425
|
+
}
|
|
426
|
+
/** Scroll THIS menu's body so `item` sits inside the visible scroll viewport,
|
|
427
|
+
* touching only the internal `.scroll` container — never the document, whose
|
|
428
|
+
* scroll would trip the anchor-scrolled-out dismiss. No-op for a menu short
|
|
429
|
+
* enough not to scroll. */
|
|
430
|
+
scrollItemIntoView(item) {
|
|
431
|
+
const c = this.scrollEl;
|
|
432
|
+
if (!c) return;
|
|
433
|
+
const pad = 4;
|
|
434
|
+
const cr = c.getBoundingClientRect();
|
|
435
|
+
const ir = item.getBoundingClientRect();
|
|
436
|
+
if (ir.top < cr.top + pad) c.scrollTop -= cr.top + pad - ir.top;
|
|
437
|
+
else if (ir.bottom > cr.bottom - pad) c.scrollTop += ir.bottom - cr.bottom + pad;
|
|
438
|
+
}
|
|
439
|
+
/** Fade the scrolling body's content into the top / bottom edges — but only on the
|
|
440
|
+
* side that actually has more to scroll, so a short (non-scrolling) menu and the
|
|
441
|
+
* true top / bottom stay crisp. Drives the `--fade-*` / `--gap-top` vars the
|
|
442
|
+
* `.scroll` mask reads; runs on scroll and after every (re)position.
|
|
443
|
+
* Shadow-internal only. */
|
|
444
|
+
updateScrollFade() {
|
|
445
|
+
const el = this.scrollEl;
|
|
446
|
+
if (!el) return;
|
|
447
|
+
const top = el.scrollTop > 1;
|
|
448
|
+
const bottom = el.scrollTop + el.clientHeight < el.scrollHeight - 1;
|
|
449
|
+
el.style.setProperty("--fade-top", top ? "16px" : "0px");
|
|
450
|
+
el.style.setProperty("--gap-top", top ? "3px" : "0px");
|
|
451
|
+
el.style.setProperty("--fade-bottom", bottom ? "14px" : "0px");
|
|
452
|
+
}
|
|
453
|
+
/* ------------------------------ combobox mode ------------------------------ */
|
|
454
|
+
/** The filter field slotted into THIS menu (never a submenu's), or null. Its
|
|
455
|
+
* presence switches the menu to the combobox keyboard. */
|
|
456
|
+
get #searchField() {
|
|
457
|
+
const el = this.querySelector("[data-menu-search]");
|
|
458
|
+
return el && el.closest("a-menu") === this ? el : null;
|
|
459
|
+
}
|
|
460
|
+
/** Move the combobox cursor. Sets the item's `active` **property** (off-DOM
|
|
461
|
+
* `:state(active)`, no attribute churn) for the highlight, and REPORTS the
|
|
462
|
+
* active id via the `activedescendant` event so the reactive layer can set
|
|
463
|
+
* `aria-activedescendant` on the light-DOM field. `null` clears the cursor. */
|
|
464
|
+
setActive(item) {
|
|
465
|
+
if (this.activeItem && this.activeItem !== item) this.activeItem.active = false;
|
|
466
|
+
this.activeItem = item;
|
|
467
|
+
if (item) {
|
|
468
|
+
item.active = true;
|
|
469
|
+
item.scrollIntoView?.({ block: "nearest" });
|
|
470
|
+
}
|
|
471
|
+
this.dispatchEvent(
|
|
472
|
+
new CustomEvent("activedescendant", { detail: { id: item?.id ?? null } })
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
/** Re-seat the cursor on the first option — but only once the filter has input.
|
|
476
|
+
* An empty filter (e.g. right after opening) shows NO active row, so the first
|
|
477
|
+
* ArrowDown is what steps onto the list; typing then keeps the top match active.
|
|
478
|
+
* Rows marked `data-menu-skip-active` (e.g. a Select-all action) are skipped as
|
|
479
|
+
* the seat target — the cursor lands on the first real option — but they stay
|
|
480
|
+
* arrow-reachable. */
|
|
481
|
+
resetActive() {
|
|
482
|
+
const q = this.#searchField?.value;
|
|
483
|
+
if (!q || !q.trim()) return this.setActive(null);
|
|
484
|
+
const items = this.focusableItems();
|
|
485
|
+
this.setActive(items.find((it) => !it.hasAttribute("data-menu-skip-active")) ?? items[0] ?? null);
|
|
486
|
+
}
|
|
487
|
+
/** Combobox arrow / Home / End / Enter handling; returns true if it consumed the
|
|
488
|
+
* key (so `handleKey` stops). Any other key falls through to the input (typing). */
|
|
489
|
+
handleComboKey(e) {
|
|
490
|
+
const items = this.focusableItems();
|
|
491
|
+
const idx = this.activeItem ? items.indexOf(this.activeItem) : -1;
|
|
492
|
+
switch (e.key) {
|
|
493
|
+
case "ArrowDown":
|
|
494
|
+
e.preventDefault();
|
|
495
|
+
this.setActive(items[idx < 0 ? 0 : (idx + 1) % items.length] ?? null);
|
|
496
|
+
return true;
|
|
497
|
+
case "ArrowUp":
|
|
498
|
+
e.preventDefault();
|
|
499
|
+
this.setActive(items[idx <= 0 ? items.length - 1 : idx - 1] ?? null);
|
|
500
|
+
return true;
|
|
501
|
+
case "Home":
|
|
502
|
+
e.preventDefault();
|
|
503
|
+
this.setActive(items[0] ?? null);
|
|
504
|
+
return true;
|
|
505
|
+
case "End":
|
|
506
|
+
e.preventDefault();
|
|
507
|
+
this.setActive(items[items.length - 1] ?? null);
|
|
508
|
+
return true;
|
|
509
|
+
case "Enter":
|
|
510
|
+
if (this.activeItem) {
|
|
511
|
+
e.preventDefault();
|
|
512
|
+
this.activeItem.click();
|
|
513
|
+
}
|
|
514
|
+
return true;
|
|
515
|
+
default:
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
318
518
|
}
|
|
319
519
|
/* ============================ open / close ============================ */
|
|
320
520
|
/** Public imperative API. Routes through the same intent path as the
|
|
@@ -372,7 +572,7 @@ class AMenuElement extends HTMLElementBase {
|
|
|
372
572
|
* controlled `state` sync. */
|
|
373
573
|
show(coord, viaKeyboard = false, _originEvent) {
|
|
374
574
|
if (this.isSubmenu) {
|
|
375
|
-
const parent = this
|
|
575
|
+
const parent = this.#ownerMenu;
|
|
376
576
|
if (parent) {
|
|
377
577
|
const pidx = openStack.indexOf(parent);
|
|
378
578
|
if (pidx !== -1) {
|
|
@@ -381,7 +581,19 @@ class AMenuElement extends HTMLElementBase {
|
|
|
381
581
|
}
|
|
382
582
|
}
|
|
383
583
|
} else if (!openStack.includes(this)) {
|
|
384
|
-
|
|
584
|
+
const anchor = this.triggerAnchor;
|
|
585
|
+
const container = anchor ? openStack.find((m) => m !== this && m.contains(anchor)) : void 0;
|
|
586
|
+
if (container) {
|
|
587
|
+
const idx = openStack.indexOf(container);
|
|
588
|
+
for (let i = openStack.length - 1; i > idx; i--) {
|
|
589
|
+
const m = openStack[i];
|
|
590
|
+
if (m.isOpen && !m._dismissNotified) m.emitChange("closed");
|
|
591
|
+
m._doHide();
|
|
592
|
+
}
|
|
593
|
+
openStack.length = idx + 1;
|
|
594
|
+
} else {
|
|
595
|
+
closeAll();
|
|
596
|
+
}
|
|
385
597
|
}
|
|
386
598
|
if (openStack.includes(this)) {
|
|
387
599
|
this.position(coord);
|
|
@@ -394,7 +606,29 @@ class AMenuElement extends HTMLElementBase {
|
|
|
394
606
|
bindDocListeners(this.doc, this.view);
|
|
395
607
|
this.armPositionTracker();
|
|
396
608
|
}
|
|
397
|
-
|
|
609
|
+
const search = this.#searchField;
|
|
610
|
+
if (search) {
|
|
611
|
+
;
|
|
612
|
+
search.focus({ preventScroll: true });
|
|
613
|
+
this.resetActive();
|
|
614
|
+
this.startComboObserver();
|
|
615
|
+
} else {
|
|
616
|
+
this.seatInitialFocus(viaKeyboard);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
/** While a filter field is open, the visible option list changes as the user
|
|
620
|
+
* types (the consumer re-renders the matches); re-seat the cursor on the first
|
|
621
|
+
* match. `childList` only — an item toggling its own `selected` (multi-select)
|
|
622
|
+
* mutates deep in its subtree, not this menu's direct children, so it won't
|
|
623
|
+
* spuriously reset the cursor. */
|
|
624
|
+
startComboObserver() {
|
|
625
|
+
this.comboObserver?.disconnect();
|
|
626
|
+
this.comboObserver = new this.view.MutationObserver(() => {
|
|
627
|
+
if (!this._shown || !this.#searchField) return;
|
|
628
|
+
this.resetActive();
|
|
629
|
+
this.position(void 0, false, true);
|
|
630
|
+
});
|
|
631
|
+
this.comboObserver.observe(this, { childList: true });
|
|
398
632
|
}
|
|
399
633
|
/** Watch the root trigger and dismiss the system once it scrolls out of the
|
|
400
634
|
* spot it held at open (see trackPosition). Deferred a frame so the trigger's
|
|
@@ -413,6 +647,10 @@ class AMenuElement extends HTMLElementBase {
|
|
|
413
647
|
/** Apply CLOSE to the DOM (no event). Closes this menu and everything stacked
|
|
414
648
|
* above it (its submenus). */
|
|
415
649
|
hide() {
|
|
650
|
+
this.comboObserver?.disconnect();
|
|
651
|
+
this.comboObserver = void 0;
|
|
652
|
+
this.setActive(null);
|
|
653
|
+
this._flippedTop = null;
|
|
416
654
|
const idx = openStack.indexOf(this);
|
|
417
655
|
if (idx === -1) {
|
|
418
656
|
if (this._shown) this._doHide();
|
|
@@ -431,7 +669,7 @@ class AMenuElement extends HTMLElementBase {
|
|
|
431
669
|
if (this.surface.isConnected && !this._shown) this.surface.showPopover();
|
|
432
670
|
this._shown = true;
|
|
433
671
|
this._dismissNotified = false;
|
|
434
|
-
this.
|
|
672
|
+
this.reflectOpen(true);
|
|
435
673
|
this.hideAnchorTooltip();
|
|
436
674
|
this.position(coord, instant);
|
|
437
675
|
}
|
|
@@ -447,27 +685,27 @@ class AMenuElement extends HTMLElementBase {
|
|
|
447
685
|
_doHide() {
|
|
448
686
|
if (this.surface.isConnected && this._shown) this.surface.hidePopover();
|
|
449
687
|
this._shown = false;
|
|
450
|
-
this.
|
|
688
|
+
this.reflectOpen(false);
|
|
451
689
|
this.cancelOpenTimer();
|
|
452
690
|
this.cancelCloseTimer();
|
|
453
691
|
}
|
|
454
|
-
/**
|
|
455
|
-
*
|
|
456
|
-
*
|
|
457
|
-
*
|
|
458
|
-
*
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
*
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
692
|
+
/** Expose the menu's OWN open state as an off-DOM custom state (`:state(open)`),
|
|
693
|
+
* never a light-DOM attribute. A web component must not mutate light DOM — it
|
|
694
|
+
* desyncs the worker-thread reactive model, which owns the light tree. A
|
|
695
|
+
* submenu parent lights its open branch purely in CSS via
|
|
696
|
+
* `a-menu-item:has(> a-menu:state(open))`; the state is element-owned (like
|
|
697
|
+
* `a-menu-item`'s `:state(active)`) and, being off-DOM, survives a reactive
|
|
698
|
+
* re-render without the element ever writing an attribute. Set on every menu
|
|
699
|
+
* (harmless on roots — no `a-menu-item` parent matches the selector). */
|
|
700
|
+
reflectOpen(open) {
|
|
701
|
+
try {
|
|
702
|
+
if (open) this.internals?.states.add("open");
|
|
703
|
+
else this.internals?.states.delete("open");
|
|
704
|
+
} catch {
|
|
705
|
+
}
|
|
468
706
|
}
|
|
469
707
|
/* ============================ positioning ============================ */
|
|
470
|
-
position(coord, sync = false) {
|
|
708
|
+
position(coord, sync = false, reanchor = false) {
|
|
471
709
|
const run = () => {
|
|
472
710
|
if (!this._shown) return;
|
|
473
711
|
const view = this.view;
|
|
@@ -486,30 +724,38 @@ class AMenuElement extends HTMLElementBase {
|
|
|
486
724
|
if (top + box.height > vh - MARGIN) top = top - box.height;
|
|
487
725
|
top = Math.max(MARGIN, top);
|
|
488
726
|
} else if (this.isSubmenu) {
|
|
489
|
-
const it = this.triggerAnchor
|
|
727
|
+
const it = this.triggerAnchor ? anchorRect(this.triggerAnchor) : null;
|
|
490
728
|
if (!it) return;
|
|
491
729
|
surface.style.maxHeight = `${Math.max(MIN_HEIGHT, vh - 2 * MARGIN)}px`;
|
|
492
730
|
const box = surface.getBoundingClientRect();
|
|
493
|
-
left = it.right + this
|
|
731
|
+
left = it.right + this.#offset;
|
|
494
732
|
if (left + box.width > vw - MARGIN) {
|
|
495
|
-
left = it.left - box.width - this
|
|
733
|
+
left = it.left - box.width - this.#offset;
|
|
496
734
|
}
|
|
497
735
|
left = Math.max(MARGIN, left);
|
|
498
736
|
const cs = view.getComputedStyle(surface);
|
|
499
|
-
const
|
|
737
|
+
const scs = view.getComputedStyle(this.scrollEl);
|
|
738
|
+
const insetTop = parseFloat(cs.borderTopWidth) + parseFloat(cs.paddingTop) + parseFloat(scs.paddingTop);
|
|
500
739
|
top = it.top - insetTop;
|
|
501
740
|
if (top + box.height > vh - MARGIN) top = vh - box.height - MARGIN;
|
|
502
741
|
top = Math.max(MARGIN, top);
|
|
503
742
|
} else {
|
|
504
|
-
const a = this.triggerAnchor
|
|
743
|
+
const a = this.triggerAnchor ? anchorRect(this.triggerAnchor) : null;
|
|
505
744
|
if (!a) return;
|
|
506
|
-
|
|
745
|
+
surface.style.setProperty("--_anchor-width", `${Math.ceil(a.width)}px`);
|
|
746
|
+
const p = this.#placement;
|
|
507
747
|
const spaceBelow = vh - a.bottom - 2 * MARGIN;
|
|
508
748
|
const spaceAbove = a.top - 2 * MARGIN;
|
|
509
749
|
let onTop = p.startsWith("top");
|
|
750
|
+
surface.style.maxHeight = "";
|
|
510
751
|
const natural = surface.scrollHeight;
|
|
511
|
-
if (
|
|
512
|
-
|
|
752
|
+
if (reanchor && this._flippedTop !== null) {
|
|
753
|
+
onTop = this._flippedTop;
|
|
754
|
+
} else {
|
|
755
|
+
if (onTop && spaceAbove < natural && spaceBelow > spaceAbove) onTop = false;
|
|
756
|
+
else if (!onTop && spaceBelow < natural && spaceAbove > spaceBelow) onTop = true;
|
|
757
|
+
}
|
|
758
|
+
this._flippedTop = onTop;
|
|
513
759
|
const space = onTop ? spaceAbove : spaceBelow;
|
|
514
760
|
surface.style.maxHeight = `${Math.max(MIN_HEIGHT, Math.floor(space))}px`;
|
|
515
761
|
const box = surface.getBoundingClientRect();
|
|
@@ -517,10 +763,11 @@ class AMenuElement extends HTMLElementBase {
|
|
|
517
763
|
left = align === "center" ? a.left + a.width / 2 - box.width / 2 : align === "end" ? a.right - box.width : a.left;
|
|
518
764
|
if (left + box.width > vw - MARGIN) left = vw - box.width - MARGIN;
|
|
519
765
|
left = Math.max(MARGIN, left);
|
|
520
|
-
top = onTop ? a.top - box.height - this
|
|
766
|
+
top = onTop ? a.top - box.height - this.#offset : a.bottom + this.#offset;
|
|
521
767
|
top = Math.max(MARGIN, top);
|
|
522
768
|
}
|
|
523
769
|
surface.style.transform = `translate(${Math.round(left)}px, ${Math.round(top)}px)`;
|
|
770
|
+
this.updateScrollFade();
|
|
524
771
|
};
|
|
525
772
|
if (sync) run();
|
|
526
773
|
else requestAnimationFrame(run);
|
|
@@ -540,6 +787,23 @@ class AMenuElement extends HTMLElementBase {
|
|
|
540
787
|
* - nothing → keep open (plain custom content doesn't dismiss).
|
|
541
788
|
*/
|
|
542
789
|
onSurfaceClick = (e) => {
|
|
790
|
+
for (const node of e.composedPath()) {
|
|
791
|
+
if (node === this.surface) break;
|
|
792
|
+
if (node instanceof AMenuItemElement) {
|
|
793
|
+
if (!node.hasAttribute("disabled") && !node.querySelector("a-menu") && node.closest("a-menu") === this) {
|
|
794
|
+
node.dispatchEvent(
|
|
795
|
+
new MouseEvent("menuselect", {
|
|
796
|
+
bubbles: false,
|
|
797
|
+
altKey: e.altKey,
|
|
798
|
+
ctrlKey: e.ctrlKey,
|
|
799
|
+
metaKey: e.metaKey,
|
|
800
|
+
shiftKey: e.shiftKey
|
|
801
|
+
})
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
break;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
543
807
|
for (const node of e.composedPath()) {
|
|
544
808
|
if (node === this.surface) break;
|
|
545
809
|
if (!(node instanceof Element)) continue;
|
|
@@ -569,9 +833,8 @@ class AMenuElement extends HTMLElementBase {
|
|
|
569
833
|
if (e.key === "Escape") {
|
|
570
834
|
e.preventDefault();
|
|
571
835
|
e.stopPropagation();
|
|
572
|
-
const anchor = this.triggerAnchor;
|
|
573
836
|
this.requestClose(e);
|
|
574
|
-
|
|
837
|
+
this.focusTrigger();
|
|
575
838
|
return;
|
|
576
839
|
}
|
|
577
840
|
if (e.key === "Tab") {
|
|
@@ -587,6 +850,11 @@ class AMenuElement extends HTMLElementBase {
|
|
|
587
850
|
f[next]?.focus();
|
|
588
851
|
return;
|
|
589
852
|
}
|
|
853
|
+
const search = this.#searchField;
|
|
854
|
+
if (search && (active === search || search.contains(active))) {
|
|
855
|
+
this.handleComboKey(e);
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
590
858
|
const within = active?.closest("a-menu") === this;
|
|
591
859
|
if (within && !(active instanceof AMenuItemElement)) return;
|
|
592
860
|
const items = this.focusableItems();
|
|
@@ -594,19 +862,19 @@ class AMenuElement extends HTMLElementBase {
|
|
|
594
862
|
switch (e.key) {
|
|
595
863
|
case "ArrowDown":
|
|
596
864
|
e.preventDefault();
|
|
597
|
-
items[idx < 0 ? 0 : (idx + 1) % items.length]?.focus();
|
|
865
|
+
items[idx < 0 ? 0 : (idx + 1) % items.length]?.focus({ preventScroll: true });
|
|
598
866
|
break;
|
|
599
867
|
case "ArrowUp":
|
|
600
868
|
e.preventDefault();
|
|
601
|
-
items[idx <= 0 ? items.length - 1 : idx - 1]?.focus();
|
|
869
|
+
items[idx <= 0 ? items.length - 1 : idx - 1]?.focus({ preventScroll: true });
|
|
602
870
|
break;
|
|
603
871
|
case "Home":
|
|
604
872
|
e.preventDefault();
|
|
605
|
-
items[0]?.focus();
|
|
873
|
+
items[0]?.focus({ preventScroll: true });
|
|
606
874
|
break;
|
|
607
875
|
case "End":
|
|
608
876
|
e.preventDefault();
|
|
609
|
-
items[items.length - 1]?.focus();
|
|
877
|
+
items[items.length - 1]?.focus({ preventScroll: true });
|
|
610
878
|
break;
|
|
611
879
|
case "ArrowRight": {
|
|
612
880
|
const sub = this.submenuOf(active);
|
|
@@ -665,7 +933,7 @@ class AMenuElement extends HTMLElementBase {
|
|
|
665
933
|
anchor.addEventListener("click", onClick);
|
|
666
934
|
let onEnter;
|
|
667
935
|
let onLeave;
|
|
668
|
-
if (this
|
|
936
|
+
if (this.#isHover) {
|
|
669
937
|
onEnter = (e) => {
|
|
670
938
|
if (e.pointerType === "mouse") this.scheduleOpen();
|
|
671
939
|
};
|
|
@@ -681,7 +949,7 @@ class AMenuElement extends HTMLElementBase {
|
|
|
681
949
|
if (onLeave) anchor.removeEventListener("pointerleave", onLeave);
|
|
682
950
|
this.listening = false;
|
|
683
951
|
};
|
|
684
|
-
} else if (this
|
|
952
|
+
} else if (this.#isContext) {
|
|
685
953
|
const onContext = (e) => {
|
|
686
954
|
e.preventDefault();
|
|
687
955
|
this.requestOpen({ coord: [e.clientX, e.clientY], originEvent: e });
|
|
@@ -695,9 +963,9 @@ class AMenuElement extends HTMLElementBase {
|
|
|
695
963
|
const onClick = (e) => {
|
|
696
964
|
const viaKeyboard = e.detail === 0;
|
|
697
965
|
let coord;
|
|
698
|
-
if (this
|
|
966
|
+
if (this.#isCoord) {
|
|
699
967
|
if (viaKeyboard) {
|
|
700
|
-
const r = anchor
|
|
968
|
+
const r = anchorRect(anchor);
|
|
701
969
|
coord = [r.left, r.bottom];
|
|
702
970
|
} else {
|
|
703
971
|
coord = [e.clientX, e.clientY];
|
|
@@ -731,12 +999,23 @@ class AMenuElement extends HTMLElementBase {
|
|
|
731
999
|
scheduleClose() {
|
|
732
1000
|
this.cancelOpenTimer();
|
|
733
1001
|
if (!this._shown) return;
|
|
1002
|
+
if (this.#hasKeyboardFocusInside) return;
|
|
734
1003
|
this.cancelCloseTimer();
|
|
735
1004
|
this.closeTimer = setTimeout(() => {
|
|
736
1005
|
this.closeTimer = void 0;
|
|
737
1006
|
this.requestClose();
|
|
738
1007
|
}, SUBMENU_CLOSE_DELAY);
|
|
739
1008
|
}
|
|
1009
|
+
/** True when the document's focused element is inside this menu AND is
|
|
1010
|
+
* keyboard-focused (`:focus-visible`). Distinguishes "arrowed into the flyout"
|
|
1011
|
+
* (keep it open on hover-out) from a mouse-click focus (close as usual).
|
|
1012
|
+
* `activeElement` retargets to the shadow host at the document level, so a
|
|
1013
|
+
* focused menu item (or its delegated inner control) reads as a light-DOM
|
|
1014
|
+
* descendant here. */
|
|
1015
|
+
get #hasKeyboardFocusInside() {
|
|
1016
|
+
const active = this.doc.activeElement;
|
|
1017
|
+
return !!active && this.contains(active) && active.matches(":focus-visible");
|
|
1018
|
+
}
|
|
740
1019
|
cancelOpenTimer() {
|
|
741
1020
|
if (this.openTimer !== void 0) {
|
|
742
1021
|
clearTimeout(this.openTimer);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
@layer anta{a-progress{--progress-bg: var(--bg-4);--progress-border-color: var(--border-4);--progress-indicator-bg: var(--bg-5);--progress-indicator-edge: linear-gradient( 90deg, var(--progress-indicator-bg) 0%, var(--progress-border-color) 100% );--progress-text-color: var(--text-2);--progress-hint-color: var(--text-3);display:block;container-type:inline-size;padding:4px 8px;min-height:8px;background:var(--progress-bg);border:0px solid var(--progress-border-color);border-radius:0}a-progress[tone=brand]{--progress-bg: var(--bg-4-brand);--progress-border-color: var(--border-4-brand);--progress-indicator-bg: var(--bg-5-brand);--progress-text-color: var(--text-2-brand);--progress-hint-color: var(--text-3-brand)}a-progress[tone=info]{--progress-bg: var(--bg-4-info);--progress-border-color: var(--border-4-info);--progress-indicator-bg: var(--bg-5-info);--progress-text-color: var(--text-2-info);--progress-hint-color: var(--text-3-info)}a-progress[tone=success]{--progress-bg: var(--bg-4-success);--progress-border-color: var(--border-4-success);--progress-indicator-bg: var(--bg-5-success);--progress-text-color: var(--text-2-success);--progress-hint-color: var(--text-3-success)}a-progress[tone=warning]{--progress-bg: var(--bg-4-warning);--progress-border-color: var(--border-4-warning);--progress-indicator-bg: var(--bg-5-warning);--progress-text-color: var(--text-2-warning);--progress-hint-color: var(--text-3-warning)}a-progress[tone=critical]{--progress-bg: var(--bg-4-critical);--progress-border-color: var(--border-4-critical);--progress-indicator-bg: var(--bg-5-critical);--progress-text-color: var(--text-2-critical);--progress-hint-color: var(--text-3-critical)}a-progress[tone]:not([tone=""],[tone=neutral],[tone=brand],[tone=info],[tone=success],[tone=warning],[tone=critical]){--progress-tone-source: attr(tone type(<color>), transparent);--progress-bg: oklch(from var(--progress-tone-source) .95 calc(c*.4) h);--progress-border-color: oklch(from var(--progress-tone-source) .85 calc(c*.6) h);--progress-indicator-bg: oklch(from var(--progress-tone-source) .6 c h);--progress-text-color: oklch(from var(--progress-tone-source) .45 c h);--progress-hint-color: oklch(from var(--progress-tone-source) .55 c h);.dark &{--progress-bg: oklch(from var(--progress-tone-source) .28 calc(c/2) h);--progress-border-color: oklch(from var(--progress-tone-source) .4 calc(c*.6) h);--progress-indicator-bg: oklch(from var(--progress-tone-source) .6 c h);--progress-text-color: oklch(from var(--progress-tone-source) .85 c h);--progress-hint-color: oklch(from var(--progress-tone-source) .75 c h)}}a-progress-label{display:flex;align-items:flex-start;gap:.5ch;font-variant-numeric:tabular-nums;font-feature-settings:"ss02","ss05";letter-spacing:.03em;font-size:13px;line-height:16px}a-progress-number{flex-shrink:0;color:var(--progress-text-color)}a-progress-text{flex:1 1 auto;min-width:0;color:var(--progress-text-color)}a-progress-hint{flex-shrink:0;margin-left:auto;color:var(--progress-hint-color)}}
|
|
1
|
+
@layer anta{a-progress{--progress-bg: var(--bg-4);--progress-border-color: var(--border-4);--progress-indicator-bg: var(--bg-5);--progress-indicator-edge: linear-gradient( 90deg, var(--progress-indicator-bg) 0%, var(--progress-border-color) 100% );--progress-text-color: var(--text-2);--progress-hint-color: var(--text-3);display:block;container-type:inline-size;padding:4px 8px;min-height:8px;background:var(--progress-bg);border:0px solid var(--progress-border-color);border-radius:0}a-progress[round]{--progress-round: attr(round type(<length>), 999px);border-radius:var(--progress-round, 999px)}a-progress[tone=brand]{--progress-bg: var(--bg-4-brand);--progress-border-color: var(--border-4-brand);--progress-indicator-bg: var(--bg-5-brand);--progress-text-color: var(--text-2-brand);--progress-hint-color: var(--text-3-brand)}a-progress[tone=info]{--progress-bg: var(--bg-4-info);--progress-border-color: var(--border-4-info);--progress-indicator-bg: var(--bg-5-info);--progress-text-color: var(--text-2-info);--progress-hint-color: var(--text-3-info)}a-progress[tone=success]{--progress-bg: var(--bg-4-success);--progress-border-color: var(--border-4-success);--progress-indicator-bg: var(--bg-5-success);--progress-text-color: var(--text-2-success);--progress-hint-color: var(--text-3-success)}a-progress[tone=warning]{--progress-bg: var(--bg-4-warning);--progress-border-color: var(--border-4-warning);--progress-indicator-bg: var(--bg-5-warning);--progress-text-color: var(--text-2-warning);--progress-hint-color: var(--text-3-warning)}a-progress[tone=critical]{--progress-bg: var(--bg-4-critical);--progress-border-color: var(--border-4-critical);--progress-indicator-bg: var(--bg-5-critical);--progress-text-color: var(--text-2-critical);--progress-hint-color: var(--text-3-critical)}a-progress[tone]:not([tone=""],[tone=neutral],[tone=brand],[tone=info],[tone=success],[tone=warning],[tone=critical]){--progress-tone-source: attr(tone type(<color>), transparent);--progress-bg: oklch(from var(--progress-tone-source) .95 calc(c*.4) h);--progress-border-color: oklch(from var(--progress-tone-source) .85 calc(c*.6) h);--progress-indicator-bg: oklch(from var(--progress-tone-source) .6 c h);--progress-text-color: oklch(from var(--progress-tone-source) .45 c h);--progress-hint-color: oklch(from var(--progress-tone-source) .55 c h);.dark &{--progress-bg: oklch(from var(--progress-tone-source) .28 calc(c/2) h);--progress-border-color: oklch(from var(--progress-tone-source) .4 calc(c*.6) h);--progress-indicator-bg: oklch(from var(--progress-tone-source) .6 c h);--progress-text-color: oklch(from var(--progress-tone-source) .85 c h);--progress-hint-color: oklch(from var(--progress-tone-source) .75 c h)}}a-progress-label{display:flex;align-items:flex-start;gap:.5ch;font-variant-numeric:lining-nums tabular-nums;font-feature-settings:"ss02","ss05";letter-spacing:.03em;font-size:13px;line-height:16px}a-progress-number{flex-shrink:0;color:var(--progress-text-color)}a-progress-text{flex:1 1 auto;min-width:0;color:var(--progress-text-color)}a-progress-hint{flex-shrink:0;margin-left:auto;color:var(--progress-hint-color)}}
|