@antadesign/anta 0.1.1-dev.7 → 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.
- package/README.md +11 -0
- package/dist/components/Button.js +4 -3
- package/dist/components/Menu.d.ts +61 -0
- package/dist/components/Menu.js +34 -0
- package/dist/components/MenuGroup.d.ts +24 -0
- package/dist/components/MenuGroup.js +19 -0
- package/dist/components/MenuItem.d.ts +49 -0
- package/dist/components/MenuItem.js +40 -0
- package/dist/components/MenuSeparator.d.ts +14 -0
- package/dist/components/MenuSeparator.js +7 -0
- package/dist/components/Tag.d.ts +55 -0
- package/dist/components/Tag.js +40 -0
- package/dist/components/Text.d.ts +4 -2
- package/dist/components/Tooltip.d.ts +57 -0
- package/dist/components/Tooltip.js +26 -0
- package/dist/elements/a-button.css +31 -8
- package/dist/elements/a-button.d.ts +1 -0
- package/dist/elements/a-button.js +2 -0
- package/dist/elements/a-icon.d.ts +2 -0
- package/dist/elements/a-icon.js +3 -0
- package/dist/elements/a-icon.shapes.css +4 -0
- package/dist/elements/a-icon.shapes.d.ts +2 -1
- package/dist/elements/a-icon.shapes.js +3 -1
- package/dist/elements/a-menu-group.css +21 -0
- package/dist/elements/a-menu-group.d.ts +13 -0
- package/dist/elements/a-menu-group.js +15 -0
- package/dist/elements/a-menu-item.css +162 -0
- package/dist/elements/a-menu-item.d.ts +27 -0
- package/dist/elements/a-menu-item.js +29 -0
- package/dist/elements/a-menu-separator.css +15 -0
- package/dist/elements/a-menu-separator.d.ts +12 -0
- package/dist/elements/a-menu-separator.js +15 -0
- package/dist/elements/a-menu.css +34 -0
- package/dist/elements/a-menu.d.ts +120 -0
- package/dist/elements/a-menu.js +649 -0
- package/dist/elements/a-progress.d.ts +1 -0
- package/dist/elements/a-progress.js +2 -0
- package/dist/elements/a-sticker-animated.d.ts +1 -0
- package/dist/elements/a-sticker-animated.js +2 -0
- package/dist/elements/a-sticker.d.ts +1 -0
- package/dist/elements/a-sticker.js +2 -0
- package/dist/elements/a-tag.css +196 -0
- package/dist/elements/a-text.css +17 -13
- package/dist/elements/a-text.d.ts +1 -0
- package/dist/elements/a-text.js +2 -0
- package/dist/elements/a-tooltip.css +62 -0
- package/dist/elements/a-tooltip.d.ts +68 -0
- package/dist/elements/a-tooltip.js +488 -0
- package/dist/elements/index.d.ts +18 -10
- package/dist/elements/index.js +12 -35
- package/dist/general_types.d.ts +40 -41
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/jsx-runtime.d.ts +7 -5
- package/dist/reset.css +14 -5
- package/package.json +13 -14
|
@@ -330,6 +330,10 @@
|
|
|
330
330
|
--icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 3H5a2 2 0 0 0-2 2v4m6-6h10a2 2 0 0 1 2 2v4M9 3v18m0 0h10a2 2 0 0 0 2-2V9M9 21H5a2 2 0 0 1-2-2V9m0 0h18'/%3E%3C/svg%3E%0A");
|
|
331
331
|
}
|
|
332
332
|
|
|
333
|
+
a-icon[shape="tag"] {
|
|
334
|
+
--icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z'/%3E%3Ccircle cx='7.5' cy='7.5' r='.5' fill='currentColor'/%3E%3C/svg%3E%0A");
|
|
335
|
+
}
|
|
336
|
+
|
|
333
337
|
a-icon[shape="text-highlight"] {
|
|
334
338
|
--icon: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%0A%3C!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E%0A%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'%0A viewBox='0 0 16 16' enable-background='new 0 0 16 16' xml:space='preserve'%3E%0A%3Cg id='text_highlight_1_'%3E%0A %3Cg%3E%0A %3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M9,10H2V6h7V4H1C0.45,4,0,4.45,0,5v6c0,0.55,0.45,1,1,1h8V10z M13,13h-1V3h1%0A c0.55,0,1-0.45,1-1c0-0.55-0.45-1-1-1h-1c-0.37,0-0.7,0.11-1,0.28C10.7,1.11,10.37,1,10,1H9C8.45,1,8,1.45,8,2c0,0.55,0.45,1,1,1%0A h1v10H9c-0.55,0-1,0.45-1,1c0,0.55,0.45,1,1,1h1c0.37,0,0.7-0.11,1-0.28c0.3,0.17,0.63,0.28,1,0.28h1c0.55,0,1-0.45,1-1%0A C14,13.45,13.55,13,13,13z M15,4h-2v2h1v4h-1v2h2c0.55,0,1-0.45,1-1V5C16,4.45,15.55,4,15,4z'/%3E%0A %3C/g%3E%0A%3C/g%3E%0A%3C/svg%3E%0A");
|
|
335
339
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { IconShapes } from '@antadesign/anta';
|
|
2
|
-
export declare const ICON_SHAPES: readonly ["arrow-left-to-line", "arrow-left", "arrow-narrow-down", "arrow-narrow-up-down", "arrow-narrow-up", "arrow-right", "arrow-top-right", "asterisk", "book-open", "braces", "bug", "calendar", "case-sensitive", "chat", "check", "chevron-down", "chevron-left", "chevron-right", "chevron-up", "chevrons-right", "circle-check", "circle-large", "circle", "click", "clock", "cloud-upload", "copy", "corner-down-right", "cube", "dots-vertical", "download", "edit", "education-disk", "external-link", "file-down", "file", "filter", "folder-close", "folder-open", "folder-tree", "github-logo", "gitlab-logo", "hat-glasses", "heart-handshake", "help-disk", "history-tree", "history", "home", "hourglass", "info", "jira-logo", "linear-logo", "link", "list-detail-view", "maximize", "megaphone", "menu", "minimize", "minus", "moon", "more", "move-horizontal", "not-equal", "play", "plus", "pointer", "presentation", "refresh-ccw-dot", "refresh", "regex", "repeat", "rotate-ccw", "rss", "runs-history", "scroll-text", "search-check", "search", "send", "sparkles", "sun", "swatch-book", "table-2", "text-highlight", "text-initial", "timer", "trash", "trello-logo", "view", "warning-triangle", "webhook", "workflow", "x"];
|
|
2
|
+
export declare const ICON_SHAPES: readonly ["arrow-left-to-line", "arrow-left", "arrow-narrow-down", "arrow-narrow-up-down", "arrow-narrow-up", "arrow-right", "arrow-top-right", "asterisk", "book-open", "braces", "bug", "calendar", "case-sensitive", "chat", "check", "chevron-down", "chevron-left", "chevron-right", "chevron-up", "chevrons-right", "circle-check", "circle-large", "circle", "click", "clock", "cloud-upload", "copy", "corner-down-right", "cube", "dots-vertical", "download", "edit", "education-disk", "external-link", "file-down", "file", "filter", "folder-close", "folder-open", "folder-tree", "github-logo", "gitlab-logo", "hat-glasses", "heart-handshake", "help-disk", "history-tree", "history", "home", "hourglass", "info", "jira-logo", "linear-logo", "link", "list-detail-view", "maximize", "megaphone", "menu", "minimize", "minus", "moon", "more", "move-horizontal", "not-equal", "play", "plus", "pointer", "presentation", "refresh-ccw-dot", "refresh", "regex", "repeat", "rotate-ccw", "rss", "runs-history", "scroll-text", "search-check", "search", "send", "sparkles", "sun", "swatch-book", "table-2", "tag", "text-highlight", "text-initial", "timer", "trash", "trello-logo", "view", "warning-triangle", "webhook", "workflow", "x"];
|
|
3
3
|
export declare const ICON_SYNONYMS: Readonly<Record<string, readonly string[]>>;
|
|
4
4
|
declare module '@antadesign/anta' {
|
|
5
5
|
interface IconShapes {
|
|
@@ -85,6 +85,7 @@ declare module '@antadesign/anta' {
|
|
|
85
85
|
'sun': true;
|
|
86
86
|
'swatch-book': true;
|
|
87
87
|
'table-2': true;
|
|
88
|
+
'tag': true;
|
|
88
89
|
'text-highlight': true;
|
|
89
90
|
'text-initial': true;
|
|
90
91
|
'timer': true;
|
|
@@ -81,6 +81,7 @@ const ICON_SHAPES = [
|
|
|
81
81
|
"sun",
|
|
82
82
|
"swatch-book",
|
|
83
83
|
"table-2",
|
|
84
|
+
"tag",
|
|
84
85
|
"text-highlight",
|
|
85
86
|
"text-initial",
|
|
86
87
|
"timer",
|
|
@@ -178,7 +179,8 @@ const ICON_SYNONYMS = {
|
|
|
178
179
|
"download": ["save", "import"],
|
|
179
180
|
"cloud-upload": ["save", "sync", "upload", "send", "export"],
|
|
180
181
|
"link": ["url", "href", "anchor"],
|
|
181
|
-
"more": ["options", "extra"]
|
|
182
|
+
"more": ["options", "extra"],
|
|
183
|
+
"tag": ["label", "badge", "chip"]
|
|
182
184
|
};
|
|
183
185
|
export {
|
|
184
186
|
ICON_SHAPES,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
@layer anta {
|
|
2
|
+
a-menu-group:not(:defined) {
|
|
3
|
+
display: none;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
a-menu-group {
|
|
7
|
+
display: block;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/* Non-focusable section heading. */
|
|
11
|
+
a-menu-group-label {
|
|
12
|
+
display: block;
|
|
13
|
+
padding: 6px 8px 2px;
|
|
14
|
+
color: var(--text-5);
|
|
15
|
+
font-size: 0.75em;
|
|
16
|
+
font-weight: 600;
|
|
17
|
+
letter-spacing: 0.06em;
|
|
18
|
+
text-transform: uppercase;
|
|
19
|
+
user-select: none;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { HTMLElementBase } from '../anta_helpers';
|
|
2
|
+
import './a-menu-group.css';
|
|
3
|
+
/**
|
|
4
|
+
* `<a-menu-group>` — a titled section grouping related menu items.
|
|
5
|
+
* No JS / no shadow DOM; styled by `a-menu-group.css`. The group's heading
|
|
6
|
+
* is a non-focusable `<a-menu-group-label>` child rendered by the
|
|
7
|
+
* `MenuGroup` JSX wrapper; `role="group"` + `aria-label` are added there too.
|
|
8
|
+
* Keyboard navigation in `<a-menu>` flattens items across group boundaries,
|
|
9
|
+
* skipping the heading.
|
|
10
|
+
*/
|
|
11
|
+
export declare class AMenuGroupElement extends HTMLElementBase {
|
|
12
|
+
}
|
|
13
|
+
export declare function register_a_menu_group(): void;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { HTMLElementBase } from "../anta_helpers";
|
|
2
|
+
import "./a-menu-group.css";
|
|
3
|
+
class AMenuGroupElement extends HTMLElementBase {
|
|
4
|
+
}
|
|
5
|
+
function register_a_menu_group() {
|
|
6
|
+
if (typeof customElements === "undefined") return;
|
|
7
|
+
if (!customElements.get("a-menu-group")) {
|
|
8
|
+
customElements.define("a-menu-group", AMenuGroupElement);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
register_a_menu_group();
|
|
12
|
+
export {
|
|
13
|
+
AMenuGroupElement,
|
|
14
|
+
register_a_menu_group
|
|
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;
|