@antadesign/anta 0.3.34 → 0.3.35

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 (184) hide show
  1. package/LICENSE.md +14 -1
  2. package/README.md +9 -0
  3. package/dist/anta_helpers.d.ts +46 -30
  4. package/dist/anta_helpers.js +66 -25
  5. package/dist/bundle.css +1 -1
  6. package/dist/bundle.js +2 -1333
  7. package/dist/components/Banner.d.ts +25 -40
  8. package/dist/components/Banner.js +15 -17
  9. package/dist/components/Button.js +1 -0
  10. package/dist/components/ButtonCopy.js +3 -2
  11. package/dist/components/Calendar.js +13 -13
  12. package/dist/components/Card.d.ts +9 -17
  13. package/dist/components/Card.js +50 -46
  14. package/dist/components/Dialog.d.ts +1 -0
  15. package/dist/components/Dialog.js +16 -10
  16. package/dist/components/Expander.d.ts +6 -21
  17. package/dist/components/Expander.js +24 -23
  18. package/dist/components/Icon.d.ts +8 -14
  19. package/dist/components/Input.d.ts +77 -96
  20. package/dist/components/Input.js +23 -27
  21. package/dist/components/InputAutocomplete.d.ts +29 -10
  22. package/dist/components/InputAutocomplete.js +25 -8
  23. package/dist/components/InputDate.js +2 -2
  24. package/dist/components/InputTime.d.ts +10 -4
  25. package/dist/components/InputTime.js +20 -16
  26. package/dist/components/Loader.d.ts +2 -2
  27. package/dist/components/Menu.d.ts +5 -4
  28. package/dist/components/Menu.js +13 -8
  29. package/dist/components/MenuItem.js +1 -14
  30. package/dist/components/MenuItemCopy.js +2 -2
  31. package/dist/components/Panel.d.ts +3 -1
  32. package/dist/components/Panel.js +17 -11
  33. package/dist/components/Progress.d.ts +21 -23
  34. package/dist/components/Progress.js +13 -9
  35. package/dist/components/RadioGroup.js +6 -8
  36. package/dist/components/{select-parts.css → Select.css} +1 -1
  37. package/dist/components/Select.d.ts +53 -35
  38. package/dist/components/Select.js +53 -97
  39. package/dist/components/SelectFaceted.d.ts +7 -3
  40. package/dist/components/SelectFaceted.js +21 -9
  41. package/dist/components/Slider.d.ts +28 -11
  42. package/dist/components/Slider.js +20 -20
  43. package/dist/components/Steps.css +1 -1
  44. package/dist/components/Steps.js +1 -11
  45. package/dist/components/Tabs.d.ts +17 -20
  46. package/dist/components/Tabs.js +5 -8
  47. package/dist/components/Text.d.ts +55 -36
  48. package/dist/components/Text.js +35 -22
  49. package/dist/components/Title.d.ts +16 -26
  50. package/dist/components/Title.js +2 -1
  51. package/dist/components/Toaster.js +9 -2
  52. package/dist/components/Tooltip.d.ts +10 -15
  53. package/dist/components/copy-props.d.ts +14 -3
  54. package/dist/components/copy-props.js +14 -3
  55. package/dist/components/select-tree.d.ts +24 -0
  56. package/dist/components/select-tree.js +35 -0
  57. package/dist/element-base.d.ts +10 -0
  58. package/dist/element-base.js +15 -0
  59. package/dist/elements/a-banner.css +1 -1
  60. package/dist/elements/a-banner.d.ts +4 -1
  61. package/dist/elements/a-banner.js +22 -112
  62. package/dist/elements/a-button.d.ts +2 -1
  63. package/dist/elements/a-button.js +18 -1
  64. package/dist/elements/a-calendar.d.ts +14 -13
  65. package/dist/elements/a-calendar.js +29 -24
  66. package/dist/elements/a-card.css +1 -1
  67. package/dist/elements/a-card.d.ts +0 -9
  68. package/dist/elements/a-card.js +0 -184
  69. package/dist/elements/a-checkbox.css +1 -1
  70. package/dist/elements/a-copy.css +1 -1
  71. package/dist/elements/a-copy.d.ts +1 -5
  72. package/dist/elements/a-copy.js +129 -108
  73. package/dist/elements/a-dialog.css +1 -1
  74. package/dist/elements/a-dialog.d.ts +1 -1
  75. package/dist/elements/a-dialog.js +66 -194
  76. package/dist/elements/a-expander.css +1 -1
  77. package/dist/elements/a-expander.d.ts +4 -30
  78. package/dist/elements/a-expander.js +85 -344
  79. package/dist/elements/a-icon.d.ts +5 -23
  80. package/dist/elements/a-icon.js +1 -2
  81. package/dist/elements/a-input-time.css +1 -1
  82. package/dist/elements/a-input-time.d.ts +2 -1
  83. package/dist/elements/a-input-time.js +101 -168
  84. package/dist/elements/a-input.css +1 -1
  85. package/dist/elements/a-input.d.ts +10 -8
  86. package/dist/elements/a-input.js +155 -230
  87. package/dist/elements/a-loader.d.ts +5 -8
  88. package/dist/elements/a-loader.js +1 -2
  89. package/dist/elements/a-menu-item.css +1 -1
  90. package/dist/elements/a-menu-item.d.ts +5 -3
  91. package/dist/elements/a-menu-item.js +5 -1
  92. package/dist/elements/a-menu.css +1 -1
  93. package/dist/elements/a-menu.d.ts +5 -7
  94. package/dist/elements/a-menu.js +383 -299
  95. package/dist/elements/a-panel.css +1 -1
  96. package/dist/elements/a-panel.js +19 -37
  97. package/dist/elements/a-progress.css +1 -1
  98. package/dist/elements/a-progress.d.ts +0 -22
  99. package/dist/elements/a-progress.js +0 -100
  100. package/dist/elements/a-radio-group.d.ts +1 -1
  101. package/dist/elements/a-radio-group.js +6 -4
  102. package/dist/elements/a-select-faceted.d.ts +1 -1
  103. package/dist/elements/a-select-faceted.js +1 -1
  104. package/dist/elements/a-select.d.ts +1 -1
  105. package/dist/elements/a-select.js +1 -1
  106. package/dist/elements/a-slider.css +1 -1
  107. package/dist/elements/a-slider.d.ts +11 -3
  108. package/dist/elements/a-slider.js +244 -242
  109. package/dist/elements/a-switch.css +1 -1
  110. package/dist/elements/a-tab.css +1 -1
  111. package/dist/elements/a-tabs.css +1 -1
  112. package/dist/elements/a-tabs.d.ts +1 -1
  113. package/dist/elements/a-tabs.js +6 -4
  114. package/dist/elements/a-tag.css +1 -1
  115. package/dist/elements/a-text.css +1 -1
  116. package/dist/elements/a-text.d.ts +6 -43
  117. package/dist/elements/a-text.js +110 -193
  118. package/dist/elements/a-title.css +1 -1
  119. package/dist/elements/a-toast.css +1 -1
  120. package/dist/elements/a-toast.d.ts +10 -45
  121. package/dist/elements/a-toast.js +80 -66
  122. package/dist/elements/a-toaster.css +1 -1
  123. package/dist/elements/a-toaster.js +9 -44
  124. package/dist/elements/a-tooltip.css +1 -1
  125. package/dist/elements/a-tooltip.d.ts +1 -1
  126. package/dist/elements/a-tooltip.js +142 -136
  127. package/dist/elements/copy-behavior.d.ts +1 -1
  128. package/dist/elements/copy-behavior.js +21 -21
  129. package/dist/elements/index.d.ts +3 -3
  130. package/dist/elements/index.js +3 -7
  131. package/dist/elements/input-focus-modality.d.ts +15 -0
  132. package/dist/elements/input-focus-modality.js +44 -0
  133. package/dist/elements/input-trailing-actions.d.ts +5 -0
  134. package/dist/elements/input-trailing-actions.js +38 -0
  135. package/dist/elements/shadow-aria.d.ts +2 -0
  136. package/dist/elements/shadow-aria.js +2 -1
  137. package/dist/elements/value-input.d.ts +3 -0
  138. package/dist/elements/value-input.js +12 -0
  139. package/dist/general_types.d.ts +103 -96
  140. package/dist/index.d.ts +1 -1
  141. package/dist/input-events.d.ts +4 -0
  142. package/dist/input-events.js +15 -0
  143. package/dist/jsx-runtime.d.ts +70 -23
  144. package/dist/jsx-runtime.js +26 -19
  145. package/dist/native-event-props.d.ts +2 -0
  146. package/dist/native-event-props.js +10 -0
  147. package/dist/reset.css +1 -1
  148. package/dist/theme-antithesis.css +10 -1
  149. package/dist/theme-antune.css +1 -1
  150. package/docs/accessibility.md +17 -3
  151. package/docs/changelog.md +348 -2
  152. package/docs/comparison.md +2 -2
  153. package/docs/components/avatar.md +5 -0
  154. package/docs/components/banner.md +117 -187
  155. package/docs/components/button.md +22 -0
  156. package/docs/components/card.md +74 -28
  157. package/docs/components/checkbox.md +9 -10
  158. package/docs/components/dialog.md +83 -32
  159. package/docs/components/expander.md +198 -435
  160. package/docs/components/icon.md +11 -17
  161. package/docs/components/input-autocomplete.md +152 -52
  162. package/docs/components/input-date.md +175 -148
  163. package/docs/components/input-time.md +81 -38
  164. package/docs/components/input.md +343 -349
  165. package/docs/components/loader.md +2 -1
  166. package/docs/components/menu.md +35 -6
  167. package/docs/components/panel.md +18 -0
  168. package/docs/components/progress.md +83 -49
  169. package/docs/components/radio.md +39 -43
  170. package/docs/components/select-faceted.md +54 -7
  171. package/docs/components/select.md +390 -386
  172. package/docs/components/slider.md +136 -19
  173. package/docs/components/steps.md +2 -1
  174. package/docs/components/switch.md +8 -9
  175. package/docs/components/tabs.md +110 -104
  176. package/docs/components/text.md +110 -86
  177. package/docs/components/title.md +47 -33
  178. package/docs/components/toaster.md +242 -84
  179. package/docs/components/tooltip.md +204 -113
  180. package/docs/install-config.md +183 -26
  181. package/docs/packages/plot.md +170 -148
  182. package/docs/theming.md +7 -0
  183. package/package.json +41 -3
  184. package/theme-antithesis-license.md +66 -0
