@antadesign/anta 0.3.0 → 0.3.2

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.
Files changed (51) hide show
  1. package/dist/anta_helpers.d.ts +10 -0
  2. package/dist/anta_helpers.js +13 -1
  3. package/dist/components/Button.js +8 -17
  4. package/dist/components/Checkbox.d.ts +16 -15
  5. package/dist/components/Checkbox.js +10 -6
  6. package/dist/components/Input.d.ts +4 -4
  7. package/dist/components/Input.js +5 -5
  8. package/dist/components/Menu.d.ts +7 -4
  9. package/dist/components/Menu.js +2 -2
  10. package/dist/components/MenuItem.d.ts +18 -7
  11. package/dist/components/MenuItem.js +11 -2
  12. package/dist/components/RadioGroup.d.ts +15 -15
  13. package/dist/components/RadioGroup.js +8 -8
  14. package/dist/components/Tab.d.ts +34 -0
  15. package/dist/components/Tab.js +4 -0
  16. package/dist/components/TabPanel.d.ts +22 -0
  17. package/dist/components/TabPanel.js +4 -0
  18. package/dist/components/Tabs.d.ts +112 -0
  19. package/dist/components/Tabs.js +174 -0
  20. package/dist/components/Tabs.module.css +1 -0
  21. package/dist/components/Tooltip.d.ts +10 -1
  22. package/dist/components/Tooltip.js +4 -0
  23. package/dist/elements/a-button.css +1 -1
  24. package/dist/elements/a-checkbox.css +1 -1
  25. package/dist/elements/a-expander.js +6 -1
  26. package/dist/elements/a-icon.shapes.css +1 -1
  27. package/dist/elements/a-icon.shapes.d.ts +2 -1
  28. package/dist/elements/a-icon.shapes.js +1 -0
  29. package/dist/elements/a-input.js +1 -1
  30. package/dist/elements/a-menu.d.ts +12 -0
  31. package/dist/elements/a-menu.js +70 -21
  32. package/dist/elements/a-radio.css +1 -1
  33. package/dist/elements/a-tab.css +1 -0
  34. package/dist/elements/a-tab.d.ts +14 -0
  35. package/dist/elements/a-tab.js +46 -0
  36. package/dist/elements/a-tabpanel.css +1 -0
  37. package/dist/elements/a-tabs.css +1 -0
  38. package/dist/elements/a-tabs.d.ts +31 -0
  39. package/dist/elements/a-tabs.js +157 -0
  40. package/dist/elements/a-text.css +1 -1
  41. package/dist/elements/a-title.css +1 -1
  42. package/dist/elements/a-tooltip.d.ts +18 -0
  43. package/dist/elements/a-tooltip.js +45 -0
  44. package/dist/elements/index.d.ts +3 -0
  45. package/dist/elements/index.js +7 -0
  46. package/dist/general_types.d.ts +150 -23
  47. package/dist/index.d.ts +6 -0
  48. package/dist/index.js +6 -0
  49. package/dist/jsx-runtime.d.ts +5 -1
  50. package/dist/tokens.css +1 -1
  51. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ @layer anta{a-tab{user-select:none;display:inline-flex;align-items:center;justify-content:center;gap:.5ch;flex-shrink:1;min-width:0;cursor:pointer;color:var(--tab-fg, var(--text-3));background-color:var(--tab-bg, transparent);border:none;border-radius:var(--tab-radius);box-shadow:0 0 0 0 transparent;font-size:var(--_fs);font-family:var(--sans-serif);font-weight:450;font-variation-settings:"wdth" 88,"slnt" 0,"ital" 0;font-feature-settings:"ss02","ss05","tnum";line-height:var(--_lh);letter-spacing:.05ch;white-space:nowrap;text-wrap:nowrap;padding-block:var(--tab-padding-y);padding-inline:var(--tab-padding-x);transition:background-color var(--tab-timing),box-shadow var(--tab-timing);>a-tab-label{display:inline-block;min-width:0;overflow:hidden;text-overflow:ellipsis;text-wrap:nowrap;white-space:nowrap;line-height:calc(var(--_lh) - 2px);padding-bottom:var(--_pb);&:has(>*){display:inline-flex;gap:.25ch}}>*{pointer-events:none}&:has(>a-icon:first-child):has(>a-tab-label){padding-inline-start:max(0px,var(--tab-padding-x) - 2px)}&:has(>a-icon:last-child):has(>a-tab-label){padding-inline-end:max(0px,var(--tab-padding-x) - 2px)}}a-tab:focus-visible{outline:1px solid var(--focus-ring);outline-offset:-1px}a-tab[disabled]{pointer-events:none;cursor:not-allowed;opacity:.45}}
