@antadesign/anta 0.1.1-dev.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +2 -2
  2. package/dist/components/Button.js +4 -3
  3. package/dist/components/Menu.d.ts +61 -0
  4. package/dist/components/Menu.js +34 -0
  5. package/dist/components/MenuGroup.d.ts +24 -0
  6. package/dist/components/MenuGroup.js +19 -0
  7. package/dist/components/MenuItem.d.ts +49 -0
  8. package/dist/components/MenuItem.js +40 -0
  9. package/dist/components/MenuSeparator.d.ts +14 -0
  10. package/dist/components/MenuSeparator.js +7 -0
  11. package/dist/components/Tag.d.ts +55 -0
  12. package/dist/components/Tag.js +40 -0
  13. package/dist/components/Text.d.ts +4 -2
  14. package/dist/components/Tooltip.d.ts +4 -2
  15. package/dist/elements/a-button.css +31 -8
  16. package/dist/elements/a-icon.shapes.css +4 -0
  17. package/dist/elements/a-icon.shapes.d.ts +2 -1
  18. package/dist/elements/a-icon.shapes.js +3 -1
  19. package/dist/elements/a-menu-group.css +21 -0
  20. package/dist/elements/a-menu-group.d.ts +13 -0
  21. package/dist/elements/a-menu-group.js +15 -0
  22. package/dist/elements/a-menu-item.css +162 -0
  23. package/dist/elements/a-menu-item.d.ts +27 -0
  24. package/dist/elements/a-menu-item.js +29 -0
  25. package/dist/elements/a-menu-separator.css +15 -0
  26. package/dist/elements/a-menu-separator.d.ts +12 -0
  27. package/dist/elements/a-menu-separator.js +15 -0
  28. package/dist/elements/a-menu.css +34 -0
  29. package/dist/elements/a-menu.d.ts +120 -0
  30. package/dist/elements/a-menu.js +649 -0
  31. package/dist/elements/a-tag.css +196 -0
  32. package/dist/elements/a-text.css +17 -13
  33. package/dist/elements/a-tooltip.css +21 -1
  34. package/dist/elements/a-tooltip.d.ts +7 -0
  35. package/dist/elements/a-tooltip.js +96 -19
  36. package/dist/elements/index.d.ts +2 -4
  37. package/dist/elements/index.js +1 -6
  38. package/dist/general_types.d.ts +15 -35
  39. package/dist/index.d.ts +2 -4
  40. package/dist/index.js +2 -4
  41. package/dist/jsx-runtime.d.ts +6 -5
  42. package/dist/reset.css +11 -7
  43. package/package.json +13 -15