@@ -1,51 +1,16 @@
1
1
  import { HTMLElementBase } from '../anta_helpers';
2
2
  import './a-toast.css';
3
- /**
4
- * `<a-toast>` — one item inside an `<a-toaster>`. A style-neutral container around
5
- * slotted content (a `Banner`, `Card`, `Sticker`, a string, or a raw DOM node):
6
- * it owns the enter / exit animation and the auto-dismiss timer (paused while
7
- * hovered or focused), nothing else. No chrome of its own — no ✕, no surface; the
8
- * toasted content brings its own look and its own dismiss affordance. Its `slot`
9
- * attribute (`slot="bottom-right"`, …) routes it to a placement zone.
3
+ /** One notification in a Toaster placement slot. The host owns entry/exit motion;
4
+ * its shadow content slot carries the inherited `--toast-remaining` countdown.
5
+ * The timer and countdown pause while hovered or focused. `Infinity` is sticky.
10
6
  *
11
- * Content arrives one of two ways: as slotted light-DOM children (a string / JSX
12
- * the `<Toaster>` renders through the reconciler), or as a live DOM node set on
13
- * the `content` property (the wrapper's DOM-node branch), which the element slots
14
- * into its own light DOM. Either way it projects through the shadow `<slot>`.
7
+ * JSX/raw authors own host attributes and children. Setting `content` transfers
8
+ * insertion ownership of that exact node to the element, without cloning it or
9
+ * changing authored siblings. Content must not belong to another reconciler.
15
10
  *
16
- * ## Time left — `--toast-remaining`
17
- *
18
- * While the auto-dismiss timer runs, the element animates `--toast-remaining` from
19
- * `1` to `0` on the slot (paused in lockstep with the timer on hover / focus).
20
- * It's a registered `@property` (see `a-toast.css`) with `inherits: true`, so
21
- * slotted content inherits the live value and can draw a countdown with one CSS
22
- * rule — `transform: scaleX(var(--toast-remaining))` — with no timer of its own
23
- * and no re-render. Display only: the real dismiss is the JS timer, so it degrades
24
- * to a full bar where custom-property animation is unsupported. Sticky
25
- * (`duration="0"`) runs no countdown; the var stays `1`.
26
- *
27
- * ## Dismiss from an action — `data-toast-dismiss`
28
- *
29
- * Any element inside the toast carrying `data-toast-dismiss` dismisses it on click
30
- * — the same convention as `<a-banner>`'s `data-banner-dismiss` and `<a-dialog>`'s
31
- * `data-dialog-close`. It's a click listener (a light-DOM READ, declarative-safe),
32
- * so it catches any activated control — native `<button>`, `<a>`, `<a-button>`,
33
- * `<a-menu-item>` — and keyboard activation for free. A toasted `<Banner>` instead
34
- * dismisses through its own `onDismiss` (wire it to `manager.dismiss(id)`); its
35
- * built-in ✕ and any `data-banner-dismiss` action ride that channel.
36
- *
37
- * It **never removes itself**. Dismissal — from the timer, a `data-toast-dismiss`
38
- * control, or the `leaving` attribute the wrapper sets for a programmatic
39
- * `dismiss` — fades the host out (an off-DOM `:state(leaving)`), then emits a
40
- * bubbling **`dismiss`**; the owner removes the node on that event. So the element
41
- * writes only its own shadow + internals, reads its own attributes (and, read-only,
42
- * the light DOM for the dismiss trigger), appends only its own (wrapper-supplied)
43
- * content node, and dispatches events.
44
- *
45
- * Shadow structure — the host IS the box (no wrappers):
46
- *
47
- * :host ← the fade/slide layer
48
- * <slot> ← the content
11
+ * A `data-toast-dismiss` control, timer expiry, or authored `leaving` attribute
12
+ * starts the exit. After it finishes, the element emits bubbling `dismiss` and
13
+ * its owner removes the toast. The element never removes itself.
49
14
  */
