@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.
- package/README.md +2 -2
- 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 +4 -2
- package/dist/elements/a-button.css +31 -8
- 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-tag.css +196 -0
- package/dist/elements/a-text.css +17 -13
- package/dist/elements/a-tooltip.css +21 -1
- package/dist/elements/a-tooltip.d.ts +7 -0
- package/dist/elements/a-tooltip.js +96 -19
- package/dist/elements/index.d.ts +2 -4
- package/dist/elements/index.js +1 -6
- package/dist/general_types.d.ts +15 -35
- package/dist/index.d.ts +2 -4
- package/dist/index.js +2 -4
- package/dist/jsx-runtime.d.ts +6 -5
- package/dist/reset.css +11 -7
- package/package.json +13 -15
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
@layer anta {
|
|
2
|
+
/* `<a-tag>` is intentionally CSS-only — no JS, no shadow DOM, no
|
|
3
|
+
`customElements.define`. It's a small uppercase pill / chip styled
|
|
4
|
+
purely from attributes; the `Tag` JSX wrapper composes its content
|
|
5
|
+
from `icon` / `label` / `value` / `iconTrailing` props (mirroring
|
|
6
|
+
`<a-button>`) into the light-DOM child elements styled below.
|
|
7
|
+
|
|
8
|
+
Sizing is intrinsic — no fixed height. Like `<a-button>`, the pill's
|
|
9
|
+
height falls out of `line-height` + `padding-block` + the border, so
|
|
10
|
+
the text is never clipped and a consumer can retune the padding
|
|
11
|
+
tokens without fighting a hard height.
|
|
12
|
+
|
|
13
|
+
Color comes from Anta's theme-aware semantic tokens (`--text-*`,
|
|
14
|
+
`--bg-*`), so the named tones need no `.dark` rules — the tokens
|
|
15
|
+
re-theme themselves. Only the custom-tone `oklch` knobs are re-tuned
|
|
16
|
+
for dark mode. The hairline border derives from `--tag-text`, so
|
|
17
|
+
every tone (named or custom) gets a matching edge. */
|
|
18
|
+
|
|
19
|
+
a-tag {
|
|
20
|
+
--tag-text: var(--text-3);
|
|
21
|
+
--tag-bg: var(--bg-4);
|
|
22
|
+
--tag-border: color-mix(in oklch, var(--tag-text) 9%, transparent);
|
|
23
|
+
--tag-separator: color-mix(in oklch, var(--tag-text) 30%, transparent);
|
|
24
|
+
|
|
25
|
+
/* 1.5px top/bottom — the extra 0.5px each side compensates for the
|
|
26
|
+
0.5px (vs 1px) border, keeping the pill at its original 20px height. */
|
|
27
|
+
--tag-padding-block: 1.5px;
|
|
28
|
+
--tag-padding-inline: 6.5px;
|
|
29
|
+
--tag-icon-size: 13px;
|
|
30
|
+
--tag-label-weight: 600;
|
|
31
|
+
--tag-gap: 0.6ch;
|
|
32
|
+
|
|
33
|
+
display: inline-flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
gap: var(--tag-gap);
|
|
36
|
+
box-sizing: border-box;
|
|
37
|
+
max-width: 100%;
|
|
38
|
+
vertical-align: middle;
|
|
39
|
+
overflow: hidden;
|
|
40
|
+
white-space: nowrap;
|
|
41
|
+
-webkit-user-drag: none;
|
|
42
|
+
|
|
43
|
+
color: var(--tag-text);
|
|
44
|
+
background: var(--tag-bg);
|
|
45
|
+
border: 0.5px solid var(--tag-border);
|
|
46
|
+
border-radius: 22px;
|
|
47
|
+
|
|
48
|
+
padding-block: var(--tag-padding-block);
|
|
49
|
+
padding-inline: var(--tag-padding-inline);
|
|
50
|
+
|
|
51
|
+
font-family: var(--sans-serif);
|
|
52
|
+
font-weight: 450;
|
|
53
|
+
font-size: 11px;
|
|
54
|
+
line-height: 16px;
|
|
55
|
+
text-transform: uppercase;
|
|
56
|
+
letter-spacing: 0.08ch;
|
|
57
|
+
/* Tabular figures are always on (so counts / versions / timers don't
|
|
58
|
+
reflow), plus `ss05` alternate numerals — both layered on top of
|
|
59
|
+
the default ss01 / case / calt set. */
|
|
60
|
+
font-feature-settings: 'tnum' 1, 'ss05' 1, 'ss01', 'case', 'calt';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Icons scale to the pill (the default 16px a-icon is too big here) and
|
|
64
|
+
don't shrink in the flex row. */
|
|
65
|
+
a-tag a-icon {
|
|
66
|
+
--icon-size: var(--tag-icon-size);
|
|
67
|
+
flex: none;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* label / value segments. Each ellipsizes independently if the pill is
|
|
71
|
+
width-constrained. */
|
|
72
|
+
a-tag a-tag-label,
|
|
73
|
+
a-tag a-tag-value {
|
|
74
|
+
display: inline-block;
|
|
75
|
+
min-width: 0;
|
|
76
|
+
overflow: hidden;
|
|
77
|
+
text-overflow: ellipsis;
|
|
78
|
+
white-space: nowrap;
|
|
79
|
+
}
|
|
80
|
+
/* `value` is the default, unmodified text — it just inherits the tag's
|
|
81
|
+
color and base weight. */
|
|
82
|
+
a-tag a-tag-value { font-weight: 450; }
|
|
83
|
+
/* `label` is the bold "key" placed before the value: same color, just
|
|
84
|
+
heavier (weight 600). It only renders as <a-tag-label> when paired
|
|
85
|
+
with a value — the JSX wrapper drops a lone label into <a-tag-value>
|
|
86
|
+
so a standalone tag keeps the default styling. No divider; the weight
|
|
87
|
+
contrast does the separating. */
|
|
88
|
+
a-tag a-tag-label { font-weight: var(--tag-label-weight); }
|
|
89
|
+
|
|
90
|
+
/* Optical edge padding: an icon at an edge needs less room than a text
|
|
91
|
+
edge, so trim ~2px on whichever side a leading / trailing icon sits.
|
|
92
|
+
`max(0px, …)` keeps it from going negative at small sizes. */
|
|
93
|
+
a-tag:has(> a-icon:first-child) { padding-inline-start: max(0px, var(--tag-padding-inline) - 2px); }
|
|
94
|
+
a-tag:has(> a-icon:last-child) { padding-inline-end: max(0px, var(--tag-padding-inline) - 2px); }
|
|
95
|
+
|
|
96
|
+
/* Segmented children — when raw children (not the structured label /
|
|
97
|
+
value) are passed, each segment after the first gets a hairline
|
|
98
|
+
divider, the legacy multi-part style. Icons and the label / value
|
|
99
|
+
elements are excluded, so `label` + `value` stay divider-less and a
|
|
100
|
+
leading / trailing icon sits flush. The flex `gap` sits to the left
|
|
101
|
+
of the border and `padding-left` balances it on the right. */
|
|
102
|
+
a-tag
|
|
103
|
+
> :not(a-icon, a-tag-label, a-tag-value)
|
|
104
|
+
~ :not(a-icon, a-tag-label, a-tag-value) {
|
|
105
|
+
padding-left: var(--tag-gap);
|
|
106
|
+
border-left: 0.5px solid var(--tag-separator);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* Named tones — re-point text + bg only; the border re-derives, and
|
|
110
|
+
dark mode falls out of the semantic tokens for free. */
|
|
111
|
+
a-tag[tone="brand"] { --tag-text: var(--text-2-brand); --tag-bg: var(--bg-4-brand); }
|
|
112
|
+
a-tag[tone="info"] { --tag-text: var(--text-2-info); --tag-bg: var(--bg-4-info); }
|
|
113
|
+
a-tag[tone="success"] { --tag-text: var(--text-2-success); --tag-bg: var(--bg-4-success); }
|
|
114
|
+
a-tag[tone="warning"] { --tag-text: var(--text-2-warning); --tag-bg: var(--bg-4-warning); }
|
|
115
|
+
a-tag[tone="critical"] { --tag-text: var(--text-2-critical); --tag-bg: var(--bg-4-critical); }
|
|
116
|
+
|
|
117
|
+
/* Custom tone — any non-named `tone` value. Keep the source hue;
|
|
118
|
+
pin lightness/chroma so any input reads like a sibling of the named
|
|
119
|
+
tones. Knobs are the only numbers to tune; the dark block re-tunes
|
|
120
|
+
them and the formulas resolve against whichever value computes.
|
|
121
|
+
The JSX wrapper writes `--tag-tone-source` inline; the typed
|
|
122
|
+
`attr()` fallback picks up raw `<a-tag tone="…">` on modern engines.
|
|
123
|
+
(oklch relative color: Chrome 119+, Safari 16.4+, Firefox 128+.) */
|
|
124
|
+
a-tag[tone]:not(
|
|
125
|
+
[tone="neutral"],
|
|
126
|
+
[tone="brand"],
|
|
127
|
+
[tone="info"],
|
|
128
|
+
[tone="success"],
|
|
129
|
+
[tone="warning"],
|
|
130
|
+
[tone="critical"]
|
|
131
|
+
) {
|
|
132
|
+
--tag-tone-source: attr(tone type(<color>), currentColor);
|
|
133
|
+
|
|
134
|
+
--_tag-text-l: 0.46; /* fg lightness */
|
|
135
|
+
--_tag-text-c: 0.17; /* fg chroma */
|
|
136
|
+
--_tag-bg-l: 0.96; /* tinted-bg lightness — a hair above the named --bg-4 so the tint reads light */
|
|
137
|
+
--_tag-bg-c: 0.04; /* tinted-bg chroma — light pastel, like --bg-4-{tone} */
|
|
138
|
+
|
|
139
|
+
--tag-text: oklch(from var(--tag-tone-source) var(--_tag-text-l) var(--_tag-text-c) h);
|
|
140
|
+
--tag-bg: oklch(from var(--tag-tone-source) var(--_tag-bg-l) var(--_tag-bg-c) h);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* Custom tone, dark mode — lighter fg; the tint sits a touch above the
|
|
144
|
+
dark --bg-4-{tone} lightness so it doesn't read pitch-black. */
|
|
145
|
+
.dark a-tag[tone]:not(
|
|
146
|
+
[tone="neutral"],
|
|
147
|
+
[tone="brand"],
|
|
148
|
+
[tone="info"],
|
|
149
|
+
[tone="success"],
|
|
150
|
+
[tone="warning"],
|
|
151
|
+
[tone="critical"]
|
|
152
|
+
) {
|
|
153
|
+
--_tag-text-l: 0.80;
|
|
154
|
+
--_tag-text-c: 0.11;
|
|
155
|
+
--_tag-bg-l: 0.21;
|
|
156
|
+
--_tag-bg-c: 0.05;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
a-tag[size="small"] {
|
|
160
|
+
--tag-padding-block: 0.5px;
|
|
161
|
+
--tag-padding-inline: 4.5px;
|
|
162
|
+
--tag-icon-size: 11px;
|
|
163
|
+
|
|
164
|
+
font-size: 10px;
|
|
165
|
+
line-height: 14px;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* Large — 24px tall (line-height 18 + 2×2.5 padding + 2×0.5 border),
|
|
169
|
+
on par with Button's large. */
|
|
170
|
+
a-tag[size="large"] {
|
|
171
|
+
--tag-padding-block: 2.5px;
|
|
172
|
+
--tag-padding-inline: 8.5px;
|
|
173
|
+
--tag-icon-size: 15px;
|
|
174
|
+
|
|
175
|
+
font-size: 12px;
|
|
176
|
+
line-height: 18px;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* Normal-case opt-out: drop the uppercase transform for proportional,
|
|
180
|
+
mixed-case text. Keep a small body-text tracking (Anta's 0.02ch)
|
|
181
|
+
rather than zero — uppercase just needs more (0.08ch). Tabular
|
|
182
|
+
figures + `ss05` stay on (only the case / stylistic-set extras
|
|
183
|
+
change). */
|
|
184
|
+
a-tag[nocaps] {
|
|
185
|
+
font-size: 12px;
|
|
186
|
+
text-transform: none;
|
|
187
|
+
letter-spacing: 0.02ch;
|
|
188
|
+
font-feature-settings: 'tnum' 1, 'ss05' 1, 'lnum', 'ss01', 'ss04', 'case', 'calt';
|
|
189
|
+
}
|
|
190
|
+
/* Large + nocaps reads larger too — bump to 13px (one over the 12px
|
|
191
|
+
uppercase large), keeping the same +1px over-uppercase relationship
|
|
192
|
+
medium has. Height is unchanged (driven by line-height + padding). */
|
|
193
|
+
a-tag[size="large"][nocaps] {
|
|
194
|
+
font-size: 13px;
|
|
195
|
+
}
|
|
196
|
+
}
|
package/dist/elements/a-text.css
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
@layer anta {
|
|
2
2
|
a-text {
|
|
3
|
-
|
|
3
|
+
/* Default (no `priority`) is the SECONDARY level — body text reads at
|
|
4
|
+
--text-2; `priority="primary"` opts into the strongest --text-1. */
|
|
5
|
+
--text-color: var(--text-2);
|
|
4
6
|
--text-link-color: var(--link-color);
|
|
5
7
|
--text-link-hover: var(--link-color-hover);
|
|
6
8
|
|
|
@@ -27,10 +29,12 @@
|
|
|
27
29
|
min-width: 0;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
/* Neutral priority overrides.
|
|
32
|
+
/* Neutral priority overrides. The default (no `priority`) is the
|
|
33
|
+
secondary level (--text-2, on the base rule above); `primary` opts
|
|
34
|
+
into the strongest --text-1. Levels 1 & 2 keep the brand link color;
|
|
31
35
|
levels 3-5 mute the link to currentColor and step the hover up by one
|
|
32
36
|
level (3→2, 4→3, 5→4). */
|
|
33
|
-
a-text[priority="
|
|
37
|
+
a-text[priority="primary"] { --text-color: var(--text-1); }
|
|
34
38
|
a-text[priority="tertiary"] { --text-color: var(--text-3); --text-link-color: currentColor; --text-link-hover: var(--text-2); }
|
|
35
39
|
a-text[priority="quaternary"] { --text-color: var(--text-4); --text-link-color: currentColor; --text-link-hover: var(--text-3); }
|
|
36
40
|
a-text[priority="quinary"] { --text-color: var(--text-5); --text-link-color: currentColor; --text-link-hover: var(--text-4); }
|
|
@@ -39,32 +43,32 @@
|
|
|
39
43
|
muted (currentColor) and hover steps up one level of the same tint.
|
|
40
44
|
Level 1 has no level above; hover keeps the color, only the underline
|
|
41
45
|
alpha changes. */
|
|
42
|
-
a-text[tone="brand"] { --text-color: var(--text-
|
|
43
|
-
a-text[tone="brand"][priority="
|
|
46
|
+
a-text[tone="brand"] { --text-color: var(--text-2-brand); --text-link-color: currentColor; --text-link-hover: var(--text-1-brand); }
|
|
47
|
+
a-text[tone="brand"][priority="primary"] { --text-color: var(--text-1-brand); }
|
|
44
48
|
a-text[tone="brand"][priority="tertiary"] { --text-color: var(--text-3-brand); --text-link-hover: var(--text-2-brand); }
|
|
45
49
|
a-text[tone="brand"][priority="quaternary"] { --text-color: var(--text-4-brand); --text-link-hover: var(--text-3-brand); }
|
|
46
50
|
a-text[tone="brand"][priority="quinary"] { --text-color: var(--text-5-brand); --text-link-hover: var(--text-4-brand); }
|
|
47
51
|
|
|
48
|
-
a-text[tone="success"] { --text-color: var(--text-
|
|
49
|
-
a-text[tone="success"][priority="
|
|
52
|
+
a-text[tone="success"] { --text-color: var(--text-2-success); --text-link-color: currentColor; --text-link-hover: var(--text-1-success); }
|
|
53
|
+
a-text[tone="success"][priority="primary"] { --text-color: var(--text-1-success); }
|
|
50
54
|
a-text[tone="success"][priority="tertiary"] { --text-color: var(--text-3-success); --text-link-hover: var(--text-2-success); }
|
|
51
55
|
a-text[tone="success"][priority="quaternary"] { --text-color: var(--text-4-success); --text-link-hover: var(--text-3-success); }
|
|
52
56
|
a-text[tone="success"][priority="quinary"] { --text-color: var(--text-5-success); --text-link-hover: var(--text-4-success); }
|
|
53
57
|
|
|
54
|
-
a-text[tone="critical"] { --text-color: var(--text-
|
|
55
|
-
a-text[tone="critical"][priority="
|
|
58
|
+
a-text[tone="critical"] { --text-color: var(--text-2-critical); --text-link-color: currentColor; --text-link-hover: var(--text-1-critical); }
|
|
59
|
+
a-text[tone="critical"][priority="primary"] { --text-color: var(--text-1-critical); }
|
|
56
60
|
a-text[tone="critical"][priority="tertiary"] { --text-color: var(--text-3-critical); --text-link-hover: var(--text-2-critical); }
|
|
57
61
|
a-text[tone="critical"][priority="quaternary"] { --text-color: var(--text-4-critical); --text-link-hover: var(--text-3-critical); }
|
|
58
62
|
a-text[tone="critical"][priority="quinary"] { --text-color: var(--text-5-critical); --text-link-hover: var(--text-4-critical); }
|
|
59
63
|
|
|
60
|
-
a-text[tone="warning"] { --text-color: var(--text-
|
|
61
|
-
a-text[tone="warning"][priority="
|
|
64
|
+
a-text[tone="warning"] { --text-color: var(--text-2-warning); --text-link-color: currentColor; --text-link-hover: var(--text-1-warning); }
|
|
65
|
+
a-text[tone="warning"][priority="primary"] { --text-color: var(--text-1-warning); }
|
|
62
66
|
a-text[tone="warning"][priority="tertiary"] { --text-color: var(--text-3-warning); --text-link-hover: var(--text-2-warning); }
|
|
63
67
|
a-text[tone="warning"][priority="quaternary"] { --text-color: var(--text-4-warning); --text-link-hover: var(--text-3-warning); }
|
|
64
68
|
a-text[tone="warning"][priority="quinary"] { --text-color: var(--text-5-warning); --text-link-hover: var(--text-4-warning); }
|
|
65
69
|
|
|
66
|
-
a-text[tone="info"] { --text-color: var(--text-
|
|
67
|
-
a-text[tone="info"][priority="
|
|
70
|
+
a-text[tone="info"] { --text-color: var(--text-2-info); --text-link-color: currentColor; --text-link-hover: var(--text-1-info); }
|
|
71
|
+
a-text[tone="info"][priority="primary"] { --text-color: var(--text-1-info); }
|
|
68
72
|
a-text[tone="info"][priority="tertiary"] { --text-color: var(--text-3-info); --text-link-hover: var(--text-2-info); }
|
|
69
73
|
a-text[tone="info"][priority="quaternary"] { --text-color: var(--text-4-info); --text-link-hover: var(--text-3-info); }
|
|
70
74
|
a-text[tone="info"][priority="quinary"] { --text-color: var(--text-5-info); --text-link-hover: var(--text-4-info); }
|
|
@@ -8,6 +8,20 @@
|
|
|
8
8
|
display: none;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
/* On touch devices, an anchor that owns a tooltip suppresses the long-press
|
|
12
|
+
text-selection and the iOS callout, so press-and-hold cleanly reveals the
|
|
13
|
+
bubble (see the long-press handling in a-tooltip.ts) instead of selecting
|
|
14
|
+
text or popping the system menu. Scoped to coarse/no-hover pointers, so
|
|
15
|
+
fine-pointer devices keep normal text selection. The anchor is always
|
|
16
|
+
<a-tooltip>'s direct parent (set in connectedCallback), so `:has(> …)`
|
|
17
|
+
targets exactly it; `:where()` keeps specificity at 0 for easy override. */
|
|
18
|
+
@media (hover: none) and (pointer: coarse) {
|
|
19
|
+
:where(:has(> a-tooltip)) {
|
|
20
|
+
-webkit-touch-callout: none;
|
|
21
|
+
user-select: none;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
11
25
|
/* Tooltip exposes only the box "chrome" that lives inside the shadow
|
|
12
26
|
popover and is therefore impossible to reach with plain consumer CSS.
|
|
13
27
|
The bubble's content is slotted light DOM, so its font / colour / size
|
|
@@ -28,6 +42,12 @@
|
|
|
28
42
|
0 0 1px color-mix(in oklch, var(--text-1) 50%, transparent);
|
|
29
43
|
--tooltip-radius: 3px;
|
|
30
44
|
--tooltip-max-width: min(calc(100vw - 20px), 80ch);
|
|
45
|
+
/* No real border by default — the hairline edge comes from --tooltip-shadow.
|
|
46
|
+
Set e.g. `1px solid var(--border-5)` for an actual border. */
|
|
47
|
+
--tooltip-border: none;
|
|
48
|
+
/* The frost. Set to `none` (with --tooltip-bg: transparent) for a fully
|
|
49
|
+
see-through bubble. */
|
|
50
|
+
--tooltip-backdrop-filter: blur(8px);
|
|
31
51
|
}
|
|
32
52
|
|
|
33
53
|
/* Dark: a slightly more transparent black frost. A solid black cast shadow
|
|
@@ -37,6 +57,6 @@
|
|
|
37
57
|
--tooltip-bg: color-mix(in oklch, var(--bg-1) 70%, transparent);
|
|
38
58
|
--tooltip-shadow:
|
|
39
59
|
0 2px 16px color-mix(in oklch, black 70%, transparent),
|
|
40
|
-
inset 0 0 0 1px color-mix(in oklch, white
|
|
60
|
+
inset 0 0 0 1px color-mix(in oklch, white 15%, transparent);
|
|
41
61
|
}
|
|
42
62
|
}
|
|
@@ -7,6 +7,10 @@ export declare class ATooltipElement extends HTMLElementBase {
|
|
|
7
7
|
private anchor;
|
|
8
8
|
listening: boolean;
|
|
9
9
|
private shown;
|
|
10
|
+
/** True while the current open was triggered by a touch long-press. Such a
|
|
11
|
+
* bubble is pinned (never cursor-follows) and biases above the anchor so the
|
|
12
|
+
* fingertip doesn't cover it. */
|
|
13
|
+
private touchOpen;
|
|
10
14
|
private lastMouse?;
|
|
11
15
|
private debouncedShow?;
|
|
12
16
|
private teardown?;
|
|
@@ -35,6 +39,9 @@ export declare class ATooltipElement extends HTMLElementBase {
|
|
|
35
39
|
/** Pinned under the anchor (no cursor-following). Interactive implies this —
|
|
36
40
|
* you can't move into a bubble that's chasing the cursor. */
|
|
37
41
|
private get isStatic();
|
|
42
|
+
/** Pinned to the anchor for any reason: the `static`/`interactive` attrs, or
|
|
43
|
+
* a touch long-press (a finger can't track a following bubble). */
|
|
44
|
+
private get isPinned();
|
|
38
45
|
private get prefersTop();
|
|
39
46
|
private get delay();
|
|
40
47
|
private positionToTarget;
|
|
@@ -8,6 +8,9 @@ const DEFAULT_DELAY = 250;
|
|
|
8
8
|
const FADE_MS = 150;
|
|
9
9
|
const GRACE_MS = 300;
|
|
10
10
|
const CLOSE_DELAY = 120;
|
|
11
|
+
const ENTER_TOUCH_DELAY = 500;
|
|
12
|
+
const LEAVE_TOUCH_DELAY = 1500;
|
|
13
|
+
const TOUCH_SLOP = 10;
|
|
11
14
|
let currentOpen = null;
|
|
12
15
|
let graceTimer;
|
|
13
16
|
function graceActive() {
|
|
@@ -58,6 +61,10 @@ class ATooltipElement extends HTMLElementBase {
|
|
|
58
61
|
anchor = null;
|
|
59
62
|
listening = false;
|
|
60
63
|
shown = false;
|
|
64
|
+
/** True while the current open was triggered by a touch long-press. Such a
|
|
65
|
+
* bubble is pinned (never cursor-follows) and biases above the anchor so the
|
|
66
|
+
* fingertip doesn't cover it. */
|
|
67
|
+
touchOpen = false;
|
|
61
68
|
lastMouse;
|
|
62
69
|
debouncedShow;
|
|
63
70
|
teardown;
|
|
@@ -88,17 +95,34 @@ class ATooltipElement extends HTMLElementBase {
|
|
|
88
95
|
text-align: left;
|
|
89
96
|
|
|
90
97
|
background: var(--tooltip-bg, Canvas);
|
|
91
|
-
color: var(--text-
|
|
98
|
+
color: var(--text-3, CanvasText);
|
|
92
99
|
box-shadow: var(--tooltip-shadow, 0 1px 8px rgba(0, 0, 0, 0.2));
|
|
93
|
-
backdrop-filter: blur(8px);
|
|
100
|
+
-webkit-backdrop-filter: var(--tooltip-backdrop-filter, blur(8px));
|
|
101
|
+
backdrop-filter: var(--tooltip-backdrop-filter, blur(8px));
|
|
94
102
|
padding: 5px 8px;
|
|
95
|
-
border: none;
|
|
103
|
+
border: var(--tooltip-border, none);
|
|
96
104
|
border-radius: var(--tooltip-radius, 3px);
|
|
97
105
|
outline: none;
|
|
98
106
|
|
|
107
|
+
/* The bubble establishes its own text baseline so inheritable text
|
|
108
|
+
properties from the anchor (e.g. a Button's condensed "wdth" 88
|
|
109
|
+
axis, its 0.05ch letter-spacing, an uppercase transform, a custom
|
|
110
|
+
font) don't bleed into the slotted content. The content is slotted
|
|
111
|
+
light DOM, so it inherits these *from this container* \u2014 making this
|
|
112
|
+
the single choke point. Values mirror Anta's body text. Consumers
|
|
113
|
+
still customise a single tooltip by styling their own content
|
|
114
|
+
element directly (a class on the content overrides what it inherits
|
|
115
|
+
\u2014 see the Tooltip docs). */
|
|
116
|
+
font-family: var(--sans-serif, system-ui, sans-serif);
|
|
99
117
|
font-size: 14px;
|
|
100
|
-
line-height: 130%;
|
|
101
118
|
font-weight: 400;
|
|
119
|
+
font-style: normal;
|
|
120
|
+
font-stretch: normal;
|
|
121
|
+
font-variation-settings: "wdth" 100, "slnt" 0, "ital" 0;
|
|
122
|
+
line-height: 1.5;
|
|
123
|
+
letter-spacing: 0.02ch;
|
|
124
|
+
word-spacing: normal;
|
|
125
|
+
text-transform: none;
|
|
102
126
|
white-space: break-spaces;
|
|
103
127
|
word-break: break-word;
|
|
104
128
|
overflow-wrap: break-word;
|
|
@@ -196,6 +220,11 @@ class ATooltipElement extends HTMLElementBase {
|
|
|
196
220
|
get isStatic() {
|
|
197
221
|
return this.hasAttribute("static") || this.isInteractive;
|
|
198
222
|
}
|
|
223
|
+
/** Pinned to the anchor for any reason: the `static`/`interactive` attrs, or
|
|
224
|
+
* a touch long-press (a finger can't track a following bubble). */
|
|
225
|
+
get isPinned() {
|
|
226
|
+
return this.isStatic || this.touchOpen;
|
|
227
|
+
}
|
|
199
228
|
get prefersTop() {
|
|
200
229
|
return this.getAttribute("placement") === "top";
|
|
201
230
|
}
|
|
@@ -218,7 +247,7 @@ class ATooltipElement extends HTMLElementBase {
|
|
|
218
247
|
if (left + box.width > vw) left = vw - box.width - MARGIN;
|
|
219
248
|
left = Math.max(MARGIN, left);
|
|
220
249
|
let top;
|
|
221
|
-
if (this.prefersTop) {
|
|
250
|
+
if (this.prefersTop || this.touchOpen) {
|
|
222
251
|
top = a.top - box.height - MARGIN;
|
|
223
252
|
if (top < MARGIN) top = a.bottom + MARGIN;
|
|
224
253
|
} else {
|
|
@@ -252,7 +281,7 @@ class ATooltipElement extends HTMLElementBase {
|
|
|
252
281
|
});
|
|
253
282
|
};
|
|
254
283
|
position(e) {
|
|
255
|
-
if (!this.
|
|
284
|
+
if (!this.isPinned && e) this.positionToMouse(e);
|
|
256
285
|
else this.positionToTarget();
|
|
257
286
|
}
|
|
258
287
|
// --- show / hide ---
|
|
@@ -284,6 +313,7 @@ class ATooltipElement extends HTMLElementBase {
|
|
|
284
313
|
this.cancelHide();
|
|
285
314
|
if (!this.shown) return;
|
|
286
315
|
this.shown = false;
|
|
316
|
+
this.touchOpen = false;
|
|
287
317
|
this.container.hidePopover();
|
|
288
318
|
this.view.removeEventListener("scroll", this.hide, { capture: true });
|
|
289
319
|
this.doc.removeEventListener("keydown", this.onKeyDown, true);
|
|
@@ -303,7 +333,7 @@ class ATooltipElement extends HTMLElementBase {
|
|
|
303
333
|
* the close delay). Cursor-following only — static/interactive bubbles
|
|
304
334
|
* stay pinned. */
|
|
305
335
|
onDocMove = (e) => {
|
|
306
|
-
if ((this.shown || this.fading) && !this.
|
|
336
|
+
if ((this.shown || this.fading) && !this.isPinned) {
|
|
307
337
|
this.lastMouse = e;
|
|
308
338
|
this.positionToMouse(e);
|
|
309
339
|
}
|
|
@@ -311,12 +341,12 @@ class ATooltipElement extends HTMLElementBase {
|
|
|
311
341
|
/** Hide after CLOSE_DELAY unless something cancels it first (re-enter, or
|
|
312
342
|
* another tooltip claiming the slot). Lets the bubble bridge the gap to a
|
|
313
343
|
* neighbouring anchor — and survive the trip into an interactive bubble. */
|
|
314
|
-
scheduleHide = () => {
|
|
344
|
+
scheduleHide = (delay = CLOSE_DELAY) => {
|
|
315
345
|
this.cancelHide();
|
|
316
346
|
this.closeTimer = setTimeout(() => {
|
|
317
347
|
this.closeTimer = void 0;
|
|
318
348
|
this.hide();
|
|
319
|
-
},
|
|
349
|
+
}, delay);
|
|
320
350
|
};
|
|
321
351
|
cancelHide() {
|
|
322
352
|
if (this.closeTimer !== void 0) {
|
|
@@ -347,46 +377,93 @@ class ATooltipElement extends HTMLElementBase {
|
|
|
347
377
|
const view = this.view;
|
|
348
378
|
const doc = this.doc;
|
|
349
379
|
this.makeDebouncedShow();
|
|
350
|
-
const
|
|
380
|
+
const onPointerMove = (e) => {
|
|
381
|
+
if (e.pointerType !== "mouse") return;
|
|
351
382
|
this.lastMouse = e;
|
|
352
383
|
if (this.shown) {
|
|
353
|
-
if (!this.
|
|
384
|
+
if (!this.isPinned) this.positionToMouse(e);
|
|
354
385
|
} else {
|
|
355
386
|
this.trigger(e);
|
|
356
387
|
}
|
|
357
388
|
};
|
|
358
389
|
const onLeave = () => {
|
|
359
390
|
this.debouncedShow?.cancel();
|
|
360
|
-
anchor.removeEventListener("
|
|
361
|
-
anchor.removeEventListener("
|
|
391
|
+
anchor.removeEventListener("pointermove", onPointerMove);
|
|
392
|
+
anchor.removeEventListener("pointerleave", onLeave);
|
|
362
393
|
anchor.removeEventListener("blur", onLeave);
|
|
363
394
|
view.removeEventListener("pagehide", onLeave);
|
|
364
395
|
doc.removeEventListener("visibilitychange", onLeave);
|
|
365
396
|
this.scheduleHide();
|
|
366
397
|
};
|
|
367
398
|
const onEnter = (e) => {
|
|
399
|
+
if (e.pointerType !== "mouse") return;
|
|
368
400
|
this.lastMouse = e;
|
|
369
|
-
anchor.addEventListener("
|
|
370
|
-
anchor.addEventListener("
|
|
401
|
+
anchor.addEventListener("pointermove", onPointerMove);
|
|
402
|
+
anchor.addEventListener("pointerleave", onLeave);
|
|
371
403
|
view.addEventListener("pagehide", onLeave);
|
|
372
404
|
doc.addEventListener("visibilitychange", onLeave);
|
|
373
405
|
this.trigger(e);
|
|
374
406
|
};
|
|
375
407
|
const onFocus = () => {
|
|
408
|
+
if (!anchor.matches(":focus-visible")) return;
|
|
376
409
|
anchor.addEventListener("blur", onLeave);
|
|
377
410
|
view.addEventListener("pagehide", onLeave);
|
|
378
411
|
doc.addEventListener("visibilitychange", onLeave);
|
|
379
412
|
this.trigger();
|
|
380
413
|
};
|
|
381
|
-
|
|
414
|
+
let pressTimer;
|
|
415
|
+
let pressStart;
|
|
416
|
+
const preventContext = (e) => e.preventDefault();
|
|
417
|
+
const clearPressTimer = () => {
|
|
418
|
+
if (pressTimer !== void 0) {
|
|
419
|
+
clearTimeout(pressTimer);
|
|
420
|
+
pressTimer = void 0;
|
|
421
|
+
}
|
|
422
|
+
pressStart = void 0;
|
|
423
|
+
};
|
|
424
|
+
const onPressMove = (e) => {
|
|
425
|
+
if (pressStart && Math.hypot(e.clientX - pressStart.x, e.clientY - pressStart.y) > TOUCH_SLOP) {
|
|
426
|
+
clearPressTimer();
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
const onPressEnd = () => {
|
|
430
|
+
clearPressTimer();
|
|
431
|
+
detachPress();
|
|
432
|
+
if (this.shown && this.touchOpen) this.scheduleHide(LEAVE_TOUCH_DELAY);
|
|
433
|
+
};
|
|
434
|
+
const detachPress = () => {
|
|
435
|
+
anchor.removeEventListener("pointermove", onPressMove);
|
|
436
|
+
anchor.removeEventListener("pointerup", onPressEnd);
|
|
437
|
+
anchor.removeEventListener("pointercancel", onPressEnd);
|
|
438
|
+
anchor.removeEventListener("contextmenu", preventContext);
|
|
439
|
+
};
|
|
440
|
+
const onPointerDown = (e) => {
|
|
441
|
+
if (e.pointerType === "mouse") return;
|
|
442
|
+
clearPressTimer();
|
|
443
|
+
pressStart = { x: e.clientX, y: e.clientY };
|
|
444
|
+
anchor.addEventListener("pointermove", onPressMove);
|
|
445
|
+
anchor.addEventListener("pointerup", onPressEnd);
|
|
446
|
+
anchor.addEventListener("pointercancel", onPressEnd);
|
|
447
|
+
anchor.addEventListener("contextmenu", preventContext);
|
|
448
|
+
pressTimer = setTimeout(() => {
|
|
449
|
+
pressTimer = void 0;
|
|
450
|
+
this.touchOpen = true;
|
|
451
|
+
this.show();
|
|
452
|
+
}, ENTER_TOUCH_DELAY);
|
|
453
|
+
};
|
|
454
|
+
anchor.addEventListener("pointerenter", onEnter);
|
|
382
455
|
anchor.addEventListener("focus", onFocus);
|
|
456
|
+
anchor.addEventListener("pointerdown", onPointerDown);
|
|
383
457
|
this.teardown = () => {
|
|
384
458
|
this.debouncedShow?.cancel();
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
anchor.removeEventListener("
|
|
459
|
+
clearPressTimer();
|
|
460
|
+
detachPress();
|
|
461
|
+
anchor.removeEventListener("pointermove", onPointerMove);
|
|
462
|
+
anchor.removeEventListener("pointerenter", onEnter);
|
|
463
|
+
anchor.removeEventListener("pointerleave", onLeave);
|
|
388
464
|
anchor.removeEventListener("focus", onFocus);
|
|
389
465
|
anchor.removeEventListener("blur", onLeave);
|
|
466
|
+
anchor.removeEventListener("pointerdown", onPointerDown);
|
|
390
467
|
view.removeEventListener("pagehide", onLeave);
|
|
391
468
|
doc.removeEventListener("visibilitychange", onLeave);
|
|
392
469
|
this.listening = false;
|
package/dist/elements/index.d.ts
CHANGED
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* For a SMALLER footprint, import just the element(s) you use instead:
|
|
10
10
|
* import '@antadesign/anta/elements/a-tooltip' // registers a-tooltip + its CSS, nothing else
|
|
11
|
-
* That granular path pulls in only that element's code
|
|
12
|
-
* `lottie-web`, which only `a-sticker-animated` needs).
|
|
11
|
+
* That granular path pulls in only that element's code and CSS, nothing else.
|
|
13
12
|
*
|
|
14
13
|
* Must only be imported client-side — registration is guarded against missing
|
|
15
14
|
* `customElements` (SSR), but there's no reason to load it server-side.
|
|
@@ -18,7 +17,6 @@ export { AProgressElement, register_a_progress } from './a-progress';
|
|
|
18
17
|
export { ATextElement, register_a_text } from './a-text';
|
|
19
18
|
export { AIconElement, register_a_icon } from './a-icon';
|
|
20
19
|
export { AButtonElement, register_a_button } from './a-button';
|
|
21
|
-
export { AStickerElement, register_a_sticker } from './a-sticker';
|
|
22
|
-
export { AStickerAnimatedElement, register_a_sticker_animated } from './a-sticker-animated';
|
|
23
20
|
export { ATooltipElement, register_a_tooltip } from './a-tooltip';
|
|
24
21
|
import './a-title.css';
|
|
22
|
+
import './a-tag.css';
|
package/dist/elements/index.js
CHANGED
|
@@ -2,23 +2,18 @@ import { AProgressElement, register_a_progress } from "./a-progress";
|
|
|
2
2
|
import { ATextElement, register_a_text } from "./a-text";
|
|
3
3
|
import { AIconElement, register_a_icon } from "./a-icon";
|
|
4
4
|
import { AButtonElement, register_a_button } from "./a-button";
|
|
5
|
-
import { AStickerElement, register_a_sticker } from "./a-sticker";
|
|
6
|
-
import { AStickerAnimatedElement, register_a_sticker_animated } from "./a-sticker-animated";
|
|
7
5
|
import { ATooltipElement, register_a_tooltip } from "./a-tooltip";
|
|
8
6
|
import "./a-title.css";
|
|
7
|
+
import "./a-tag.css";
|
|
9
8
|
export {
|
|
10
9
|
AButtonElement,
|
|
11
10
|
AIconElement,
|
|
12
11
|
AProgressElement,
|
|
13
|
-
AStickerAnimatedElement,
|
|
14
|
-
AStickerElement,
|
|
15
12
|
ATextElement,
|
|
16
13
|
ATooltipElement,
|
|
17
14
|
register_a_button,
|
|
18
15
|
register_a_icon,
|
|
19
16
|
register_a_progress,
|
|
20
|
-
register_a_sticker,
|
|
21
|
-
register_a_sticker_animated,
|
|
22
17
|
register_a_text,
|
|
23
18
|
register_a_tooltip
|
|
24
19
|
};
|
package/dist/general_types.d.ts
CHANGED
|
@@ -108,43 +108,23 @@ export interface ATitleAttributes extends BaseAttributes {
|
|
|
108
108
|
'aria-level'?: number | string;
|
|
109
109
|
}
|
|
110
110
|
/**
|
|
111
|
-
* Attributes for the `<a-
|
|
111
|
+
* Attributes for the `<a-tag>` styled tag.
|
|
112
112
|
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* `<slot>`, so children aren't rendered). The JSX wrapper
|
|
116
|
-
* (`Sticker{Name}`) sets it from the per-sticker module's inlined SVG.
|
|
117
|
-
* Sizing comes from `--sticker-size`.
|
|
113
|
+
* `<a-tag>` has no JS — it's a CSS-only styled element. Low-level
|
|
114
|
+
* attributes; for the JSX wrapper use `Tag` from `@antadesign/anta`.
|
|
118
115
|
*/
|
|
119
|
-
export interface
|
|
120
|
-
/**
|
|
121
|
-
*
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
'
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
* string. On change the element `JSON.parse`s it once and drives a
|
|
132
|
-
* `lottie-web` player (SVG renderer) inside its shadow DOM. The JSX
|
|
133
|
-
* wrapper (`Sticker{Name}Animated`) sets it from the per-sticker
|
|
134
|
-
* module's inlined JSON. Sizing comes from the `--sticker-size` CSS
|
|
135
|
-
* variable; `paused` controls playback.
|
|
136
|
-
*/
|
|
137
|
-
export interface AStickerAnimatedAttributes extends BaseAttributes {
|
|
138
|
-
/** Lottie payload as a JSON string. The element parses it once on
|
|
139
|
-
* change. */
|
|
140
|
-
animation?: string;
|
|
141
|
-
/** Present (any string value, including `""`) freezes the animation.
|
|
142
|
-
* A numeric string is parsed as seconds and the player seeks to
|
|
143
|
-
* that time before pausing. Omit to play. */
|
|
144
|
-
paused?: string | boolean | number;
|
|
145
|
-
role?: string;
|
|
146
|
-
'aria-label'?: string;
|
|
147
|
-
'aria-hidden'?: 'true' | 'false' | boolean;
|
|
116
|
+
export interface ATagAttributes extends BaseAttributes {
|
|
117
|
+
/** Semantic tone, or any literal CSS color for a one-off custom tone.
|
|
118
|
+
* Named tones map to the `--text-2-{tone}` / `--bg-4-{tone}` palette;
|
|
119
|
+
* a custom color keeps its hue with lightness/chroma pinned.
|
|
120
|
+
* `'neutral'` is the default gray (same as omitting it). */
|
|
121
|
+
tone?: 'neutral' | 'brand' | 'info' | 'success' | 'warning' | 'critical' | (string & {});
|
|
122
|
+
/** Size variant. `small` = 16px tall, `medium` (default) = 20px,
|
|
123
|
+
* `large` = 24px. */
|
|
124
|
+
size?: 'small' | 'medium' | 'large';
|
|
125
|
+
/** Render in normal case instead of the default uppercase.
|
|
126
|
+
* Presence-based (`''` on, omit off). */
|
|
127
|
+
nocaps?: boolean | '';
|
|
148
128
|
}
|
|
149
129
|
/**
|
|
150
130
|
* Attributes for the `<a-icon>` custom element. `shape` is typed as
|