@@ -0,0 +1,162 @@
1
+ @layer anta {
2
+ /* Before the custom element upgrades, items inside an <a-menu> would flash
3
+ inline in the page (the menu host hides them once defined — see
4
+ a-menu.css). Items used standalone stay hidden until defined too, which
5
+ is harmless. */
6
+ a-menu-item:not(:defined) {
7
+ display: none;
8
+ }
9
+
10
+ /* a-menu-item is light DOM — style it (and its a-icon / kbd /
11
+ a-menu-item-label children) directly with your own CSS. The custom
12
+ properties below are INTERNAL plumbing for the tone + dark variant
13
+ matrix, not a public theming API: `--menu-item-color` is the text colour
14
+ (the hover/active tint mixes from `currentColor`, so it tracks the tone
15
+ for free), and `--menu-item-hover` / `--menu-item-active` are the mix
16
+ percentages. */
17
+ a-menu-item {
18
+ --menu-item-color: var(--text-2);
19
+ --menu-item-icon-color: var(--text-3);
20
+ --menu-item-hover: 6%;
21
+ --menu-item-active: 10%;
22
+ --menu-item-padding-x: 8px;
23
+ --menu-item-padding-y: 6px;
24
+ /* Keyboard focus ring colour — matches a-button's --button-focus (brand). */
25
+ --menu-item-focus: #503cb4;
26
+
27
+ display: flex;
28
+ flex-shrink: 0;
29
+ align-items: center;
30
+ gap: 0.6ch;
31
+ box-sizing: border-box;
32
+ width: 100%;
33
+ min-height: 28px;
34
+ padding-block: var(--menu-item-padding-y);
35
+ padding-inline: var(--menu-item-padding-x);
36
+ border-radius: var(--radius-sm, 4px);
37
+ color: var(--menu-item-color);
38
+ background: transparent;
39
+ font: inherit;
40
+ font-size: 14px;
41
+ line-height: 16px;
42
+ text-align: left;
43
+ white-space: nowrap;
44
+ cursor: pointer;
45
+ user-select: none;
46
+ outline: none;
47
+ }
48
+
49
+ a-menu-item:hover,
50
+ a-menu-item:focus-visible {
51
+ background: color-mix(in oklch, currentColor var(--menu-item-hover), transparent);
52
+ }
53
+ a-menu-item:active {
54
+ background: color-mix(in oklch, currentColor var(--menu-item-active), transparent);
55
+ }
56
+
57
+ /* Keyboard focus ring (like a-button): the ring sits just OUTSIDE the row at
58
+ the same +1px offset buttons use. The surface's 4px padding gives it room
59
+ on the sides. Pairs with the focus background above, so a keyboard-focused
60
+ row reads as bg tint + ring (distinct from a plain hover, which is bg only). */
61
+ a-menu-item:focus-visible {
62
+ outline: 1px solid var(--menu-item-focus);
63
+ outline-offset: 1px;
64
+ }
65
+
66
+ /* Label takes the remaining width and truncates; the trailing kbd / chevron
67
+ stays pinned to the right. */
68
+ a-menu-item > a-menu-item-label {
69
+ flex: 1 1 auto;
70
+ min-width: 0;
71
+ overflow: hidden;
72
+ text-overflow: ellipsis;
73
+ }
74
+
75
+ a-menu-item > a-icon {
76
+ flex: none;
77
+ --icon-size: 16px;
78
+ color: var(--menu-item-icon-color);
79
+ }
80
+
81
+ /* Optical side padding (same idea as a-button): an icon at an edge needs
82
+ less breathing room than text, so trim ~2px off whichever side a (non-only)
83
+ icon occupies — leading icon trims the start, trailing icon trims the end.
84
+ Submenu items are NOT trimmed here: they keep the symmetric padding and the
85
+ chevron is shifted instead (see the [submenu] chevron rule above). */
86
+ a-menu-item:has(> a-icon:first-child:not(:only-child)) {
87
+ padding-inline-start: max(0px, var(--menu-item-padding-x) - 2px);
88
+ }
89
+ a-menu-item:has(> a-icon:last-child:not(:only-child)) {
90
+ padding-inline-end: max(0px, var(--menu-item-padding-x) - 2px);
91
+ }
92
+
93
+ /* Trailing keyboard hint — a plain light-DOM <kbd>. */
94
+ a-menu-item > kbd {
95
+ flex: none;
96
+ margin-left: auto;
97
+ padding-left: 12px;
98
+ color: var(--text-5);
99
+ font: inherit;
100
+ font-size: 1em;
101
+ letter-spacing: 0.1ch;
102
+ white-space: nowrap;
103
+ }
104
+
105
+ /* The submenu chevron hugs the right edge. The nested submenu <a-menu> never
106
+ renders in flow (it's a popover), so it doesn't participate in layout. */
107
+ a-menu-item[submenu] > a-icon:last-of-type {
108
+ margin-left: auto;
109
+ }
110
+
111
+ /* A trailing icon and a submenu chevron are both nudged toward the edge with
112
+ relative positioning (visual only, no reflow). A real trailing icon ALSO
113
+ gets the end-padding trim below (→ 6px), so it sits a touch closer to the
114
+ edge; the submenu keeps its symmetric padding (the nested <a-menu> is the
115
+ actual last child, so the trim rule doesn't fire for it). */
116
+ a-menu-item[submenu] > a-icon:last-of-type,
117
+ a-menu-item > a-icon:last-child:not(:only-child) {
118
+ position: relative;
119
+ inset-inline-end: -2px;
120
+ }
121
+
122
+ /* Semantic tones: text + icon take the tone's `--text-2-{tone}`. The
123
+ hover/active tint mixes from currentColor at the SAME percentages as the
124
+ neutral item, so a toned row doesn't read heavier than a gray one. */
125
+ a-menu-item[tone="brand"],
126
+ a-menu-item[tone="info"],
127
+ a-menu-item[tone="success"],
128
+ a-menu-item[tone="warning"],
129
+ a-menu-item[tone="critical"] {
130
+ --menu-item-icon-color: var(--menu-item-color);
131
+ }
132
+ a-menu-item[tone="brand"] { --menu-item-color: var(--text-2-brand); }
133
+ a-menu-item[tone="info"] { --menu-item-color: var(--text-2-info); }
134
+ a-menu-item[tone="success"] { --menu-item-color: var(--text-2-success); }
135
+ a-menu-item[tone="warning"] { --menu-item-color: var(--text-2-warning); }
136
+ a-menu-item[tone="critical"] { --menu-item-color: var(--text-2-critical); }
137
+
138
+ a-menu-item[disabled] {
139
+ --menu-item-color: var(--text-5);
140
+ --menu-item-icon-color: var(--text-5);
141
+ cursor: default;
142
+ pointer-events: none;
143
+ }
144
+
145
+ /* Future checkbox / radio items: a leading check slot keyed off the role +
146
+ checked presence. Architecture-ready — no item logic changes needed. */
147
+ a-menu-item[role="menuitemcheckbox"] > a-icon[data-check],
148
+ a-menu-item[role="menuitemradio"] > a-icon[data-check] {
149
+ visibility: hidden;
150
+ }
151
+ a-menu-item[role="menuitemcheckbox"][checked] > a-icon[data-check],
152
+ a-menu-item[role="menuitemradio"][checked] > a-icon[data-check] {
153
+ visibility: visible;
154
+ }
155
+
156
+ /* Hover/active read a touch stronger in dark. */
157
+ .dark a-menu-item {
158
+ --menu-item-hover: 13%;
159
+ --menu-item-active: 19%;
160
+ --menu-item-focus: #a897fc;
161
+ }
162
+ }
@@ -0,0 +1,27 @@
1
+ import { HTMLElementBase } from '../anta_helpers';
2
+ import './a-menu-item.css';
3
+ declare global {
4
+ interface Document {
5
+ hasKeyListenerForAMenuItem?: boolean;
6
+ }
7
+ }
8
+ /**
9
+ * `<a-menu-item>` — a single row inside an `<a-menu>`.
10
+ *
11
+ * No shadow DOM: its content (leading icon, label, trailing `kbd` /
12
+ * chevron) is slotted light DOM, styled entirely from `a-menu-item.css`.
13
+ * The element carries almost no logic — the parent `<a-menu>` owns
14
+ * click delegation, keyboard navigation, and the close contract. This
15
+ * class exists so the menu can identify items via `instanceof` and so
16
+ * Enter / Space on a focused item synthesizes a click (the single
17
+ * activation path that flows through the menu's click delegation).
18
+ *
19
+ * ARIA (`role="menuitem"`, `tabindex="-1"`, `aria-haspopup`/`aria-expanded`
20
+ * for submenu parents) is added by the `MenuItem` JSX wrapper, never here —
21
+ * the element must stay re-renderable from any reactive engine without
22
+ * churning host attributes.
23
+ */
24
+ export declare class AMenuItemElement extends HTMLElementBase {
25
+ connectedCallback(): void;
26
+ }
27
+ export declare function register_a_menu_item(): void;
@@ -0,0 +1,29 @@
1
+ import { HTMLElementBase } from "../anta_helpers";
2
+ import "./a-menu-item.css";
3
+ class AMenuItemElement extends HTMLElementBase {
4
+ connectedCallback() {
5
+ if (!document.hasKeyListenerForAMenuItem) {
6
+ document.addEventListener("keydown", handleKeyDown, true);
7
+ document.hasKeyListenerForAMenuItem = true;
8
+ }
9
+ }
10
+ }
11
+ function handleKeyDown(e) {
12
+ if (e.key !== "Enter" && e.key !== " ") return;
13
+ const el = e.target?.closest?.("a-menu-item");
14
+ if (!el) return;
15
+ e.preventDefault();
16
+ if (el.hasAttribute("disabled")) return;
17
+ el.click();
18
+ }
19
+ function register_a_menu_item() {
20
+ if (typeof customElements === "undefined") return;
21
+ if (!customElements.get("a-menu-item")) {
22
+ customElements.define("a-menu-item", AMenuItemElement);
23
+ }
24
+ }
25
+ register_a_menu_item();
26
+ export {
27
+ AMenuItemElement,
28
+ register_a_menu_item
29
+ };
@@ -0,0 +1,15 @@
1
+ @layer anta {
2
+ a-menu-separator:not(:defined) {
3
+ display: none;
4
+ }
5
+
6
+ a-menu-separator {
7
+ display: block;
8
+ flex-shrink: 0;
9
+ height: 1px;
10
+ margin: 2px 4px;
11
+ /* --border-5 is the most subtle border token, and it's mode-adaptive
12
+ (light in light mode, dark in dark mode), so no .dark override needed. */
13
+ background: var(--border-5);
14
+ }
15
+ }
@@ -0,0 +1,12 @@
1
+ import { HTMLElementBase } from '../anta_helpers';
2
+ import './a-menu-separator.css';
3
+ /**
4
+ * `<a-menu-separator>` — a thin divider between groups of menu items.
5
+ * No JS / no shadow DOM; styled entirely by `a-menu-separator.css`. The
6
+ * trivial class exists only so importing this module registers the tag and
7
+ * pulls its CSS along the granular entry point. `role="separator"` is added
8
+ * by the `MenuSeparator` JSX wrapper.
9
+ */
10
+ export declare class AMenuSeparatorElement extends HTMLElementBase {
11
+ }
12
+ export declare function register_a_menu_separator(): void;
@@ -0,0 +1,15 @@
1
+ import { HTMLElementBase } from "../anta_helpers";
2
+ import "./a-menu-separator.css";
3
+ class AMenuSeparatorElement extends HTMLElementBase {
4
+ }
5
+ function register_a_menu_separator() {
6
+ if (typeof customElements === "undefined") return;
7
+ if (!customElements.get("a-menu-separator")) {
8
+ customElements.define("a-menu-separator", AMenuSeparatorElement);
9
+ }
10
+ }
11
+ register_a_menu_separator();
12
+ export {
13
+ AMenuSeparatorElement,
14
+ register_a_menu_separator
15
+ };
@@ -0,0 +1,34 @@
1
+ @layer anta {
2
+ /* Before the element upgrades, <a-menu> is an unknown inline element and
3
+ its items would flash inline in the page. Hide it until defined; once
4
+ defined the shadow `:host { display: contents }` governs and content
5
+ renders only inside the popover surface via the slot. */
6
+ a-menu:not(:defined) {
7
+ display: none;
8
+ }
9
+
10
+ /* Only the surface "chrome" is exposed as tokens — it lives inside the
11
+ shadow popover and is unreachable from plain consumer CSS. Item styling
12
+ lives in a-menu-item.css (slotted light DOM, already styleable). These
13
+ custom properties inherit across the shadow boundary into the surface
14
+ (see a-menu.ts). The host draws nothing and stays out of layout. */
15
+ a-menu {
16
+ --menu-bg: color-mix(in oklch, var(--bg-1) 90%, transparent);
17
+ --menu-radius: var(--radius-md, 8px);
18
+ --menu-border: 1px solid var(--border-3);
19
+ --menu-shadow:
20
+ 0 2px 6px color-mix(in oklch, var(--text-1) 8%, transparent),
21
+ 0 8px 24px color-mix(in oklch, var(--text-1) 10%, transparent);
22
+ --menu-padding: 4px;
23
+ --menu-min-width: 88px;
24
+ --menu-backdrop-filter: blur(20px);
25
+ }
26
+
27
+ .dark a-menu {
28
+ --menu-bg: color-mix(in oklch, var(--bg-1) 78%, transparent);
29
+ --menu-shadow:
30
+ 0 2px 8px color-mix(in oklch, black 50%, transparent),
31
+ 0 12px 32px color-mix(in oklch, black 40%, transparent),
32
+ inset 0 0 0 1px color-mix(in oklch, white 8%, transparent);
33
+ }
34
+ }
@@ -0,0 +1,120 @@
1
+ import { HTMLElementBase } from '../anta_helpers';
2
+ import './a-menu.css';
3
+ export declare class AMenuElement extends HTMLElementBase {
4
+ static observedAttributes: string[];
5
+ /** Shadow-internal popover surface — the only thing we ever mutate. */
6
+ surface: HTMLDivElement;
7
+ listening: boolean;
8
+ private _shown;
9
+ private teardown?;
10
+ private openTimer?;
11
+ private closeTimer?;
12
+ private typeBuffer;
13
+ private typeTimer?;
14
+ constructor();
15
+ connectedCallback(): void;
16
+ disconnectedCallback(): void;
17
+ attributeChangedCallback(name: string): void;
18
+ /** Apply the controlled `state` attribute to actual visibility, silently.
19
+ * Absent → uncontrolled (no-op here; triggers manage it). */
20
+ private syncState;
21
+ /** Controlled iff the consumer is managing the `state` attribute. */
22
+ get isControlled(): boolean;
23
+ private get view();
24
+ private get doc();
25
+ get isSubmenu(): boolean;
26
+ private get isContext();
27
+ private get isCoord();
28
+ private get isHover();
29
+ private get offset();
30
+ private get placement();
31
+ /** Root menu: the previous element sibling is the trigger. Submenu: the
32
+ * enclosing menu item. One deterministic rule per case — no ambiguity. */
33
+ get triggerAnchor(): HTMLElement | null;
34
+ /** For a submenu: the menu that contains its anchor item. */
35
+ private get ownerMenu();
36
+ get isOpen(): boolean;
37
+ /** Skip elements that can't actually take focus — `display:none` (incl. a
38
+ * closed submenu's contents), `visibility:hidden`, `content-visibility`
39
+ * skipped — so navigation never lands on a hidden node (programmatic
40
+ * `.focus()` on one silently fails). `getClientRects` is the fallback where
41
+ * `checkVisibility` isn't available. */
42
+ private isVisible;
43
+ private focusableItems;
44
+ /** Every tabbable element belonging to THIS menu (items + nested controls
45
+ * like inputs / sliders / buttons), in DOM order, visible and enabled —
46
+ * used to trap Tab within the open menu. Submenu contents are excluded
47
+ * (their nearest `a-menu` is the submenu). */
48
+ private focusables;
49
+ private focusFirstItem;
50
+ /** Public imperative API. Routes through the same intent path as the
51
+ * triggers, so it emits `openchange` and respects controlled mode. */
52
+ open(opts?: {
53
+ coord?: [number, number];
54
+ viaKeyboard?: boolean;
55
+ originEvent?: Event;
56
+ }): void;
57
+ close(originEvent?: Event): void;
58
+ toggle(opts?: {
59
+ coord?: [number, number];
60
+ viaKeyboard?: boolean;
61
+ originEvent?: Event;
62
+ }): void;
63
+ /** Dispatch the single `openchange` event (new state + old state). */
64
+ emitChange(next: boolean, opts?: {
65
+ coord?: [number, number];
66
+ originEvent?: Event;
67
+ }): void;
68
+ /** Intent to open (trigger / method / keyboard). Emits `openchange`; applies
69
+ * the visibility itself ONLY when uncontrolled — a controlled menu waits for
70
+ * the consumer to flip `state` (strict). */
71
+ requestOpen(opts?: {
72
+ coord?: [number, number];
73
+ viaKeyboard?: boolean;
74
+ originEvent?: Event;
75
+ }): void;
76
+ /** Intent to close. Emits `openchange`; hides itself only when uncontrolled. */
77
+ requestClose(originEvent?: Event): void;
78
+ /** Apply OPEN to the DOM (no event) — used by uncontrolled intent and by the
79
+ * controlled `state` sync. */
80
+ private show;
81
+ /** Apply CLOSE to the DOM (no event). Closes this menu and everything stacked
82
+ * above it (its submenus). */
83
+ private hide;
84
+ /** Shadow-only show: open the popover and position it. `instant` skips the
85
+ * enter-fade and positions synchronously, so a menu opening over an
86
+ * already-visible one snaps in without a blink. */
87
+ _doShow(coord?: [number, number], instant?: boolean): void;
88
+ /** Shadow-only hide. */
89
+ _doHide(): void;
90
+ private position;
91
+ /**
92
+ * Fully declarative close contract — decided synchronously from the DOM, so
93
+ * it never depends on the consumer's click handler (which in a worker-thread
94
+ * runtime can't `preventDefault` on the UI thread). The menu never
95
+ * stops/prevents the click, so the consumer's selection handler always runs.
96
+ *
97
+ * Walk the click's composedPath outward to the surface; the NEAREST marker
98
+ * wins:
99
+ * - `data-menu-open` → keep the menu open (a Done button can still close
100
+ * from inside such a region — it's hit first).
101
+ * - `a-menu-item` (a choice) or `data-menu-close` → close the menu.
102
+ * - nothing → keep open (plain custom content doesn't dismiss).
103
+ */
104
+ private onSurfaceClick;
105
+ /** Close the whole open menu system from the root down. */
106
+ private closeSystem;
107
+ /** Called by the coordinator on the topmost open menu. Handles navigation;
108
+ * Enter / Space activation is handled by a-menu-item's own global keydown
109
+ * (which synthesizes a click → routed through onSurfaceClick). */
110
+ handleKey(e: KeyboardEvent): void;
111
+ private submenuOf;
112
+ private typeahead;
113
+ setupListeners(): void;
114
+ teardownListeners(): void;
115
+ private scheduleOpen;
116
+ private scheduleClose;
117
+ private cancelOpenTimer;
118
+ private cancelCloseTimer;
119
+ }
120
+ export declare function register_a_menu(): void;