50
15
  export declare class AToastElement extends HTMLElementBase {
51
16
  #private;
@@ -64,7 +29,7 @@ export declare class AToastElement extends HTMLElementBase {
64
29
  restart(): void;
65
30
  /** Play the exit (shadow only), then emit a bubbling `dismiss`. The owner
66
31
  * removes the node on that event — the element never removes itself.
67
- * Idempotent. */
32
+ * Idempotent within each connection. */
68
33
  dismiss(): void;
69
34
  }
70
35
  export declare function register_a_toast(): void;
@@ -1,4 +1,4 @@
1
- import { HTMLElementBase, installDismissTrigger } from "../anta_helpers";
1
+ import { HTMLElementBase, installDismissTrigger, isDOMNode } from "../anta_helpers";
2
2
  import "./a-toast.css";
3
3
  const EXIT_MS = 220;
4
4
  const DEFAULT_DURATION = 5e3;
@@ -10,21 +10,27 @@ class AToastElement extends HTMLElementBase {
10
10
  /** Off-DOM state channel — drives `:state(leaving)` for the exit fade. */
11
11
  #internals;
12
12
  /** The content slot. Also the element the `--toast-remaining` countdown
13
- * animates on, so slotted content can inherit the value (see SHADOW_STYLE). */
13
+ * animates on, so slotted content can inherit the value (see a-toast.css). */
14
14
  #slot;
15
15
  /** A DOM node handed in via the `content` property (the wrapper's DOM-node
16
16
  * branch). String / JSX content arrives as slotted children instead. */
17
17
  #content;
18
18
  /** The content node we last slotted, so an in-place update can swap or clear it
19
19
  * without touching the reconciler's own children. */
20
- #slotted;
20
+ #slotted = [];
21
+ #appliedContent;
21
22
  #timer;
22
23
  #exitTimer;
24
+ #timerView;
25
+ #exitView;
26
+ #countdown;
27
+ #exitCountdownTime = null;
23
28
  /** Time left on the auto-dismiss timer (ms), tracked across pause/resume. */
24
29
  #remaining = 0;
25
30
  /** When the current run started, for computing remaining on pause. */
26
31
  #startedAt = 0;
27
32
  #leaving = false;
33
+ #dismissed = false;
28
34
  // Independent pause holds — the countdown runs only when BOTH are false.
29
35
  #hovered = false;
30
36
  #focused = false;
@@ -36,10 +42,9 @@ class AToastElement extends HTMLElementBase {
36
42
  console.warn("a-toast: ElementInternals unavailable \u2014 exit fade disabled.", err);
37
43
  }
38
44
  const shadow = this.attachShadow({ mode: "open" });
39
- const style = document.createElement("style");
40
- style.textContent = SHADOW_STYLE;
41
- this.#slot = document.createElement("slot");
42
- shadow.append(style, this.#slot);
45
+ this.#slot = this.doc.createElement("slot");
46
+ this.#slot.setAttribute("part", "content");
47
+ shadow.append(this.#slot);
43
48
  installDismissTrigger(this, DISMISS_ATTR, () => this.dismiss());
44
49
  }
45
50
  connectedCallback() {
@@ -48,7 +53,13 @@ class AToastElement extends HTMLElementBase {
48
53
  this.addEventListener("pointerleave", this.#onPointerLeave);
49
54
  this.addEventListener("focusin", this.#onFocusIn);
50
55
  this.addEventListener("focusout", this.#onFocusOut);
51
- if (this.hasAttribute("leaving")) {
56
+ this.#focused = this.matches(":focus-within");
57
+ if (this.#leaving || this.hasAttribute("leaving")) {
58
+ this.#dismissed = false;
59
+ if (this.#exitCountdownTime != null) {
60
+ this.#startCountdown(this.#exitCountdownTime);
61
+ this.#exitCountdownTime = null;
62
+ }
52
63
  this.dismiss();
53
64
  return;
54
65
  }
@@ -56,10 +67,12 @@ class AToastElement extends HTMLElementBase {
56
67
  }
57
68
  disconnectedCallback() {
58
69
  this.#clearTimer();
59
- if (this.#exitTimer != null) {
60
- this.view.clearTimeout(this.#exitTimer);
61
- this.#exitTimer = void 0;
62
- }
70
+ this.#clearExitTimer();
71
+ if (this.#leaving) this.#exitCountdownTime = this.#countdown?.currentTime ?? this.#exitCountdownTime;
72
+ this.#countdown?.cancel();
73
+ this.#countdown = void 0;
74
+ this.#hovered = false;
75
+ this.#focused = false;
63
76
  this.removeEventListener("pointerenter", this.#onPointerEnter);
64
77
  this.removeEventListener("pointerleave", this.#onPointerLeave);
65
78
  this.removeEventListener("focusin", this.#onFocusIn);
@@ -79,13 +92,12 @@ class AToastElement extends HTMLElementBase {
79
92
  /** Cancel a pending exit and bring the toast back (an upsert landed during the
80
93
  * exit window). */
81
94
  #revive() {
82
- if (this.#exitTimer != null) {
83
- this.view.clearTimeout(this.#exitTimer);
84
- this.#exitTimer = void 0;
85
- }
95
+ this.#clearExitTimer();
86
96
  this.#leaving = false;
97
+ this.#dismissed = false;
98
+ this.#exitCountdownTime = null;
87
99
  this.#internals?.states?.delete("leaving");
88
- this.#startTimer();
100
+ if (this.isConnected) this.restart();
89
101
  }
90
102
  /** A live DOM node to show as the content (set by the wrapper's DOM-node
91
103
  * branch). Paired getter/setter so React 19's property assignment works. */
@@ -93,7 +105,7 @@ class AToastElement extends HTMLElementBase {
93
105
  return this.#content ?? null;
94
106
  }
95
107
  set content(node) {
96
- this.#content = node ?? void 0;
108
+ this.#content = isDOMNode(node) ? node : void 0;
97
109
  this.#applyContent();
98
110
  }
99
111
  /** Slot the content node into our own light DOM. Only ever touches the node WE
@@ -104,9 +116,12 @@ class AToastElement extends HTMLElementBase {
104
116
  * arrives as reconciler-owned children, so `#content` is undefined and this
105
117
  * only clears any node we had slotted. */
106
118
  #applyContent() {
107
- if (this.#content === this.#slotted) return;
108
- if (this.#slotted?.parentNode === this) this.removeChild(this.#slotted);
109
- this.#slotted = this.#content;
119
+ if (this.#content === this.#appliedContent && !(this.#content?.nodeType === 11 && this.#content.hasChildNodes())) return;
120
+ for (const node of this.#slotted) {
121
+ if (node.parentNode === this) this.removeChild(node);
122
+ }
123
+ this.#appliedContent = this.#content;
124
+ this.#slotted = this.#content?.nodeType === 11 ? Array.from(this.#content.childNodes) : this.#content ? [this.#content] : [];
110
125
  if (this.#content) this.append(this.#content);
111
126
  }
112
127
  /** Auto-dismiss delay in ms. Empty / non-positive falls back to the 5000ms
@@ -132,25 +147,32 @@ class AToastElement extends HTMLElementBase {
132
147
  * only — the auto-dismiss above is the real JS timer — so it degrades to a
133
148
  * static full bar where custom-property animation is unsupported. Sticky
134
149
  * (`Infinity` duration) runs no countdown; the var stays at its initial 1. */
135
- #startCountdown() {
136
- if (Number.isFinite(this.#duration)) {
137
- this.#slot.style.animation = `toast-remaining ${this.#duration}ms linear forwards`;
138
- this.#eachCountdown((a) => {
139
- a.currentTime = 0;
140
- });
150
+ #startCountdown(currentTime = 0) {
151
+ if (!Number.isFinite(this.#duration)) {
152
+ this.#countdown?.cancel();
153
+ this.#countdown = void 0;
154
+ return;
155
+ }
156
+ try {
157
+ if (!this.#countdown) {
158
+ const styles = this.view.getComputedStyle(this.#slot);
159
+ this.#countdown = this.#slot.animate?.({
160
+ "--toast-remaining": [
161
+ styles.getPropertyValue("--_toast-countdown-from").trim(),
162
+ styles.getPropertyValue("--_toast-countdown-to").trim()
163
+ ]
164
+ }, { duration: this.#duration, fill: "forwards" });
165
+ } else {
166
+ this.#countdown.effect?.updateTiming({ duration: this.#duration });
167
+ }
168
+ if (this.#countdown) this.#countdown.currentTime = currentTime;
141
169
  if (this.#paused || this.#leaving) this.#pauseCountdown();
142
- } else {
143
- this.#slot.style.animation = "";
170
+ } catch {
144
171
  }
145
172
  }
146
- /** Run `fn` for the `--toast-remaining` animation on the slot, if present.
147
- * Guarded — `getAnimations` / animation control may be absent in a partial
148
- * runtime, and the countdown is non-essential (never let it break the toast). */
149
173
  #eachCountdown(fn) {
150
174
  try {
151
- for (const a of this.#slot.getAnimations?.() ?? []) {
152
- if (a.animationName === "toast-remaining") fn(a);
153
- }
175
+ if (this.#countdown) fn(this.#countdown);
154
176
  } catch {
155
177
  }
156
178
  }
@@ -170,16 +192,26 @@ class AToastElement extends HTMLElementBase {
170
192
  return;
171
193
  }
172
194
  this.#startedAt = Date.now();
173
- this.#timer = this.view.setTimeout(() => {
195
+ this.#timerView = this.view;
196
+ this.#timer = this.#timerView.setTimeout(() => {
174
197
  this.#timer = void 0;
198
+ this.#timerView = void 0;
175
199
  this.dismiss();
176
200
  }, this.#remaining);
177
201
  this.#playCountdown();
178
202
  }
179
203
  #clearTimer() {
180
204
  if (this.#timer != null) {
181
- this.view.clearTimeout(this.#timer);
205
+ this.#timerView?.clearTimeout(this.#timer);
182
206
  this.#timer = void 0;
207
+ this.#timerView = void 0;
208
+ }
209
+ }
210
+ #clearExitTimer() {
211
+ if (this.#exitTimer != null) {
212
+ this.#exitView?.clearTimeout(this.#exitTimer);
213
+ this.#exitTimer = void 0;
214
+ this.#exitView = void 0;
183
215
  }
184
216
  }
185
217
  /** Freeze the timer, banking the time left, and freeze the display countdown
@@ -202,7 +234,8 @@ class AToastElement extends HTMLElementBase {
202
234
  this.#focused = true;
203
235
  this.#pause();
204
236
  };
205
- #onFocusOut = () => {
237
+ #onFocusOut = (event) => {
238
+ if (isDOMNode(event.relatedTarget) && this.contains(event.relatedTarget)) return;
206
239
  this.#focused = false;
207
240
  this.#armIfActive();
208
241
  };
@@ -227,49 +260,30 @@ class AToastElement extends HTMLElementBase {
227
260
  * update). If the toast is currently paused, the fresh time is banked and the
228
261
  * countdown stays paused until hover / focus releases. */
229
262
  restart() {
230
- if (this.#leaving) return;
263
+ if (this.#leaving || !this.isConnected) return;
231
264
  this.#clearTimer();
232
265
  this.#startTimer();
233
266
  }
234
267
  /** Play the exit (shadow only), then emit a bubbling `dismiss`. The owner
235
268
  * removes the node on that event — the element never removes itself.
236
- * Idempotent. */
269
+ * Idempotent within each connection. */
237
270
  dismiss() {
238
- if (this.#leaving) return;
271
+ if (this.#exitTimer != null || this.#dismissed) return;
239
272
  this.#leaving = true;
240
273
  this.#clearTimer();
241
274
  this.#pauseCountdown();
242
275
  this.#internals?.states?.add("leaving");
276
+ if (!this.isConnected) return;
243
277
  const wait = this.#exitDuration();
244
- this.#exitTimer = this.view.setTimeout(() => {
278
+ this.#exitView = this.view;
279
+ this.#exitTimer = this.#exitView.setTimeout(() => {
245
280
  this.#exitTimer = void 0;
246
- this.dispatchEvent(new CustomEvent("dismiss", { bubbles: true, composed: true }));
281
+ this.#exitView = void 0;
282
+ this.#dismissed = true;
283
+ this.dispatchEvent(new this.view.CustomEvent("dismiss", { bubbles: true, composed: true }));
247
284
  }, wait);
248
285
  }
249
286
  }
250
- const SHADOW_STYLE = `
251
- :host { display: block; opacity: 1; translate: 0 0; }
252
- :host(:state(leaving)) { opacity: 0; }
253
-
254
- @starting-style {
255
- :host { opacity: 0; translate: 0 12px; }
256
- }
257
-
258
- @media (prefers-reduced-motion: no-preference) {
259
- :host {
260
- transition:
261
- opacity var(--toast-dur, 220ms) ease,
262
- translate var(--toast-dur, 220ms) ease;
263
- }
264
- }
265
-
266
- slot { display: block; }
267
-
268
- @keyframes toast-remaining {
269
- from { --toast-remaining: 1; }
270
- to { --toast-remaining: 0; }
271
- }
272
- `;
273
287
  function register_a_toast() {
274
288
  if (typeof customElements === "undefined") return;
275
289
  if (!customElements.get("a-toast")) {
@@ -1 +1 @@
1
- @layer base,anta,components,utilities;@layer anta.reset,anta.components,anta.theme;@layer anta.components{a-toaster{--toaster-inset: 16px;--toaster-gap: 12px;--toaster-width: 380px}a-toaster:not(:defined){display:none}}
1
+ @layer base,anta,components,utilities;@layer anta.reset,anta.components,anta.theme;@layer anta.components{a-toaster{--toaster-inset: 16px;--toaster-gap: 12px;--toaster-width: 380px;display:contents}a-toaster:not(:defined){display:none}a-toaster::part(region){position:fixed;inset:0;margin:0;padding:0;border:0;background:transparent;overflow:visible;pointer-events:none;width:auto;height:auto}a-toaster::part(region):popover-open{display:block}a-toaster::part(zone){position:absolute;display:flex;flex-direction:column;gap:var(--toaster-gap, 12px);width:min(var(--toaster-width, 380px),calc(100vw - 2 * var(--toaster-inset, 16px)));box-sizing:border-box;pointer-events:none}a-toaster>:is([slot=top-left],[slot=top-center],[slot=top-right],[slot=bottom-left],[slot=bottom-center],[slot=bottom-right]){pointer-events:auto}a-toaster::part(top-left),a-toaster::part(top-center),a-toaster::part(top-right){top:var(--toaster-inset, 16px);flex-direction:column-reverse}a-toaster::part(bottom-left),a-toaster::part(bottom-center),a-toaster::part(bottom-right){bottom:var(--toaster-inset, 16px)}a-toaster::part(top-left),a-toaster::part(bottom-left){left:var(--toaster-inset, 16px)}a-toaster::part(top-right),a-toaster::part(bottom-right){right:var(--toaster-inset, 16px)}a-toaster::part(top-center),a-toaster::part(bottom-center){left:50%;transform:translate(-50%)}}
@@ -5,22 +5,22 @@ const ZONES = TOAST_PLACEMENTS;
5
5
  class AToasterElement extends HTMLElementBase {
6
6
  #region;
7
7
  #shown = false;
8
+ #slots = [];
8
9
  constructor() {
9
10
  super();
10
11
  const shadow = this.attachShadow({ mode: "open" });
11
- const style = document.createElement("style");
12
- style.textContent = SHADOW_STYLE;
13
- this.#region = document.createElement("div");
14
- this.#region.className = "region";
12
+ this.#region = this.doc.createElement("div");
15
13
  this.#region.setAttribute("part", "region");
16
14
  this.#region.setAttribute("popover", "manual");
17
15
  for (const zone of ZONES) {
18
- const slot = document.createElement("slot");
16
+ const slot = this.doc.createElement("slot");
19
17
  slot.name = zone;
18
+ slot.setAttribute("part", `zone ${zone}`);
20
19
  this.#region.append(slot);
20
+ this.#slots.push(slot);
21
21
  }
22
22
  this.#region.addEventListener("slotchange", this.#syncVisibility);
23
- shadow.append(style, this.#region);
23
+ shadow.append(this.#region);
24
24
  }
25
25
  connectedCallback() {
26
26
  this.#syncVisibility();
@@ -28,12 +28,12 @@ class AToasterElement extends HTMLElementBase {
28
28
  disconnectedCallback() {
29
29
  this.#shown = false;
30
30
  }
31
- /** Show the region while it holds a toast, hide it when empty. A child count
32
- * read + a shadow popover toggle — no host or light-DOM mutation. */
31
+ /** Show the popover while a placement has assigned elements. Unmatched
32
+ * children do not keep an empty region in the top layer. */
33
33
  #syncVisibility = () => {
34
34
  const region = this.#region;
35
35
  if (typeof region.showPopover !== "function") return;
36
- const has = this.childElementCount > 0;
36
+ const has = this.#slots.some((slot) => slot.assignedElements().length > 0);
37
37
  if (has && !this.#shown) {
38
38
  if (!this.isConnected) return;
39
39
  try {
@@ -50,41 +50,6 @@ class AToasterElement extends HTMLElementBase {
50
50
  }
51
51
  };
52
52
  }
53
- const SHADOW_STYLE = `
54
- :host { display: contents; }
55
-
56
- .region {
57
- position: fixed;
58
- inset: 0;
59
- margin: 0;
60
- padding: 0;
61
- border: 0;
62
- background: transparent;
63
- overflow: visible;
64
- pointer-events: none;
65
- width: auto;
66
- height: auto;
67
- }
68
- .region:popover-open { display: block; }
69
-
70
- slot {
71
- position: absolute;
72
- display: flex;
73
- flex-direction: column;
74
- gap: var(--toaster-gap, 12px);
75
- width: min(var(--toaster-width, 380px), calc(100vw - 2 * var(--toaster-inset, 16px)));
76
- box-sizing: border-box;
77
- pointer-events: none;
78
- }
79
-
80
- ::slotted(*) { pointer-events: auto; }
81
-
82
- slot[name^="top-"] { top: var(--toaster-inset, 16px); flex-direction: column-reverse; }
83
- slot[name^="bottom-"] { bottom: var(--toaster-inset, 16px); flex-direction: column; }
84
- slot[name$="-left"] { left: var(--toaster-inset, 16px); }
85
- slot[name$="-right"] { right: var(--toaster-inset, 16px); }
86
- slot[name$="-center"] { left: 50%; transform: translateX(-50%); }
87
- `;
88
53
  function register_a_toaster() {
89
54
  if (typeof customElements === "undefined") return;
90
55
  if (!customElements.get("a-toaster")) {
@@ -1 +1 @@
1
- @layer base,anta,components,utilities;@layer anta.reset,anta.components,anta.theme;@layer anta.components{a-tooltip:not(:defined){display:none}@media(hover:none)and (pointer:coarse){:where(:has(>a-tooltip)){-webkit-touch-callout:none;user-select:none}}a-tooltip{--tooltip-bg: light-dark( color-mix(in oklch, var(--bg-1) 80%, transparent), color-mix(in oklch, var(--bg-1) 70%, transparent) );--tooltip-shadow: 0 1px 8px color-mix(in oklch, var(--text-1) 15%, transparent), 0 0 1px color-mix(in oklch, var(--text-1) 50%, transparent);--tooltip-radius: 3px;--tooltip-max-width: min( calc(100vw - 20px) , 80ch);--tooltip-border: none;--tooltip-backdrop-filter: blur(8px)}a-tooltip[round]{--tooltip-radius: 20px;@supports (border-radius: attr(probe type(<length>),1px)){--tooltip-radius: attr(round type(<length>), 20px)}}.dark a-tooltip{--tooltip-shadow: 0 2px 16px color-mix(in oklch, black 70%, transparent), inset 0 0 0 1px color-mix(in oklch, white 15%, transparent)}@media(forced-colors:active){a-tooltip{--tooltip-border: 1px dotted CanvasText}}}a-tooltip>p:last-child,a-tooltip>:not([slot])>p:last-child{margin-block-end:0}
1
+ @layer base,anta,components,utilities;@layer anta.reset,anta.components,anta.theme;@layer anta.components{a-tooltip:not(:defined){display:none}@media(hover:none)and (pointer:coarse){:where(:has(>a-tooltip)){-webkit-touch-callout:none;user-select:none}}a-tooltip{--tooltip-bg: light-dark( color-mix(in oklch, var(--bg-1) 80%, transparent), color-mix(in oklch, var(--bg-1) 70%, transparent) );--tooltip-shadow: 0 1px 8px color-mix(in oklch, var(--text-1) 15%, transparent), 0 0 1px color-mix(in oklch, var(--text-1) 50%, transparent);--tooltip-radius: 3px;--tooltip-max-width: min( calc(100vw - 20px) , 80ch);--tooltip-border: none;--tooltip-backdrop-filter: blur(8px);display:contents}a-tooltip::part(container){--_dur: .15s;position:fixed;left:0;top:0;margin:0;padding:0;border:0;background:transparent;color:inherit;overflow:visible;box-sizing:border-box;width:fit-content;pointer-events:none;opacity:0;transition:opacity var(--_dur) ease,overlay var(--_dur) ease allow-discrete,display var(--_dur) ease allow-discrete}a-tooltip::part(container):popover-open{opacity:1}@starting-style{a-tooltip::part(container):popover-open{opacity:0}}a-tooltip::part(bubble){display:block;box-sizing:border-box;width:fit-content;max-width:var(--tooltip-max-width, min( calc(100vw - 20px) , 80ch));max-height:calc(100vh - 20px);overflow:clip;text-align:left;background:var(--tooltip-bg, Canvas);color:var(--text-2, CanvasText);box-shadow:var(--tooltip-shadow, 0 1px 8px color-mix(in oklch, black 20%, transparent));-webkit-backdrop-filter:var(--tooltip-backdrop-filter, blur(8px));backdrop-filter:var(--tooltip-backdrop-filter, blur(8px));padding:var(--tooltip-padding, 4px 8px);border:var(--tooltip-border, none);border-radius:var(--tooltip-radius, 3px);outline:none;font-family:var(--sans-serif, system-ui, sans-serif);font-size:14px;font-weight:400;font-style:normal;font-stretch:normal;line-height:1.5;letter-spacing:var(--_tooltip-letter-spacing, .025ch);word-spacing:normal;text-transform:none;white-space:break-spaces;word-break:break-word;overflow-wrap:break-word;transition:opacity 60ms linear}a-tooltip[interactive]::part(container){pointer-events:auto}a-tooltip:state(proximity-hidden)::part(bubble){transition:none}@media(prefers-reduced-motion:reduce){a-tooltip::part(container),a-tooltip::part(bubble){transition:none}}a-tooltip[round]{--tooltip-radius: 20px;@supports (border-radius: attr(probe type(<length>),1px)){--tooltip-radius: attr(round type(<length>), 20px)}}.dark a-tooltip{--tooltip-shadow: 0 2px 16px color-mix(in oklch, black 70%, transparent), inset 0 0 0 1px color-mix(in oklch, white 15%, transparent)}@media(forced-colors:active){a-tooltip{--tooltip-border: 1px dotted CanvasText}}}a-tooltip>p:last-child,a-tooltip>:not([slot])>p:last-child{margin-block-end:0}
@@ -7,7 +7,7 @@ import './a-tooltip.css';
7
7
  * Styling notes (`a-tooltip.css` ships comment-free):
8
8
  * - `a-tooltip:not(:defined)` is hidden — before upgrade the host is an
9
9
  * unknown inline element and its content would flash *inside the anchor*.
10
- * Once defined, the shadow `:host { display: contents }` governs and
10
+ * Once defined, external host CSS applies `display: contents` and
11
11
  * content renders only in the popover via the slot.
12
12
  * - Only the bubble "chrome" is tokenized (`--tooltip-*`): it lives inside
13
13
  * the shadow popover, unreachable from plain consumer CSS; the custom