@@ -0,0 +1,14 @@
1
+ import { HTMLElementBase } from "../anta_helpers";
2
+ import "./a-tab.css";
3
+ export declare class ATabElement extends HTMLElementBase {
4
+ static observedAttributes: string[];
5
+ private internals?;
6
+ constructor();
7
+ connectedCallback(): void;
8
+ attributeChangedCallback(name: string): void;
9
+ get selected(): boolean;
10
+ set selected(on: boolean);
11
+ get value(): string;
12
+ private applyState;
13
+ }
14
+ export declare function register_a_tab(): void;
@@ -0,0 +1,46 @@
1
+ import { HTMLElementBase } from "../anta_helpers";
2
+ import "./a-tab.css";
3
+ class ATabElement extends HTMLElementBase {
4
+ static observedAttributes = ["selected"];
5
+ internals;
6
+ constructor() {
7
+ super();
8
+ this.internals = this.attachInternals?.();
9
+ }
10
+ connectedCallback() {
11
+ if (this.hasAttribute("selected")) this.applyState(true);
12
+ }
13
+ attributeChangedCallback(name) {
14
+ if (name === "selected") this.applyState(this.hasAttribute("selected"));
15
+ }
16
+ get selected() {
17
+ return this.internals?.states.has("selected") ?? false;
18
+ }
19
+ set selected(on) {
20
+ this.applyState(!!on);
21
+ }
22
+ get value() {
23
+ return this.getAttribute("value") ?? "";
24
+ }
25
+ // `selected` is the single source: it drives the visual `:state(selected)` and
26
+ // publishes `aria-selected` through ElementInternals (the accessibility tree,
27
+ // not a DOM attribute). <a-tabs> only sets `t.selected`; the tab owns how it
28
+ // reflects that. role="tab" comes from the wrapper, which gives ariaSelected
29
+ // something to attach to.
30
+ applyState(on) {
31
+ if (!this.internals) return;
32
+ if (on) this.internals.states.add("selected");
33
+ else this.internals.states.delete("selected");
34
+ this.internals.ariaSelected = on ? "true" : "false";
35
+ }
36
+ }
37
+ function register_a_tab() {
38
+ if (typeof customElements === "undefined") return;
39
+ if (!customElements.get("a-tab"))
40
+ customElements.define("a-tab", ATabElement);
41
+ }
42
+ register_a_tab();
43
+ export {
44
+ ATabElement,
45
+ register_a_tab
46
+ };
@@ -0,0 +1 @@
1
+ @layer anta{a-tabpanel{display:block}a-tabpanel[hidden]{display:none}a-tabpanel[data-hide=visibility]{visibility:hidden}}
@@ -0,0 +1 @@
1
+ @layer anta{a-tabs{--_fs: 15px;--_lh: 20px;--_pb: 1px;--tab-padding-x: 9px;--tab-padding-y: calc((10px - var(--_pb)) / 2);--tab-radius: 4px;--tabs-gap: 3px;--tab-timing: .15s ease-out;--tabs-tone-source: #635b65;--tab-fg-rest: var(--text-3);--tab-fg-hover: var(--text-1);--tab-selected-text: var(--text-1);--tab-text-2: var(--text-2);--tabs-track-bg: color-mix(in oklch, #44374b 6%, transparent);--tabs-track-border: var(--border-4);--tab-pill-bg: var(--bg-1);--tab-pill-shadow: 0 0 3px 0 var(--border-3);--tab-secondary-bg: color-mix(in oklch, #44374b 3%, transparent);--tab-secondary-border: var(--border-3);--tab-secondary-shadow: 0 0 0px 1px var(--tab-secondary-border);display:flex;flex-direction:row;align-items:stretch;gap:var(--tabs-gap);width:fit-content;align-self:flex-start;min-width:0;max-width:100%;overflow:hidden;scroll-behavior:smooth}a-tabs[size=small]{--_fs: 13px;--_lh: 16px;--_pb: .5px;--tab-padding-x: 7px}a-tabs[size=large]{--_fs: 17px;--_lh: 24px;--_pb: 1.5px;--tab-padding-x: 13px}a-tabs a-tab a-icon{--icon-size: 16px}a-tabs[size=small] a-tab a-icon{--icon-size: 14px}a-tabs[size=large] a-tab a-icon{--icon-size: 18px}a-tabs[tone=brand],a-tab[tone=brand]{--tabs-tone-source: #5f4bc3;--tab-selected-text: var(--text-1-brand);--tab-text-2: var(--text-2-brand);--tab-rest-tone: var(--text-3-brand);--tabs-track-border: var(--border-4-brand);--tab-secondary-border: var(--border-3-brand)}a-tabs[tone=info],a-tab[tone=info]{--tabs-tone-source: #1f6eb2;--tab-selected-text: var(--text-1-info);--tab-text-2: var(--text-2-info);--tab-rest-tone: var(--text-3-info);--tabs-track-border: var(--border-4-info);--tab-secondary-border: var(--border-3-info)}a-tabs[tone=success],a-tab[tone=success]{--tabs-tone-source: #2a7e43;--tab-selected-text: var(--text-1-success);--tab-text-2: var(--text-2-success);--tab-rest-tone: var(--text-3-success);--tabs-track-border: var(--border-4-success);--tab-secondary-border: var(--border-3-success)}a-tabs[tone=warning],a-tab[tone=warning]{--tabs-tone-source: #c37416;--tab-selected-text: var(--text-1-warning);--tab-text-2: var(--text-2-warning);--tab-rest-tone: var(--text-3-warning);--tabs-track-border: var(--border-4-warning);--tab-secondary-border: var(--border-3-warning)}a-tabs[tone=critical],a-tab[tone=critical]{--tabs-tone-source: #c9302c;--tab-selected-text: var(--text-1-critical);--tab-text-2: var(--text-2-critical);--tab-rest-tone: var(--text-3-critical);--tabs-track-border: var(--border-4-critical);--tab-secondary-border: var(--border-3-critical)}a-tabs[tone]:not([tone=""],[tone=neutral]),a-tab[tone]:not([tone=""],[tone=neutral]){--tabs-track-bg: oklch(from var(--tabs-tone-source) .55 .14 h / .08);--tab-secondary-bg: oklch(from var(--tabs-tone-source) .55 .14 h / .03);--tab-pill-shadow: 0 0 3px 0 oklch(from var(--tabs-tone-source) .62 .12 h / .5)}a-tabs:has(>a-tab[tone=brand]:state(selected)){--tabs-tone-source: #5f4bc3;--tab-selected-text: var(--text-1-brand);--tab-secondary-border: var(--border-3-brand)}a-tabs:has(>a-tab[tone=info]:state(selected)){--tabs-tone-source: #1f6eb2;--tab-selected-text: var(--text-1-info);--tab-secondary-border: var(--border-3-info)}a-tabs:has(>a-tab[tone=success]:state(selected)){--tabs-tone-source: #2a7e43;--tab-selected-text: var(--text-1-success);--tab-secondary-border: var(--border-3-success)}a-tabs:has(>a-tab[tone=warning]:state(selected)){--tabs-tone-source: #c37416;--tab-selected-text: var(--text-1-warning);--tab-secondary-border: var(--border-3-warning)}a-tabs:has(>a-tab[tone=critical]:state(selected)){--tabs-tone-source: #c9302c;--tab-selected-text: var(--text-1-critical);--tab-secondary-border: var(--border-3-critical)}a-tabs:has(>a-tab[tone]:state(selected):not([tone=""],[tone=neutral])){--tab-pill-shadow: 0 0 3px 0 oklch(from var(--tabs-tone-source) .62 .12 h / .5);--tab-secondary-bg: oklch(from var(--tabs-tone-source) .55 .14 h / .03)}a-tabs[tone]:not([tone=""],[tone=neutral],[tone=brand],[tone=info],[tone=success],[tone=warning],[tone=critical]),a-tab[tone]:not([tone=""],[tone=neutral],[tone=brand],[tone=info],[tone=success],[tone=warning],[tone=critical]){--tabs-tone-source: attr(tone type(<color>), #635b65);--tab-selected-text: oklch(from var(--tabs-tone-source) .38 .13 h);--tab-text-2: oklch(from var(--tabs-tone-source) .45 .13 h);--tab-rest-tone: oklch(from var(--tabs-tone-source) .45 .13 h);--tabs-track-border: oklch(from var(--tabs-tone-source) .9 .047 h);--tab-secondary-border: oklch(from var(--tabs-tone-source) .87 .065 h)}a-tabs a-tab{--tab-fg: var(--tab-fg-rest);--tab-bg: transparent}a-tabs:not([orientation=vertical]) a-tab{flex:1 1 0;max-width:max-content}@media(hover:hover)and (pointer:fine){a-tabs a-tab:hover{--tab-fg: var(--tab-fg-hover)}}a-tabs:not([priority=secondary],[priority=tertiary]){background:var(--tabs-track-bg);border:.5px solid var(--tabs-track-border);border-radius:6px;padding:1.5px}a-tabs:not([priority=secondary],[priority=tertiary]) a-tab:state(selected){--tab-fg: var(--tab-selected-text);--tab-bg: var(--tab-pill-bg);box-shadow:var(--tab-pill-shadow)}a-tabs[tone]:not([tone=""],[tone=neutral]),a-tab[tone]:not([tone=""],[tone=neutral]){--tab-fg-rest: var(--tab-rest-tone);--tab-fg-hover: var(--tab-selected-text)}a-tabs[priority=secondary]{border:.5px solid transparent;padding:1.5px}@media(hover:hover)and (pointer:fine){a-tabs[priority=secondary] a-tab:hover{--tab-fg: var(--tab-selected-text)}}a-tabs[priority=secondary] a-tab:state(selected){--tab-fg: var(--tab-selected-text);--tab-bg: var(--tab-secondary-bg);box-shadow:var(--tab-secondary-shadow)}a-tabs[priority=tertiary]{padding-inline:2px}a-tabs[priority=tertiary] a-tab{--tab-underline-size: 1px;--tab-underline-color: transparent;position:relative;border-radius:0}a-tabs[priority=tertiary]:not([orientation=vertical]) a-tab{padding-block:calc(var(--tab-padding-y) + 2px)}a-tabs[priority=tertiary] a-tab:after{content:"";position:absolute;inset:0;box-sizing:border-box;pointer-events:none;border-bottom:var(--tab-underline-size) solid var(--tab-underline-color);transition:border-width var(--tab-timing),border-color var(--tab-timing)}a-tabs[orientation=vertical][priority=tertiary] a-tab:after{border-bottom:0;border-inline-end:var(--tab-underline-size) solid var(--tab-underline-color)}@media(hover:hover)and (pointer:fine){a-tabs[priority=tertiary] a-tab:hover{--tab-fg: var(--tab-text-2)}}a-tabs[priority=tertiary] a-tab:state(selected){--tab-fg: var(--tab-selected-text);--tab-underline-size: 1px;--tab-underline-color: var(--tab-selected-text)}a-tabs[orientation=vertical]{flex-direction:column;align-items:stretch;max-width:none;overflow-x:hidden;overflow-y:auto;flex-shrink:0}a-tabs[orientation=vertical] a-tab{justify-content:flex-start;flex-shrink:0}@supports (anchor-scope: all){a-tabs:not([noslide]){position:relative;anchor-scope:all}a-tabs:not([noslide]) a-tab{position:relative;z-index:1}a-tabs:not([noslide]) a-tab:state(selected){anchor-name:--tabs-active}a-tabs:not([noslide]):before{content:"";position:absolute;z-index:0;box-sizing:border-box;position-anchor:--tabs-active;top:anchor(top);right:anchor(right);bottom:anchor(bottom);left:anchor(left);transition:top var(--tab-timing),right var(--tab-timing),bottom var(--tab-timing),left var(--tab-timing)}a-tabs:not([noslide]):not(:has(a-tab:state(selected))):before{display:none}@media(prefers-reduced-motion:reduce){a-tabs:not([noslide]):before{transition:none}}a-tabs:not([noslide]) a-tab:state(selected){--tab-bg: transparent;box-shadow:none}a-tabs:not([noslide],[priority=secondary],[priority=tertiary]):before{background:var(--tab-pill-bg);border-radius:var(--tab-radius);box-shadow:var(--tab-pill-shadow)}a-tabs:not([noslide])[priority=secondary]:before{background:var(--tab-secondary-bg);border-radius:var(--tab-radius);box-shadow:var(--tab-secondary-shadow)}a-tabs:not([noslide])[priority=tertiary] a-tab:state(selected){--tab-underline-size: 1px;--tab-underline-color: transparent}a-tabs:not([noslide])[priority=tertiary]:before{z-index:2;border-bottom:1px solid var(--tab-selected-text)}a-tabs[orientation=vertical]:not([noslide])[priority=tertiary]:before{border-bottom:0;border-inline-end:1px solid var(--tab-selected-text)}}.dark a-tabs{--tabs-track-bg: color-mix(in oklch, #e4d1ef 10%, transparent);--tab-secondary-bg: color-mix(in oklch, #e4d1ef 6%, transparent)}.dark a-tabs[tone]:not([tone=""],[tone=neutral]),.dark a-tab[tone]:not([tone=""],[tone=neutral]){--tabs-track-bg: oklch(from var(--tabs-tone-source) .8 .12 h / .12);--tab-secondary-bg: oklch(from var(--tabs-tone-source) .8 .12 h / .06);--tab-pill-shadow: 0 0 3px 0 oklch(from var(--tabs-tone-source) .8 .1 h / .45)}.dark a-tabs:has(>a-tab[tone]:state(selected):not([tone=""],[tone=neutral])){--tab-secondary-bg: oklch(from var(--tabs-tone-source) .8 .12 h / .06);--tab-pill-shadow: 0 0 3px 0 oklch(from var(--tabs-tone-source) .8 .1 h / .45)}.dark a-tabs[tone]:not([tone=""],[tone=neutral],[tone=brand],[tone=info],[tone=success],[tone=warning],[tone=critical]),.dark a-tab[tone]:not([tone=""],[tone=neutral],[tone=brand],[tone=info],[tone=success],[tone=warning],[tone=critical]){--tab-selected-text: oklch(from var(--tabs-tone-source) .83 .1 h);--tab-text-2: oklch(from var(--tabs-tone-source) .76 .11 h);--tab-rest-tone: oklch(from var(--tabs-tone-source) .76 .11 h);--tabs-track-border: oklch(from var(--tabs-tone-source) .25 .057 h);--tab-secondary-border: oklch(from var(--tabs-tone-source) .33 .087 h)}}
@@ -0,0 +1,31 @@
1
+ import { HTMLElementBase } from "../anta_helpers";
2
+ import "./a-tabs.css";
3
+ export declare class ATabsElement extends HTMLElementBase {
4
+ static observedAttributes: string[];
5
+ private internals?;
6
+ private uncontrolledValue;
7
+ private seeded;
8
+ private observer?;
9
+ private lastSelected;
10
+ private alive;
11
+ /** The selected tab's value, or `null` when nothing is selected. */
12
+ get value(): string | null;
13
+ constructor();
14
+ connectedCallback(): void;
15
+ disconnectedCallback(): void;
16
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
17
+ formDisabledCallback(disabled: boolean): void;
18
+ private get currentValue();
19
+ private get isDisabled();
20
+ private get isVertical();
21
+ private get tabs();
22
+ private sync;
23
+ private requestSelect;
24
+ private emitChange;
25
+ /** Dispatch the shared cancelable `statechange`. `next`/`prev` are values
26
+ * (`null` when nothing is selected). Returns false if a listener vetoed. */
27
+ private emitStateChange;
28
+ private onClick;
29
+ private onKeyDown;
30
+ }
31
+ export declare function register_a_tabs(): void;
@@ -0,0 +1,157 @@
1
+ import { HTMLElementBase } from "../anta_helpers";
2
+ import "./a-tabs.css";
3
+ class ATabsElement extends HTMLElementBase {
4
+ static observedAttributes = ["state", "disabled", "orientation"];
5
+ internals;
6
+ uncontrolledValue = null;
7
+ seeded = false;
8
+ observer;
9
+ // The tab last scrolled into view — so scroll-into-view fires only when the SELECTION
10
+ // changes, not on every sync() (orientation / disabled changes call sync() too).
11
+ lastSelected = null;
12
+ // True after the first connect — gates the native `change` event so it never fires
13
+ // for the initial seed, and gates scroll-into-view so mounting doesn't jump the page.
14
+ alive = false;
15
+ /** The selected tab's value, or `null` when nothing is selected. */
16
+ get value() {
17
+ return this.currentValue;
18
+ }
19
+ constructor() {
20
+ super();
21
+ this.internals = this.attachInternals?.();
22
+ }
23
+ connectedCallback() {
24
+ if (!this.seeded) {
25
+ this.uncontrolledValue = this.getAttribute("default-state");
26
+ this.seeded = true;
27
+ }
28
+ this.addEventListener("click", this.onClick);
29
+ this.addEventListener("keydown", this.onKeyDown);
30
+ this.observer ??= new this.view.MutationObserver((records) => {
31
+ const touchedTabs = records.some(
32
+ (rec) => [...rec.addedNodes, ...rec.removedNodes].some(
33
+ (n) => n.nodeName === "A-TAB" || n.querySelector?.("a-tab") != null
34
+ )
35
+ );
36
+ if (touchedTabs) this.sync();
37
+ });
38
+ this.observer.observe(this, { childList: true, subtree: true });
39
+ this.sync();
40
+ this.alive = true;
41
+ }
42
+ disconnectedCallback() {
43
+ this.observer?.disconnect();
44
+ }
45
+ attributeChangedCallback(name, oldValue, newValue) {
46
+ this.sync();
47
+ if (name === "state" && this.alive && newValue !== oldValue) this.emitChange();
48
+ }
49
+ formDisabledCallback(disabled) {
50
+ if (disabled) this.internals?.states.add("disabled");
51
+ else this.internals?.states.delete("disabled");
52
+ this.sync();
53
+ }
54
+ // Controlled when `state` is present; otherwise the in-memory uncontrolled value.
55
+ get currentValue() {
56
+ return this.hasAttribute("state") ? this.getAttribute("state") : this.uncontrolledValue;
57
+ }
58
+ get isDisabled() {
59
+ return this.hasAttribute("disabled") || (this.internals?.states.has("disabled") ?? false);
60
+ }
61
+ get isVertical() {
62
+ return this.getAttribute("orientation") === "vertical";
63
+ }
64
+ get tabs() {
65
+ return Array.from(this.querySelectorAll("a-tab"));
66
+ }
67
+ sync = () => {
68
+ const value = this.currentValue;
69
+ const tabs = this.tabs;
70
+ const selectedEl = tabs.find((t) => t.value === value && value != null) ?? null;
71
+ for (const t of tabs) t.selected = t === selectedEl;
72
+ if (this.internals && "ariaActiveDescendantElement" in this.internals) {
73
+ this.internals.ariaActiveDescendantElement = selectedEl;
74
+ }
75
+ if (this.alive && selectedEl && selectedEl !== this.lastSelected) {
76
+ selectedEl.scrollIntoView({ block: "nearest", inline: "nearest" });
77
+ }
78
+ this.lastSelected = selectedEl;
79
+ };
80
+ // The shared state algorithm: fire the cancelable `statechange` *before* applying.
81
+ // Controlled never self-applies; uncontrolled applies unless vetoed.
82
+ requestSelect(next) {
83
+ const prev = this.currentValue;
84
+ if (next === prev) return;
85
+ const ok = this.emitStateChange(next, prev);
86
+ if (this.hasAttribute("state")) return;
87
+ if (ok) {
88
+ this.uncontrolledValue = next;
89
+ this.sync();
90
+ this.emitChange();
91
+ }
92
+ }
93
+ // Native `change`, fired *after* a selection applies (user pick or a controlled
94
+ // `state` update) — the post-apply counterpart to the cancelable `statechange`.
95
+ emitChange() {
96
+ this.dispatchEvent(new Event("change", { bubbles: true }));
97
+ }
98
+ /** Dispatch the shared cancelable `statechange`. `next`/`prev` are values
99
+ * (`null` when nothing is selected). Returns false if a listener vetoed. */
100
+ emitStateChange(next, prev) {
101
+ return this.dispatchEvent(
102
+ new CustomEvent("statechange", {
103
+ cancelable: true,
104
+ bubbles: true,
105
+ composed: true,
106
+ detail: { next, prev }
107
+ })
108
+ );
109
+ }
110
+ onClick = (e) => {
111
+ if (this.isDisabled) return;
112
+ const tab = e.target?.closest("a-tab");
113
+ if (!tab || tab.hasAttribute("disabled")) return;
114
+ tab.focus();
115
+ this.requestSelect(tab.value);
116
+ };
117
+ onKeyDown = (e) => {
118
+ if (this.isDisabled) return;
119
+ const enabled = this.tabs.filter((t) => !t.hasAttribute("disabled"));
120
+ if (enabled.length === 0) return;
121
+ const focused = e.target?.closest("a-tab");
122
+ if (e.key === " " || e.key === "Enter") {
123
+ if (focused && enabled.includes(focused)) {
124
+ e.preventDefault();
125
+ this.requestSelect(focused.value);
126
+ }
127
+ return;
128
+ }
129
+ if (e.key === "Home" || e.key === "End") {
130
+ e.preventDefault();
131
+ const target = e.key === "Home" ? enabled[0] : enabled[enabled.length - 1];
132
+ target.focus();
133
+ this.requestSelect(target.value);
134
+ return;
135
+ }
136
+ const forward = e.key === (this.isVertical ? "ArrowDown" : "ArrowRight");
137
+ const back = e.key === (this.isVertical ? "ArrowUp" : "ArrowLeft");
138
+ if (!forward && !back) return;
139
+ e.preventDefault();
140
+ let i = focused ? enabled.indexOf(focused) : -1;
141
+ if (i === -1) i = enabled.findIndex((t) => t.value === this.currentValue);
142
+ if (i === -1) i = 0;
143
+ const next = enabled[(i + (forward ? 1 : -1) + enabled.length) % enabled.length];
144
+ next.focus();
145
+ this.requestSelect(next.value);
146
+ };
147
+ }
148
+ function register_a_tabs() {
149
+ if (typeof customElements === "undefined") return;
150
+ if (!customElements.get("a-tabs"))
151
+ customElements.define("a-tabs", ATabsElement);
152
+ }
153
+ register_a_tabs();
154
+ export {
155
+ ATabsElement,
156
+ register_a_tabs
157
+ };
@@ -1 +1 @@
1
- @layer anta{a-text{--text-color: var(--text-2);--text-link-color: var(--link-color);--text-link-hover: var(--link-color-hover);display:block;color:var(--text-color);font-size:15px;line-height:20px;text-wrap:pretty;font-feature-settings:"ss02","ss05"}a-text[size=small]{font-size:13px;line-height:16px}a-text[size=medium]{font-size:15px;line-height:20px}a-text[size=large]{font-size:17px;line-height:24px}a-text[inline]{display:inline-block}a-text[truncate]{min-width:0}a-text[priority=primary]{--text-color: var(--text-1)}a-text[priority=tertiary]{--text-color: var(--text-3);--text-link-color: currentColor;--text-link-hover: var(--text-2)}a-text[priority=quaternary]{--text-color: var(--text-4);--text-link-color: currentColor;--text-link-hover: var(--text-3)}a-text[priority=quinary]{--text-color: var(--text-5);--text-link-color: currentColor;--text-link-hover: var(--text-4)}a-text[tone=brand]{--text-color: var(--text-2-brand);--text-link-color: currentColor;--text-link-hover: var(--text-1-brand)}a-text[tone=brand][priority=primary]{--text-color: var(--text-1-brand)}a-text[tone=brand][priority=tertiary]{--text-color: var(--text-3-brand);--text-link-hover: var(--text-2-brand)}a-text[tone=brand][priority=quaternary]{--text-color: var(--text-4-brand);--text-link-hover: var(--text-3-brand)}a-text[tone=brand][priority=quinary]{--text-color: var(--text-5-brand);--text-link-hover: var(--text-4-brand)}a-text[tone=success]{--text-color: var(--text-2-success);--text-link-color: currentColor;--text-link-hover: var(--text-1-success)}a-text[tone=success][priority=primary]{--text-color: var(--text-1-success)}a-text[tone=success][priority=tertiary]{--text-color: var(--text-3-success);--text-link-hover: var(--text-2-success)}a-text[tone=success][priority=quaternary]{--text-color: var(--text-4-success);--text-link-hover: var(--text-3-success)}a-text[tone=success][priority=quinary]{--text-color: var(--text-5-success);--text-link-hover: var(--text-4-success)}a-text[tone=critical]{--text-color: var(--text-2-critical);--text-link-color: currentColor;--text-link-hover: var(--text-1-critical)}a-text[tone=critical][priority=primary]{--text-color: var(--text-1-critical)}a-text[tone=critical][priority=tertiary]{--text-color: var(--text-3-critical);--text-link-hover: var(--text-2-critical)}a-text[tone=critical][priority=quaternary]{--text-color: var(--text-4-critical);--text-link-hover: var(--text-3-critical)}a-text[tone=critical][priority=quinary]{--text-color: var(--text-5-critical);--text-link-hover: var(--text-4-critical)}a-text[tone=warning]{--text-color: var(--text-2-warning);--text-link-color: currentColor;--text-link-hover: var(--text-1-warning)}a-text[tone=warning][priority=primary]{--text-color: var(--text-1-warning)}a-text[tone=warning][priority=tertiary]{--text-color: var(--text-3-warning);--text-link-hover: var(--text-2-warning)}a-text[tone=warning][priority=quaternary]{--text-color: var(--text-4-warning);--text-link-hover: var(--text-3-warning)}a-text[tone=warning][priority=quinary]{--text-color: var(--text-5-warning);--text-link-hover: var(--text-4-warning)}a-text[tone=info]{--text-color: var(--text-2-info);--text-link-color: currentColor;--text-link-hover: var(--text-1-info)}a-text[tone=info][priority=primary]{--text-color: var(--text-1-info)}a-text[tone=info][priority=tertiary]{--text-color: var(--text-3-info);--text-link-hover: var(--text-2-info)}a-text[tone=info][priority=quaternary]{--text-color: var(--text-4-info);--text-link-hover: var(--text-3-info)}a-text[tone=info][priority=quinary]{--text-color: var(--text-5-info);--text-link-hover: var(--text-4-info)}a-text a,a-text a:link,a-text a:visited{color:var(--text-link-color)}@media(hover:hover)and (pointer:fine){a-text a:hover{color:var(--text-link-hover);text-decoration-color:var(--text-link-hover)}}}
1
+ @layer anta{a-text{--text-color: var(--text-2);--text-link-color: var(--link-color);--text-link-hover: var(--link-color-hover);display:block;color:var(--text-color);font-size:15px;line-height:20px;text-wrap:pretty;font-feature-settings:"ss02","ss05","tnum"}a-text[size=small]{font-size:13px;line-height:16px}a-text[size=medium]{font-size:15px;line-height:20px}a-text[size=large]{font-size:17px;line-height:24px}a-text[inline]{display:inline-block}a-text[truncate]{min-width:0}a-text[priority=primary]{--text-color: var(--text-1)}a-text[priority=tertiary]{--text-color: var(--text-3);--text-link-color: currentColor;--text-link-hover: var(--text-2)}a-text[priority=quaternary]{--text-color: var(--text-4);--text-link-color: currentColor;--text-link-hover: var(--text-3)}a-text[priority=quinary]{--text-color: var(--text-5);--text-link-color: currentColor;--text-link-hover: var(--text-4)}a-text[tone=brand]{--text-color: var(--text-2-brand);--text-link-color: currentColor;--text-link-hover: var(--text-1-brand)}a-text[tone=brand][priority=primary]{--text-color: var(--text-1-brand)}a-text[tone=brand][priority=tertiary]{--text-color: var(--text-3-brand);--text-link-hover: var(--text-2-brand)}a-text[tone=brand][priority=quaternary]{--text-color: var(--text-4-brand);--text-link-hover: var(--text-3-brand)}a-text[tone=brand][priority=quinary]{--text-color: var(--text-5-brand);--text-link-hover: var(--text-4-brand)}a-text[tone=success]{--text-color: var(--text-2-success);--text-link-color: currentColor;--text-link-hover: var(--text-1-success)}a-text[tone=success][priority=primary]{--text-color: var(--text-1-success)}a-text[tone=success][priority=tertiary]{--text-color: var(--text-3-success);--text-link-hover: var(--text-2-success)}a-text[tone=success][priority=quaternary]{--text-color: var(--text-4-success);--text-link-hover: var(--text-3-success)}a-text[tone=success][priority=quinary]{--text-color: var(--text-5-success);--text-link-hover: var(--text-4-success)}a-text[tone=critical]{--text-color: var(--text-2-critical);--text-link-color: currentColor;--text-link-hover: var(--text-1-critical)}a-text[tone=critical][priority=primary]{--text-color: var(--text-1-critical)}a-text[tone=critical][priority=tertiary]{--text-color: var(--text-3-critical);--text-link-hover: var(--text-2-critical)}a-text[tone=critical][priority=quaternary]{--text-color: var(--text-4-critical);--text-link-hover: var(--text-3-critical)}a-text[tone=critical][priority=quinary]{--text-color: var(--text-5-critical);--text-link-hover: var(--text-4-critical)}a-text[tone=warning]{--text-color: var(--text-2-warning);--text-link-color: currentColor;--text-link-hover: var(--text-1-warning)}a-text[tone=warning][priority=primary]{--text-color: var(--text-1-warning)}a-text[tone=warning][priority=tertiary]{--text-color: var(--text-3-warning);--text-link-hover: var(--text-2-warning)}a-text[tone=warning][priority=quaternary]{--text-color: var(--text-4-warning);--text-link-hover: var(--text-3-warning)}a-text[tone=warning][priority=quinary]{--text-color: var(--text-5-warning);--text-link-hover: var(--text-4-warning)}a-text[tone=info]{--text-color: var(--text-2-info);--text-link-color: currentColor;--text-link-hover: var(--text-1-info)}a-text[tone=info][priority=primary]{--text-color: var(--text-1-info)}a-text[tone=info][priority=tertiary]{--text-color: var(--text-3-info);--text-link-hover: var(--text-2-info)}a-text[tone=info][priority=quaternary]{--text-color: var(--text-4-info);--text-link-hover: var(--text-3-info)}a-text[tone=info][priority=quinary]{--text-color: var(--text-5-info);--text-link-hover: var(--text-4-info)}a-text a,a-text a:link,a-text a:visited{color:var(--text-link-color)}@media(hover:hover)and (pointer:fine){a-text a:hover{color:var(--text-link-hover);text-decoration-color:var(--text-link-hover)}}}
@@ -1 +1 @@
1
- @layer anta{a-title{display:block;color:var(--text-1);font-weight:584.62;letter-spacing:0;text-wrap:balance;font-feature-settings:"ss02","ss05"}a-title a-icon{transform:translateY(-.05em)}a-title[level="1"]{font-size:28px;line-height:32px;margin-block-start:0;margin-block-end:16px}a-title[level="2"]{font-size:24px;line-height:28px;margin-block-start:32px;margin-block-end:12px}a-title[level="3"]{font-size:20px;line-height:24px;margin-block-start:24px;margin-block-end:12px}a-title[level="4"]{font-size:17px;line-height:20px;margin-block-start:20px;margin-block-end:8px}a-title[level="5"]{font-size:15px;line-height:20px;margin-block-start:16px;margin-block-end:8px}a-title[level="6"]{font-size:13px;line-height:16px;margin-block-start:16px;margin-block-end:4px}a-title[priority=secondary]{color:var(--text-2)}a-title[priority=tertiary]{color:var(--text-3)}a-title[priority=quaternary]{color:var(--text-4)}a-title[priority=quinary]{color:var(--text-5)}a-title[tone=brand]{color:var(--text-1-brand)}a-title[tone=brand][priority=secondary]{color:var(--text-2-brand)}a-title[tone=brand][priority=tertiary]{color:var(--text-3-brand)}a-title[tone=brand][priority=quaternary]{color:var(--text-4-brand)}a-title[tone=brand][priority=quinary]{color:var(--text-5-brand)}a-title[tone=success]{color:var(--text-1-success)}a-title[tone=success][priority=secondary]{color:var(--text-2-success)}a-title[tone=success][priority=tertiary]{color:var(--text-3-success)}a-title[tone=success][priority=quaternary]{color:var(--text-4-success)}a-title[tone=success][priority=quinary]{color:var(--text-5-success)}a-title[tone=critical]{color:var(--text-1-critical)}a-title[tone=critical][priority=secondary]{color:var(--text-2-critical)}a-title[tone=critical][priority=tertiary]{color:var(--text-3-critical)}a-title[tone=critical][priority=quaternary]{color:var(--text-4-critical)}a-title[tone=critical][priority=quinary]{color:var(--text-5-critical)}a-title[tone=warning]{color:var(--text-1-warning)}a-title[tone=warning][priority=secondary]{color:var(--text-2-warning)}a-title[tone=warning][priority=tertiary]{color:var(--text-3-warning)}a-title[tone=warning][priority=quaternary]{color:var(--text-4-warning)}a-title[tone=warning][priority=quinary]{color:var(--text-5-warning)}a-title[tone=info]{color:var(--text-1-info)}a-title[tone=info][priority=secondary]{color:var(--text-2-info)}a-title[tone=info][priority=tertiary]{color:var(--text-3-info)}a-title[tone=info][priority=quaternary]{color:var(--text-4-info)}a-title[tone=info][priority=quinary]{color:var(--text-5-info)}}
1
+ @layer anta{a-title{display:block;color:var(--text-1);font-weight:584.62;letter-spacing:0;text-wrap:balance;font-feature-settings:"ss02","ss05","tnum"}a-title a-icon{transform:translateY(-.05em)}a-title[level="1"]{font-size:28px;line-height:32px;margin-block-start:0;margin-block-end:16px}a-title[level="2"]{font-size:24px;line-height:28px;margin-block-start:32px;margin-block-end:12px}a-title[level="3"]{font-size:20px;line-height:24px;margin-block-start:24px;margin-block-end:12px}a-title[level="4"]{font-size:17px;line-height:20px;margin-block-start:20px;margin-block-end:8px}a-title[level="5"]{font-size:15px;line-height:20px;margin-block-start:16px;margin-block-end:8px}a-title[level="6"]{font-size:13px;line-height:16px;margin-block-start:16px;margin-block-end:4px}a-title[priority=secondary]{color:var(--text-2)}a-title[priority=tertiary]{color:var(--text-3)}a-title[priority=quaternary]{color:var(--text-4)}a-title[priority=quinary]{color:var(--text-5)}a-title[tone=brand]{color:var(--text-1-brand)}a-title[tone=brand][priority=secondary]{color:var(--text-2-brand)}a-title[tone=brand][priority=tertiary]{color:var(--text-3-brand)}a-title[tone=brand][priority=quaternary]{color:var(--text-4-brand)}a-title[tone=brand][priority=quinary]{color:var(--text-5-brand)}a-title[tone=success]{color:var(--text-1-success)}a-title[tone=success][priority=secondary]{color:var(--text-2-success)}a-title[tone=success][priority=tertiary]{color:var(--text-3-success)}a-title[tone=success][priority=quaternary]{color:var(--text-4-success)}a-title[tone=success][priority=quinary]{color:var(--text-5-success)}a-title[tone=critical]{color:var(--text-1-critical)}a-title[tone=critical][priority=secondary]{color:var(--text-2-critical)}a-title[tone=critical][priority=tertiary]{color:var(--text-3-critical)}a-title[tone=critical][priority=quaternary]{color:var(--text-4-critical)}a-title[tone=critical][priority=quinary]{color:var(--text-5-critical)}a-title[tone=warning]{color:var(--text-1-warning)}a-title[tone=warning][priority=secondary]{color:var(--text-2-warning)}a-title[tone=warning][priority=tertiary]{color:var(--text-3-warning)}a-title[tone=warning][priority=quaternary]{color:var(--text-4-warning)}a-title[tone=warning][priority=quinary]{color:var(--text-5-warning)}a-title[tone=info]{color:var(--text-1-info)}a-title[tone=info][priority=secondary]{color:var(--text-2-info)}a-title[tone=info][priority=tertiary]{color:var(--text-3-info)}a-title[tone=info][priority=quaternary]{color:var(--text-4-info)}a-title[tone=info][priority=quinary]{color:var(--text-5-info)}}
@@ -68,6 +68,24 @@ export declare class ATooltipElement extends HTMLElementBase {
68
68
  private get isPinned();
69
69
  private get prefersTop();
70
70
  private get delay();
71
+ /** When set, the tooltip only shows if its resolved target is actually
72
+ * truncated/clipped; a fitting label gets no tooltip. */
73
+ private get truncatedOnly();
74
+ /** The element whose overflow decides whether the tooltip shows: a
75
+ * `truncated-selector` resolved within the anchor wins; else the first of
76
+ * Anta's ellipsizing label parts inside the anchor; else the anchor itself
77
+ * (which may be the clipping box for a hand-authored target). */
78
+ private resolveTruncationTarget;
79
+ /** True when the resolved target overflows its box (horizontal ellipsis or
80
+ * vertical clamp). A 1px threshold absorbs sub-pixel rounding; a zero-size
81
+ * (hidden / detached) target counts as not truncated. Measured fresh on each
82
+ * show attempt, so late fonts / resizes self-correct with no observer. */
83
+ private isTargetTruncated;
84
+ /** True when there's nothing worth showing: no element children (so an
85
+ * icon/image-only bubble still counts as content) and no non-whitespace text
86
+ * (so formatting whitespace / an empty conditional doesn't open a blank bubble).
87
+ * Re-checked on every show(), so a tooltip populated later self-corrects. */
88
+ private isEmpty;
71
89
  private positionFrame?;
72
90
  private pendingPosition?;
73
91
  private schedulePosition;
@@ -12,6 +12,7 @@ const PROX_NEAR = 10;
12
12
  const PROX_FAR = 100;
13
13
  const PROX_FADE_MS = 60;
14
14
  const ENTER_TOUCH_DELAY = 500;
15
+ const TRUNCATING_PARTS = "a-tab-label, a-button-label";
15
16
  const LEAVE_TOUCH_DELAY = 1500;
16
17
  const TOUCH_SLOP = 10;
17
18
  let currentOpen = null;
@@ -218,6 +219,46 @@ class ATooltipElement extends HTMLElementBase {
218
219
  const n = parseInt(attr, 10);
219
220
  return Number.isFinite(n) ? n : DEFAULT_DELAY;
220
221
  }
222
+ // --- truncated-only gating (UI-thread layout READS — no mutation) ---
223
+ /** When set, the tooltip only shows if its resolved target is actually
224
+ * truncated/clipped; a fitting label gets no tooltip. */
225
+ get truncatedOnly() {
226
+ return this.hasAttribute("truncated-only");
227
+ }
228
+ /** The element whose overflow decides whether the tooltip shows: a
229
+ * `truncated-selector` resolved within the anchor wins; else the first of
230
+ * Anta's ellipsizing label parts inside the anchor; else the anchor itself
231
+ * (which may be the clipping box for a hand-authored target). */
232
+ resolveTruncationTarget() {
233
+ const anchor = this.anchor;
234
+ if (!anchor) return null;
235
+ const sel = this.getAttribute("truncated-selector");
236
+ if (sel) {
237
+ try {
238
+ const found = anchor.querySelector(sel);
239
+ if (found) return found;
240
+ } catch {
241
+ }
242
+ }
243
+ return anchor.querySelector(TRUNCATING_PARTS) ?? anchor;
244
+ }
245
+ /** True when the resolved target overflows its box (horizontal ellipsis or
246
+ * vertical clamp). A 1px threshold absorbs sub-pixel rounding; a zero-size
247
+ * (hidden / detached) target counts as not truncated. Measured fresh on each
248
+ * show attempt, so late fonts / resizes self-correct with no observer. */
249
+ isTargetTruncated() {
250
+ const t = this.resolveTruncationTarget();
251
+ if (!t) return false;
252
+ if (t.clientWidth === 0 && t.clientHeight === 0) return false;
253
+ return t.scrollWidth - t.clientWidth > 1 || t.scrollHeight - t.clientHeight > 1;
254
+ }
255
+ /** True when there's nothing worth showing: no element children (so an
256
+ * icon/image-only bubble still counts as content) and no non-whitespace text
257
+ * (so formatting whitespace / an empty conditional doesn't open a blank bubble).
258
+ * Re-checked on every show(), so a tooltip populated later self-corrects. */
259
+ isEmpty() {
260
+ return this.children.length === 0 && (this.textContent ?? "").trim() === "";
261
+ }
221
262
  // --- positioning (sets only the shadow container's own transform) ---
222
263
  // Single pending position frame: a burst of mousemoves within one frame
223
264
  // coalesces to ONE layout read + transform write, using the LATEST geometry
@@ -289,6 +330,8 @@ class ATooltipElement extends HTMLElementBase {
289
330
  // --- show / hide ---
290
331
  show = (e) => {
291
332
  if (!this.anchor) return;
333
+ if (this.isEmpty()) return;
334
+ if (this.truncatedOnly && !this.isTargetTruncated()) return;
292
335
  this.cancelHide();
293
336
  if (currentOpen && currentOpen !== this && this.anchor.contains(currentOpen.anchor)) {
294
337
  return;
@@ -400,6 +443,8 @@ class ATooltipElement extends HTMLElementBase {
400
443
  /** Open now if another tooltip is hot (skip delay), else (re)arm the delayed
401
444
  * show with the latest cursor event. */
402
445
  trigger(e) {
446
+ if (this.isEmpty()) return;
447
+ if (this.truncatedOnly && !this.isTargetTruncated()) return;
403
448
  if (isHot()) {
404
449
  this.debouncedShow?.cancel();
405
450
  this.show(e);
@@ -27,5 +27,8 @@ export { AMenuElement, register_a_menu } from './a-menu';
27
27
  export { AMenuItemElement, register_a_menu_item } from './a-menu-item';
28
28
  export { AMenuSeparatorElement, register_a_menu_separator } from './a-menu-separator';
29
29
  export { AMenuGroupElement, register_a_menu_group } from './a-menu-group';
30
+ export { ATabElement, register_a_tab } from './a-tab';
31
+ export { ATabsElement, register_a_tabs } from './a-tabs';
30
32
  import './a-title.css';
31
33
  import './a-tag.css';
34
+ import './a-tabpanel.css';
@@ -12,8 +12,11 @@ import { AMenuElement, register_a_menu } from "./a-menu";
12
12
  import { AMenuItemElement, register_a_menu_item } from "./a-menu-item";
13
13
  import { AMenuSeparatorElement, register_a_menu_separator } from "./a-menu-separator";
14
14
  import { AMenuGroupElement, register_a_menu_group } from "./a-menu-group";
15
+ import { ATabElement, register_a_tab } from "./a-tab";
16
+ import { ATabsElement, register_a_tabs } from "./a-tabs";
15
17
  import "./a-title.css";
16
18
  import "./a-tag.css";
19
+ import "./a-tabpanel.css";
17
20
  export {
18
21
  AButtonElement,
19
22
  ACheckboxElement,
@@ -27,6 +30,8 @@ export {
27
30
  AProgressElement,
28
31
  ARadioElement,
29
32
  ARadioGroupElement,
33
+ ATabElement,
34
+ ATabsElement,
30
35
  ATextElement,
31
36
  ATooltipElement,
32
37
  register_a_button,
@@ -41,6 +46,8 @@ export {
41
46
  register_a_progress,
42
47
  register_a_radio,
43
48
  register_a_radio_group,
49
+ register_a_tab,
50
+ register_a_tabs,
44
51
  register_a_text,
45
52
  register_a_tooltip
46
53
  };