@antadesign/anta 0.3.33 → 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 (205) 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 -1353
  7. package/dist/components/Banner.d.ts +25 -40
  8. package/dist/components/Banner.js +15 -17
  9. package/dist/components/Breadcrumbs.d.ts +0 -1
  10. package/dist/components/Breadcrumbs.js +0 -1
  11. package/dist/components/Button.d.ts +2 -3
  12. package/dist/components/Button.js +1 -0
  13. package/dist/components/ButtonCopy.js +3 -2
  14. package/dist/components/Calendar.js +13 -13
  15. package/dist/components/Card.d.ts +9 -17
  16. package/dist/components/Card.js +50 -46
  17. package/dist/components/Dialog.d.ts +1 -0
  18. package/dist/components/Dialog.js +16 -10
  19. package/dist/components/Expander.d.ts +6 -21
  20. package/dist/components/Expander.js +24 -23
  21. package/dist/components/Icon.d.ts +8 -14
  22. package/dist/components/Input.d.ts +77 -96
  23. package/dist/components/Input.js +23 -27
  24. package/dist/components/InputAutocomplete.d.ts +29 -10
  25. package/dist/components/InputAutocomplete.js +25 -8
  26. package/dist/components/InputDate.d.ts +0 -1
  27. package/dist/components/InputDate.js +2 -3
  28. package/dist/components/InputTime.d.ts +10 -4
  29. package/dist/components/InputTime.js +20 -16
  30. package/dist/components/Loader.d.ts +2 -2
  31. package/dist/components/Menu.d.ts +5 -4
  32. package/dist/components/Menu.js +13 -8
  33. package/dist/components/MenuItem.js +1 -14
  34. package/dist/components/MenuItemCopy.js +2 -2
  35. package/dist/components/Panel.d.ts +3 -1
  36. package/dist/components/Panel.js +17 -11
  37. package/dist/components/Progress.d.ts +21 -23
  38. package/dist/components/Progress.js +13 -9
  39. package/dist/components/RadioGroup.js +6 -8
  40. package/dist/components/{select-parts.css → Select.css} +1 -1
  41. package/dist/components/Select.d.ts +53 -36
  42. package/dist/components/Select.js +53 -98
  43. package/dist/components/SelectFaceted.d.ts +7 -4
  44. package/dist/components/SelectFaceted.js +21 -10
  45. package/dist/components/Slider.d.ts +28 -11
  46. package/dist/components/Slider.js +20 -20
  47. package/dist/components/Steps.css +1 -1
  48. package/dist/components/Steps.d.ts +0 -1
  49. package/dist/components/Steps.js +1 -12
  50. package/dist/components/Tabs.d.ts +17 -20
  51. package/dist/components/Tabs.js +7 -9
  52. package/dist/components/Text.d.ts +55 -36
  53. package/dist/components/Text.js +35 -22
  54. package/dist/components/Title.d.ts +19 -27
  55. package/dist/components/Title.js +2 -1
  56. package/dist/components/Toaster.js +9 -2
  57. package/dist/components/Tooltip.d.ts +10 -15
  58. package/dist/components/copy-props.d.ts +14 -3
  59. package/dist/components/copy-props.js +14 -3
  60. package/dist/components/select-tree.d.ts +24 -0
  61. package/dist/components/select-tree.js +35 -0
  62. package/dist/element-base.d.ts +10 -0
  63. package/dist/element-base.js +15 -0
  64. package/dist/elements/a-avatar.css +1 -1
  65. package/dist/elements/a-avatar.js +2 -28
  66. package/dist/elements/a-banner.css +1 -1
  67. package/dist/elements/a-banner.d.ts +4 -1
  68. package/dist/elements/a-banner.js +22 -112
  69. package/dist/elements/a-breadcrumbs.d.ts +1 -0
  70. package/dist/elements/a-breadcrumbs.js +1 -0
  71. package/dist/elements/a-button.css +1 -1
  72. package/dist/elements/a-button.d.ts +10 -7
  73. package/dist/elements/a-button.js +18 -1
  74. package/dist/elements/a-calendar.d.ts +14 -13
  75. package/dist/elements/a-calendar.js +29 -24
  76. package/dist/elements/a-card.css +1 -1
  77. package/dist/elements/a-card.d.ts +0 -9
  78. package/dist/elements/a-card.js +0 -184
  79. package/dist/elements/a-checkbox.css +1 -1
  80. package/dist/elements/a-copy.css +1 -1
  81. package/dist/elements/a-copy.d.ts +1 -5
  82. package/dist/elements/a-copy.js +129 -108
  83. package/dist/elements/a-dialog.css +1 -1
  84. package/dist/elements/a-dialog.d.ts +1 -1
  85. package/dist/elements/a-dialog.js +68 -194
  86. package/dist/elements/a-expander.css +1 -1
  87. package/dist/elements/a-expander.d.ts +4 -30
  88. package/dist/elements/a-expander.js +85 -344
  89. package/dist/elements/a-icon.d.ts +5 -23
  90. package/dist/elements/a-icon.js +1 -2
  91. package/dist/elements/a-input-date.d.ts +1 -0
  92. package/dist/elements/a-input-date.js +1 -0
  93. package/dist/elements/a-input-time.css +1 -1
  94. package/dist/elements/a-input-time.d.ts +2 -1
  95. package/dist/elements/a-input-time.js +101 -168
  96. package/dist/elements/a-input.css +1 -1
  97. package/dist/elements/a-input.d.ts +10 -8
  98. package/dist/elements/a-input.js +155 -230
  99. package/dist/elements/a-loader.d.ts +5 -8
  100. package/dist/elements/a-loader.js +1 -2
  101. package/dist/elements/a-menu-item.css +1 -1
  102. package/dist/elements/a-menu-item.d.ts +5 -3
  103. package/dist/elements/a-menu-item.js +5 -1
  104. package/dist/elements/a-menu.css +1 -1
  105. package/dist/elements/a-menu.d.ts +5 -7
  106. package/dist/elements/a-menu.js +383 -299
  107. package/dist/elements/a-panel.css +1 -1
  108. package/dist/elements/a-panel.js +19 -37
  109. package/dist/elements/a-progress.css +1 -1
  110. package/dist/elements/a-progress.d.ts +0 -22
  111. package/dist/elements/a-progress.js +0 -100
  112. package/dist/elements/a-radio-group.d.ts +1 -1
  113. package/dist/elements/a-radio-group.js +6 -4
  114. package/dist/elements/a-select-faceted.d.ts +1 -0
  115. package/dist/elements/a-select-faceted.js +1 -0
  116. package/dist/elements/a-select.d.ts +1 -0
  117. package/dist/elements/a-select.js +1 -0
  118. package/dist/elements/a-slider.css +1 -1
  119. package/dist/elements/a-slider.d.ts +11 -3
  120. package/dist/elements/a-slider.js +244 -242
  121. package/dist/elements/a-steps.d.ts +1 -0
  122. package/dist/elements/a-steps.js +1 -0
  123. package/dist/elements/a-switch.css +1 -1
  124. package/dist/elements/a-tab.css +1 -1
  125. package/dist/elements/a-tabpanel.d.ts +2 -2
  126. package/dist/elements/a-tabpanel.js +13 -3
  127. package/dist/elements/a-tabs.css +1 -1
  128. package/dist/elements/a-tabs.d.ts +1 -1
  129. package/dist/elements/a-tabs.js +6 -4
  130. package/dist/elements/a-tag.css +1 -1
  131. package/dist/elements/a-tag.d.ts +1 -0
  132. package/dist/elements/a-tag.js +1 -0
  133. package/dist/elements/a-text.css +1 -1
  134. package/dist/elements/a-text.d.ts +6 -43
  135. package/dist/elements/a-text.js +110 -193
  136. package/dist/elements/a-title.css +1 -1
  137. package/dist/elements/a-title.d.ts +1 -0
  138. package/dist/elements/a-title.js +1 -0
  139. package/dist/elements/a-toast.css +1 -1
  140. package/dist/elements/a-toast.d.ts +10 -45
  141. package/dist/elements/a-toast.js +80 -66
  142. package/dist/elements/a-toaster.css +1 -1
  143. package/dist/elements/a-toaster.js +9 -44
  144. package/dist/elements/a-tooltip.css +1 -1
  145. package/dist/elements/a-tooltip.d.ts +1 -1
  146. package/dist/elements/a-tooltip.js +142 -136
  147. package/dist/elements/copy-behavior.d.ts +1 -1
  148. package/dist/elements/copy-behavior.js +21 -21
  149. package/dist/elements/index.d.ts +9 -3
  150. package/dist/elements/index.js +6 -6
  151. package/dist/elements/input-focus-modality.d.ts +15 -0
  152. package/dist/elements/input-focus-modality.js +44 -0
  153. package/dist/elements/input-trailing-actions.d.ts +5 -0
  154. package/dist/elements/input-trailing-actions.js +38 -0
  155. package/dist/elements/shadow-aria.d.ts +2 -0
  156. package/dist/elements/shadow-aria.js +2 -1
  157. package/dist/elements/value-input.d.ts +3 -0
  158. package/dist/elements/value-input.js +12 -0
  159. package/dist/general_types.d.ts +103 -96
  160. package/dist/index.d.ts +1 -1
  161. package/dist/input-events.d.ts +4 -0
  162. package/dist/input-events.js +15 -0
  163. package/dist/jsx-runtime.d.ts +70 -23
  164. package/dist/jsx-runtime.js +26 -19
  165. package/dist/native-event-props.d.ts +2 -0
  166. package/dist/native-event-props.js +10 -0
  167. package/dist/reset.css +1 -1
  168. package/dist/theme-antithesis.css +10 -1
  169. package/dist/theme-antune.css +1 -1
  170. package/docs/accessibility.md +17 -3
  171. package/docs/changelog.md +384 -2
  172. package/docs/comparison.md +2 -2
  173. package/docs/components/avatar.md +5 -0
  174. package/docs/components/banner.md +117 -187
  175. package/docs/components/breadcrumbs.md +9 -6
  176. package/docs/components/button.md +22 -0
  177. package/docs/components/card.md +74 -28
  178. package/docs/components/checkbox.md +9 -10
  179. package/docs/components/dialog.md +83 -32
  180. package/docs/components/expander.md +198 -435
  181. package/docs/components/icon.md +11 -17
  182. package/docs/components/input-autocomplete.md +152 -52
  183. package/docs/components/input-date.md +175 -148
  184. package/docs/components/input-time.md +81 -38
  185. package/docs/components/input.md +343 -349
  186. package/docs/components/loader.md +2 -1
  187. package/docs/components/menu.md +35 -6
  188. package/docs/components/panel.md +18 -0
  189. package/docs/components/progress.md +83 -49
  190. package/docs/components/radio.md +39 -43
  191. package/docs/components/select-faceted.md +54 -7
  192. package/docs/components/select.md +390 -386
  193. package/docs/components/slider.md +136 -19
  194. package/docs/components/steps.md +6 -3
  195. package/docs/components/switch.md +8 -9
  196. package/docs/components/tabs.md +110 -104
  197. package/docs/components/text.md +110 -86
  198. package/docs/components/title.md +47 -33
  199. package/docs/components/toaster.md +242 -84
  200. package/docs/components/tooltip.md +204 -113
  201. package/docs/install-config.md +461 -96
  202. package/docs/packages/plot.md +228 -143
  203. package/docs/theming.md +29 -13
  204. package/package.json +43 -4
  205. package/theme-antithesis-license.md +66 -0
@@ -7,13 +7,12 @@ export interface BannerProps extends BaseProps {
7
7
  /** Trailing controls (buttons, links) rendered as a compact row after the
8
8
  * message and `children`. */
9
9
  actions?: React.ReactNode;
10
- /** Free content placed BETWEEN the message and the actions. */
10
+ /** Supporting content between the message and actions. */
11
11
  children?: React.ReactNode;
12
- /** Semantic tone, or any literal CSS color (`'#ff1493'`, `'rebeccapurple'`) for
13
- * a one-off custom tone. Named tones re-point the surface, text, and border
14
- * color (used if you opt into a border); a custom color keeps its hue while
15
- * lightness/chroma are pinned. `'neutral'` (the default) is the same as omitting it.
16
- * @defaultValue 'neutral' */
12
+ /** Semantic tone or a CSS color. Colors the surface, text, border, and dismiss
13
+ * control. Custom colors use an inline CSS variable; under a CSP that blocks
14
+ * style attributes, provide that variable through external CSS instead.
15
+ * @defaultValue 'neutral' */
17
16
  tone?: "neutral" | "brand" | "info" | "success" | "warning" | "critical" | (string & {});
18
17
  /** Positions the content row. `'start'` aligns it to the inline edge; `'center'`
19
18
  * centers it. Wrapped message text follows the selected alignment.
@@ -23,47 +22,33 @@ export interface BannerProps extends BaseProps {
23
22
  * which clamps to a stadium) instead of the edge-to-edge strip. Still borderless
24
23
  * by default. Pass a `number` (px) or a CSS length string for a custom radius. */
25
24
  round?: boolean | number | string;
26
- /** Whether the trailing ✕ dismiss button is present. `false` removes it — drive
27
- * dismissal yourself (a controlled `dismissed`, or your own control) instead.
28
- * It removes the built-in ✕ without making the banner un-dismissible.
29
- * @defaultValue true */
25
+ /** Show the built-in Dismiss control. Set false when the notice is permanent
26
+ * or another action requests dismissal through `data-banner-dismiss`.
27
+ * @defaultValue true */
30
28
  closable?: boolean;
31
- /** ARIA role for the strip host. `'status'` (a polite live region, so the notice
32
- * reaches assistive tech) by default; pass `'alert'` for an urgent notice that
33
- * should interrupt, or a landmark role.
34
- * @defaultValue 'status' */
29
+ /** Host role. Use 'status' for polite announcements or 'alert' for urgent ones.
30
+ * @defaultValue 'status' */
35
31
  role?: string;
36
- /** Controlled dismissed state. When provided, the consumer owns visibility: the
37
- * banner follows this prop only, and clicking ✕ *requests* dismissal (reject by
38
- * not updating). **Requires `onDismiss`** — controlled mode never self-hides, so
39
- * without a handler to set `dismissed` the ✕ can't close the banner. Leave
40
- * undefined for uncontrolled. */
32
+ /** Controlled visibility. Set true in `onDismiss` to accept a request, or keep
33
+ * false to reject it. Set false to show the notice again. Dismissal hides the
34
+ * host and retains its children; it does not unmount them. */
41
35
  dismissed?: boolean;
42
36
  /** Initial dismissed state for the uncontrolled case (read once on mount). */
43
37
  defaultDismissed?: boolean;
44
- /** Fired when the user dismisses the banner (clicks ✕). Uncontrolled, the banner
45
- * hides itself and this notifies. Controlled, use it to accept the
46
- * request — set `dismissed` to `true` (or ignore to reject); pair it with
47
- * `dismissed`, or the banner can't be closed. */
38
+ /** Dismissal request from the close control or a `data-banner-dismiss` action.
39
+ * Controlled Banner stays visible until `dismissed` changes. Uncontrolled
40
+ * Banner applies an uncanceled request itself. */
48
41
  onDismiss?: () => void;
49
42
  }
50
43
  /**
51
- * `<Banner>` — a full-width, dismissible message strip. `message`, `children`, and
52
- * `actions` form a row at the inline start by default; `align="center"` centers it.
44
+ * Full-width notice with a message, supporting content, actions, and dismissal.
45
+ * JSX owns the light-DOM regions and ARIA. External CSS renders the layout before
46
+ * registration; the browser element owns visibility through ElementInternals.
53
47
  *
54
- * A pure, stateless pass-through to `<a-banner>`: the element owns dismissal and
55
- * visibility, so the wrapper holds no state and grabs no ref — it maps props to
56
- * attributes (safe wherever the host DOM is reconciled, incl. off the UI thread).
57
- *
58
- * Uncontrolled (`defaultDismissed`): the element owns visibility and the ✕
59
- * self-dismisses. Controlled (`dismissed` + `onDismiss`): the wrapper emits
60
- * `state="open"|"closed"`; the element treats the attribute as the source of
61
- * truth and the ✕ only *requests* dismissal. The element uses Anta's shared
62
- * open/closed state contract under the hood — the wrapper presents it as the
63
- * banner-natural `dismissed` (closed = dismissed). See STATEFUL-COMPONENTS.md.
64
- *
65
- * Requires `@antadesign/anta/elements` to be imported (client-side only) to
66
- * register the underlying custom element.
48
+ * `dismissed` controls visibility. `onDismiss` requests dismissal without applying
49
+ * it in controlled mode. `defaultDismissed` seeds uncontrolled visibility once.
50
+ * Granular dismissal also requires `@antadesign/anta/elements/a-button` and the
51
+ * close icon requires `@antadesign/anta/elements/a-icon`.
67
52
  *
68
53
  * @example Basic usage
69
54
  * ```tsx
@@ -72,8 +57,8 @@ export interface BannerProps extends BaseProps {
72
57
  *
73
58
  * @example Controlled
74
59
  * ```tsx
75
- * const [hidden, setHidden] = useState(false)
76
- * <Banner message="Saved." dismissed={hidden} onDismiss={() => setHidden(true)} />
60
+ * const [dismissed, setDismissed] = useState(false)
61
+ * <Banner message="Saved." dismissed={dismissed} onDismiss={() => setDismissed(true)} />
77
62
  * ```
78
63
  */
79
64
  export declare const Banner: ({ message, actions, tone, align, round, closable, role, dismissed, defaultDismissed, onDismiss, className, style, children, ...rest }: BannerProps) => any;
@@ -1,6 +1,7 @@
1
1
  import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
- import { nativeStateChange, neutralToneAttr, toneStyle, roundStyle, roundAttr } from "../anta_helpers";
2
+ import { nativeStateChange, neutralToneAttr, toneStyle, roundStyle, roundAttr, hasChildren } from "../anta_helpers";
3
3
  import { Button } from "./Button";
4
+ import { useNativeEventProps } from "../native-event-props";
4
5
  const Banner = ({
5
6
  message,
6
7
  actions,
@@ -18,12 +19,20 @@ const Banner = ({
18
19
  ...rest
19
20
  }) => {
20
21
  const controlled = dismissed != null;
22
+ const eventProps = useNativeEventProps({
23
+ onstatechange: onDismiss ? (e) => {
24
+ const { detail, isOwn } = nativeStateChange(e);
25
+ if (isOwn && detail?.next === "closed") onDismiss();
26
+ } : void 0
27
+ });
21
28
  const computedStyle = roundStyle(
22
29
  round,
23
30
  "--banner-round",
24
31
  toneStyle(tone, "--banner-tone-source", style)
25
32
  );
26
- const messageNode = message == null ? null : typeof message === "string" || typeof message === "number" ? /* @__PURE__ */ jsx("a-banner-message", { slot: "message", children: message }) : /* @__PURE__ */ jsx("span", { slot: "message", style: { display: "contents" }, children: message });
33
+ const hasMessage = hasChildren(message) && typeof message !== "boolean" && (typeof message !== "string" || Boolean(message.trim()));
34
+ const hasActions = hasChildren(actions) && typeof actions !== "boolean" && (typeof actions !== "string" || Boolean(actions.trim()));
35
+ const messageNode = !hasMessage ? null : typeof message === "string" || typeof message === "number" ? /* @__PURE__ */ jsx("a-banner-message", { children: message }) : /* @__PURE__ */ jsx("a-banner-content", { children: message });
27
36
  return /* @__PURE__ */ jsxs(
28
37
  "a-banner",
29
38
  {
@@ -33,26 +42,15 @@ const Banner = ({
33
42
  align: align === "center" ? "center" : void 0,
34
43
  round: roundAttr(round),
35
44
  role,
36
- onstatechange: onDismiss ? (e) => {
37
- const { detail, isOwn } = nativeStateChange(e);
38
- if (isOwn && detail?.next === "closed") onDismiss();
39
- } : void 0,
45
+ ...eventProps,
40
46
  class: className,
41
47
  style: computedStyle,
42
48
  ...rest,
43
49
  children: [
44
50
  messageNode,
45
- children,
46
- actions != null && /* @__PURE__ */ jsx("span", { "data-anta-banner-actions-wrapper": "", slot: "actions", style: { display: "contents" }, children: actions }),
47
- closable !== false && // A real <a-button> (light DOM → fully styled, keyboard-focusable) in the
48
- // element's `close` slot. It fires the bubbling `dismissrequest` event via
49
- // a-button's global listener, so dismissing works even without framework
50
- // hydration; the element turns that into a dismiss request.
51
- // CONTRACT: `data-custom-event` below MUST match `DISMISS_TRIGGER` in the
52
- // element (src/elements/a-banner.ts). Duplicated, not shared — importing
53
- // the element module here would self-register it and break the
54
- // wrapper/element decoupling. Rename in both places.
55
- /* @__PURE__ */ jsx("span", { slot: "close", style: { display: "contents" }, children: /* @__PURE__ */ jsx(
51
+ hasChildren(children) && typeof children !== "boolean" && /* @__PURE__ */ jsx("a-banner-content", { children }),
52
+ hasActions && /* @__PURE__ */ jsx("a-banner-actions", { children: actions }),
53
+ closable !== false && /* @__PURE__ */ jsx("a-banner-close", { children: /* @__PURE__ */ jsx(
56
54
  Button,
57
55
  {
58
56
  priority: "quaternary",
@@ -2,7 +2,6 @@ import type { IconShape } from '../elements/a-icon.shapes';
2
2
  import type { BaseProps } from '../general_types';
3
3
  import type { BaseButtonProps } from './Button';
4
4
  import type { CopyTarget } from './copy-props';
5
- import './Breadcrumbs.css';
6
5
  /** The text separators built into `<Breadcrumbs>`. Pass an `IconShape` for a
7
6
  * graphic separator instead. */
8
7
  export type BreadcrumbTextSeparator = '→' | '/' | '•' | '▸';
@@ -5,7 +5,6 @@ import { Icon } from "./Icon";
5
5
  import { Menu } from "./Menu";
6
6
  import { MenuItem } from "./MenuItem";
7
7
  import { MenuItemCopy } from "./MenuItemCopy";
8
- import "./Breadcrumbs.css";
9
8
  const TEXT_SEPARATORS = /* @__PURE__ */ new Set(["\u2192", "/", "\u2022", "\u25B8"]);
10
9
  function isCopyItem(item) {
11
10
  return "copy" in item || "copyNode" in item || "copyUrl" in item;
@@ -40,9 +40,8 @@ export type BaseButtonProps = {
40
40
  };
41
41
  /** Content axis — slots render in this order inside the button:
42
42
  * `icon` → `label` → `children` → `iconTrailing`. Pass `icon` alone
43
- * for an icon-only button (the CSS detects this structurally via
44
- * `:has(> a-icon:only-child)` and gives the host square padding +
45
- * min-size pin). */
43
+ * for an icon-only button (the CSS detects a lone icon, allowing tooltip
44
+ * and copy helpers, and gives the host equal padding on both axes). */
46
45
  export type ContentMode = {
47
46
  /** Label text. Renders between the leading icon and `children`. */
48
47
  label?: string;
@@ -65,6 +65,7 @@ const Button = ({
65
65
  // own `aria-label` (via ...rest) wins by spread order — that's the channel
66
66
  // ButtonCopy uses to name an icon-only copy button "Copy".
67
67
  "aria-label": isIconOnly ? icon : void 0,
68
+ "data-anta-icon-only": isIconOnly ? "" : void 0,
68
69
  class: className,
69
70
  style: computedStyle,
70
71
  onClick: unavailable ? void 0 : onClick
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
2
  import { Button } from "./Button";
3
3
  import { useCopyFeedback } from "../anta_helpers";
4
- import { copyElementProps, isNodeCopy } from "./copy-props";
4
+ import { useCopyElementProps, isNodeCopy } from "./copy-props";
5
5
  const ButtonCopy = ({
6
6
  icon,
7
7
  iconPlacement = "leading",
@@ -22,7 +22,7 @@ const ButtonCopy = ({
22
22
  const { shownIcon, shownTone, handleCopied } = useCopyFeedback(restingIcon, tone, onCopied);
23
23
  const iconSlot = iconPlacement === "trailing" ? { iconTrailing: shownIcon } : iconPlacement === "none" ? {} : { icon: shownIcon };
24
24
  const hasText = label != null || children != null;
25
- const copyAttrs = copyElementProps({
25
+ const copyAttrs = useCopyElementProps({
26
26
  copy,
27
27
  copyNode,
28
28
  copyUrl,
@@ -40,6 +40,7 @@ const ButtonCopy = ({
40
40
  label,
41
41
  tone: iconPlacement === "none" ? tone : shownTone,
42
42
  "aria-label": ariaLabel ?? (hasText ? void 0 : "Copy"),
43
+ "data-anta-icon-only": !hasText && shownIcon != null ? "" : void 0,
43
44
  "data-copy-node-button": isNodeCopy({ copyNode }) ? "" : void 0,
44
45
  ...rest,
45
46
  children: [
@@ -1,8 +1,9 @@
1
1
  import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
2
  import cn from "clsx";
3
+ import { useNativeEventProps } from "../native-event-props";
3
4
  import { Temporal } from "temporal-polyfill";
4
5
  import { useMemo, useState } from "../jsx-runtime";
5
- import { nativeStateChange } from "../anta_helpers";
6
+ import { valueAttributes, nativeStateChange } from "../anta_helpers";
6
7
  import { Button } from "./Button";
7
8
  import { Menu } from "./Menu";
8
9
  import { MenuItem } from "./MenuItem";
@@ -32,7 +33,7 @@ const Calendar = ({
32
33
  const maxD = parseISODate(max);
33
34
  const today = Temporal.Now.plainDateISO();
34
35
  const [internalSelected, setInternalSelected] = useState(
35
- () => parseISODate(defaultValue)
36
+ () => parseISODate(value ?? defaultValue)
36
37
  );
37
38
  const selected = controlled ? parseISODate(value) : internalSelected;
38
39
  const [cursor, setCursor] = useState(
@@ -43,6 +44,7 @@ const Calendar = ({
43
44
  if (value !== lastSyncedValue) {
44
45
  setLastSyncedValue(value);
45
46
  const d = parseISODate(value);
47
+ if (controlled) setInternalSelected(d);
46
48
  if (d) {
47
49
  effectiveCursor = clampDate(d, minD, maxD);
48
50
  setCursor(effectiveCursor);
@@ -57,8 +59,8 @@ const Calendar = ({
57
59
  setLastFocusSignal(focusSignal);
58
60
  if (focusSignal !== void 0) setFocusReq({ iso: cursorIso, n: focusSignal });
59
61
  }
60
- const minYear = minD ? minD.year : today.year - 3;
61
- const maxYear = maxD ? maxD.year : today.year + 3;
62
+ const minYear = minD ? minD.year : Math.min(effectiveCursor.year - 3, maxD?.year ?? Infinity);
63
+ const maxYear = maxD ? maxD.year : Math.max(effectiveCursor.year + 3, minD?.year ?? -Infinity);
62
64
  const years = [];
63
65
  for (let y = minYear; y <= maxYear; y++) years.push(y);
64
66
  const monthNames = useMemo(
@@ -82,7 +84,7 @@ const Calendar = ({
82
84
  const jumpItems = useMemo(() => {
83
85
  const monthItem = (y, i) => {
84
86
  const m = i + 1;
85
- const isCurrent = y === cursor.year && m === cursor.month;
87
+ const isCurrent = y === effectiveCursor.year && m === effectiveCursor.month;
86
88
  return /* @__PURE__ */ jsx(
87
89
  MenuItem,
88
90
  {
@@ -99,11 +101,11 @@ const Calendar = ({
99
101
  `${y}-${m}`
100
102
  );
101
103
  };
102
- return years.length === 1 ? monthNames.map((_, i) => monthItem(years[0], i)) : years.map((y) => /* @__PURE__ */ jsxs(MenuItem, { submenu: true, label: String(y), selected: y === cursor.year, children: [
103
- y === cursor.year && /* @__PURE__ */ jsx("span", { className: "jump-dot", "aria-hidden": "true" }),
104
+ return years.length === 1 ? monthNames.map((_, i) => monthItem(years[0], i)) : years.map((y) => /* @__PURE__ */ jsxs(MenuItem, { submenu: true, label: String(y), selected: y === effectiveCursor.year, children: [
105
+ y === effectiveCursor.year && /* @__PURE__ */ jsx("span", { className: "jump-dot", "aria-hidden": "true" }),
104
106
  /* @__PURE__ */ jsx(Menu, { children: monthNames.map((_, i) => monthItem(y, i)) })
105
107
  ] }, y));
106
- }, [monthNames, cursor.year, cursor.month, minYear, maxYear, min, max, disabled]);
108
+ }, [monthNames, effectiveCursor.year, effectiveCursor.month, minYear, maxYear, min, max, disabled]);
107
109
  const onElementStateChange = (raw) => {
108
110
  const { event, detail } = nativeStateChange(raw);
109
111
  if (!detail) return;
@@ -129,6 +131,7 @@ const Calendar = ({
129
131
  };
130
132
  const prevDisabled = disabled || minD != null && Temporal.PlainDate.compare(cursor.with({ day: 1 }).subtract({ days: 1 }), minD) < 0;
131
133
  const nextDisabled = disabled || maxD != null && Temporal.PlainDate.compare(cursor.with({ day: 1 }).add({ months: 1 }), maxD) > 0;
134
+ const events = useNativeEventProps({ onstatechange: onElementStateChange, onchange: onElementChange, onnavigate: onNavigate });
132
135
  return /* @__PURE__ */ jsxs(
133
136
  "div",
134
137
  {
@@ -181,13 +184,10 @@ const Calendar = ({
181
184
  role: "group",
182
185
  "aria-label": ariaLabel ?? month.heading,
183
186
  name,
184
- value: controlled ? value : void 0,
185
- defaultvalue: !controlled ? defaultValue : void 0,
187
+ ...valueAttributes(value, defaultValue),
186
188
  disabled: disabled ? "" : void 0,
187
189
  "data-focus": focusReq ? `${focusReq.iso}#${focusReq.n}` : void 0,
188
- onstatechange: onElementStateChange,
189
- onchange: onElementChange,
190
- onnavigate: onNavigate,
190
+ ...events,
191
191
  children: [
192
192
  month.weekdays.map((w, i) => /* @__PURE__ */ jsx(
193
193
  "span",
@@ -53,10 +53,10 @@ export interface CardProps extends BaseProps {
53
53
  * `number` (px) or a CSS length string (`'1rem'`) for a custom radius. Omit for
54
54
  * the default `--card-radius`. */
55
55
  round?: boolean | number | string;
56
- /** Turn the whole card into a link. The card renders a focusable anchor and its
57
- * accessible name comes from `header` → body → this URL (override with
58
- * `aria-label`). A link card is display content — don't nest interactive
59
- * controls inside it. */
56
+ /** Turn the whole card into a native link. ARIA props and `tabIndex` apply to
57
+ * that anchor. A text header or body supplies its default name; custom content
58
+ * uses the native content name. Override with `aria-label` or `aria-labelledby`.
59
+ * Linked cards must not contain interactive controls. */
60
60
  href?: string;
61
61
  /** Anchor target (only with `href`). */
62
62
  target?: string;
@@ -70,18 +70,10 @@ export interface CardProps extends BaseProps {
70
70
  * region plus a `header` / body / `footer` stack. Pass `href` to turn the whole
71
71
  * card into a link.
72
72
  *
73
- * Renders an `<a-card>` web component. A stateless projection — the element owns
74
- * layout, the link, and the accessible name, and stylizes only its own surface
75
- * (never its children's typography). The wrapper maps props to attributes,
76
- * projects content into the `media` / `icon` / `header` / body / `footer`
77
- * sections, and supplies typography for the two text zones by wrapping a string
78
- * `header` in `<Title>` and a string body in `<Text>`. A leading `icon` (a shape
79
- * string, or any node) is laid out beside the title / subtitle by the element.
80
- * There is no actions slot — lay out any header controls (buttons, tags) inside
81
- * the `header` yourself.
82
- *
83
- * Requires `@antadesign/anta/elements` to be imported (client-side only) to
84
- * register the underlying custom element.
73
+ * Renders CSS-only `a-card-*` structural tags in light DOM. The wrapper owns
74
+ * structure, link attributes, and ARIA. Raw HTML authors write the same markup.
75
+ * Card needs no element registration; load its external CSS through the bundle
76
+ * or the granular `@antadesign/anta/elements/a-card` entry.
85
77
  *
86
78
  * @example Basic usage
87
79
  * ```tsx
@@ -97,4 +89,4 @@ export interface CardProps extends BaseProps {
97
89
  * </Card>
98
90
  * ```
99
91
  */
100
- export declare const Card: ({ header, subtitle, icon, footer, media, mediaPosition, tone, priority, size, selected, loading, round, href, target, rel, ping, className, style, children, ...rest }: CardProps) => any;
92
+ export declare const Card: ({ header, subtitle, icon, footer, media, mediaPosition, tone, priority, size, selected, loading, round, href, target, rel, ping, className, style, children, tabIndex, ...rest }: CardProps) => any;
@@ -1,5 +1,5 @@
1
- import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
- import { neutralToneAttr, toneStyle, roundStyle, roundAttr } from "../anta_helpers";
1
+ import { Fragment, jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
+ import { neutralToneAttr, toneStyle, roundStyle, roundAttr, hasChildren } from "../anta_helpers";
3
3
  import { Title } from "./Title";
4
4
  import { Text } from "./Text";
5
5
  import { Icon } from "./Icon";
@@ -23,16 +23,39 @@ const Card = ({
23
23
  className,
24
24
  style,
25
25
  children,
26
+ tabIndex,
26
27
  ...rest
27
28
  }) => {
28
29
  const toneAttr = neutralToneAttr(tone);
29
- const computedStyle = roundStyle(
30
- round,
31
- "--card-round",
32
- toneStyle(toneAttr, "--card-tone-source", style)
33
- );
30
+ const computedStyle = roundStyle(round, "--card-round", toneStyle(toneAttr, "--card-tone-source", style));
34
31
  const titleLevel = size === "small" ? 5 : size === "large" ? 3 : 4;
35
- return /* @__PURE__ */ jsxs(
32
+ const linked = href != null;
33
+ const aria = {};
34
+ const host = {};
35
+ for (const [name, value] of Object.entries(rest)) {
36
+ if (name.startsWith("aria-")) aria[name] = value;
37
+ else host[name] = value;
38
+ }
39
+ const textValue = (node) => typeof node === "string" || typeof node === "number" ? String(node).replace(/\s+/g, " ").trim() || void 0 : void 0;
40
+ const textName = textValue(header) ?? textValue(children);
41
+ const present = (node) => typeof node !== "boolean" && hasChildren(node);
42
+ const busy = aria["aria-busy"];
43
+ const label = aria["aria-label"];
44
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
45
+ present(media) && /* @__PURE__ */ jsx("a-card-media", { children: media }),
46
+ /* @__PURE__ */ jsxs("a-card-content", { children: [
47
+ (present(header) || present(subtitle) || present(icon)) && /* @__PURE__ */ jsxs("a-card-header", { children: [
48
+ present(icon) && /* @__PURE__ */ jsx("a-card-icon", { children: typeof icon === "string" ? /* @__PURE__ */ jsx("a-card-icon-chip", { children: /* @__PURE__ */ jsx(Icon, { shape: icon }) }) : icon }),
49
+ (present(header) || present(subtitle)) && /* @__PURE__ */ jsxs("a-card-title", { children: [
50
+ typeof header === "string" || typeof header === "number" ? /* @__PURE__ */ jsx(Title, { level: titleLevel, tone: toneAttr, font: "sans-serif", children: header }) : header,
51
+ present(subtitle) && /* @__PURE__ */ jsx(Text, { size: "small", priority: "tertiary", tone: toneAttr, children: subtitle })
52
+ ] })
53
+ ] }),
54
+ present(children) && /* @__PURE__ */ jsx("a-card-body", { children: typeof children === "string" || typeof children === "number" ? /* @__PURE__ */ jsx(Text, { priority: "secondary", tone: toneAttr, size, children }) : children }),
55
+ present(footer) && /* @__PURE__ */ jsx("a-card-footer", { children: footer })
56
+ ] })
57
+ ] });
58
+ return /* @__PURE__ */ jsx(
36
59
  "a-card",
37
60
  {
38
61
  tone: toneAttr,
@@ -42,46 +65,27 @@ const Card = ({
42
65
  selected: selected ? "" : void 0,
43
66
  loading: loading ? "" : void 0,
44
67
  round: roundAttr(round),
45
- href,
46
- target: href ? target : void 0,
47
- rel: href ? rel : void 0,
48
- ping: href ? ping : void 0,
49
68
  class: className,
50
69
  style: computedStyle,
51
- ...rest,
52
- children: [
53
- media != null && /* @__PURE__ */ jsx("div", { slot: "media", children: media }),
54
- icon != null && // Leading visual for the header — carried in a slot div (like `media` /
55
- // `header`); the element lays it out beside the title / subtitle (see the
56
- // `icon` slot in a-card.ts).
57
- (typeof icon === "string" ? (
58
- // A shape string → the icon centered in a circular chip, sized per the
59
- // card via --card-icon-size (32 / 40 / 44 for small / medium / large).
60
- /* @__PURE__ */ jsx("a-card-icon-chip", { slot: "icon", children: /* @__PURE__ */ jsx(Icon, { shape: icon }) })
61
- ) : (
62
- // Any node → used as-is (no chip).
63
- /* @__PURE__ */ jsx("div", { slot: "icon", children: icon })
64
- )),
65
- header != null && (typeof header === "string" || typeof header === "number" ? (
66
- // A string header becomes a real <Title> at the size-mapped level (see
67
- // titleLevel). The header section's padding is the header→body gap; the
68
- // element zeroes the slotted title's own margins so it doesn't double it.
69
- // Pass a node (a `<Title level={n}>`, an `<h2>`) for a different level.
70
- /* @__PURE__ */ jsx(Title, { slot: "header", level: titleLevel, tone: toneAttr, children: header })
71
- ) : /* @__PURE__ */ jsx("div", { slot: "header", children: header })),
72
- subtitle != null && // A subtitle sits in the header section too, right under the title, as
73
- // smaller text. Last header child, so its (absent) bottom margin leaves
74
- // the header→body gap to the section padding, like the title above it.
75
- /* @__PURE__ */ jsx(Text, { slot: "header", size: "small", priority: "tertiary", tone: toneAttr, children: subtitle }),
76
- typeof children === "string" || typeof children === "number" ? (
77
- // A string / number body becomes a <Text> at secondary priority (text-2,
78
- // the body color) — stated explicitly rather than leaning on the default —
79
- // following the card's tone and size. Anything else is a node the caller
80
- // owns, passed through untouched.
81
- /* @__PURE__ */ jsx(Text, { priority: "secondary", tone: toneAttr, size, children })
82
- ) : children,
83
- footer != null && /* @__PURE__ */ jsx("span", { slot: "footer", style: { display: "contents" }, children: footer })
84
- ]
70
+ tabIndex: linked ? void 0 : tabIndex,
71
+ ...host,
72
+ ...linked ? {} : aria,
73
+ "aria-busy": linked ? void 0 : busy ?? (loading ? "true" : void 0),
74
+ children: linked ? /* @__PURE__ */ jsx(
75
+ "a",
76
+ {
77
+ "data-anta-card-link": "",
78
+ href: loading ? void 0 : href,
79
+ target: loading ? void 0 : target,
80
+ rel: loading ? void 0 : rel,
81
+ ping: loading ? void 0 : ping,
82
+ tabIndex,
83
+ ...aria,
84
+ "aria-label": label ?? (loading ? void 0 : textName),
85
+ "aria-busy": busy ?? (loading ? "true" : void 0),
86
+ children: /* @__PURE__ */ jsx("a-card-layout", { children: content })
87
+ }
88
+ ) : content
85
89
  }
86
90
  );
87
91
  };
@@ -74,6 +74,7 @@ export interface DialogProps extends Omit<BaseProps, "title"> {
74
74
  *
75
75
  * Requires `@antadesign/anta/elements` to be imported (client-side only) to
76
76
  * register the underlying custom element.
77
+ * Inside another shadow root, load `@antadesign/anta/elements/a-dialog.css` there with a stylesheet `<link>` because document CSS cannot reach it.
77
78
  *
78
79
  * @example Controlled
79
80
  * ```tsx
@@ -1,5 +1,6 @@
1
1
  import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
2
  import { nativeStateChange, roundStyle, roundAttr } from "../anta_helpers";
3
+ import { useNativeEventProps } from "../native-event-props";
3
4
  import { Button } from "./Button";
4
5
  const ROUND_SIDES = /* @__PURE__ */ new Set(["top", "right", "bottom", "left"]);
5
6
  const Dialog = ({
@@ -20,6 +21,18 @@ const Dialog = ({
20
21
  }) => {
21
22
  const controlled = open !== void 0;
22
23
  const roundSide = typeof round === "string" && ROUND_SIDES.has(round) ? round : void 0;
24
+ const events = useNativeEventProps({
25
+ // React 19 keeps the event name's case after `on`; Preact lowercases it.
26
+ // Both bind `statechange` with this spelling, whereas `onStateChange` does not.
27
+ onstatechange: onStateChange ? (e) => {
28
+ const { event, detail, isOwn } = nativeStateChange(e);
29
+ if (isOwn && detail)
30
+ onStateChange(event, {
31
+ next: detail.next === "open",
32
+ prev: detail.prev === "open"
33
+ });
34
+ } : void 0
35
+ });
23
36
  return /* @__PURE__ */ jsxs(
24
37
  "a-dialog",
25
38
  {
@@ -29,14 +42,7 @@ const Dialog = ({
29
42
  persistent: persistent ? "" : void 0,
30
43
  round: roundSide ?? roundAttr(round),
31
44
  name: !controlled ? name : void 0,
32
- onstatechange: onStateChange ? (e) => {
33
- const { event, detail, isOwn } = nativeStateChange(e);
34
- if (isOwn && detail)
35
- onStateChange(event, {
36
- next: detail.next === "open",
37
- prev: detail.prev === "open"
38
- });
39
- } : void 0,
45
+ ...events,
40
46
  class: className,
41
47
  style: roundStyle(roundSide ? void 0 : round, "--dialog-round", style),
42
48
  ...rest,
@@ -49,7 +55,7 @@ const Dialog = ({
49
55
  // element (src/elements/a-dialog.ts). Duplicated, not shared — importing
50
56
  // the element module here would self-register it and break the
51
57
  // wrapper/element decoupling. Rename in both places.
52
- /* @__PURE__ */ jsx("span", { slot: "close", style: { display: "contents" }, children: /* @__PURE__ */ jsx(
58
+ /* @__PURE__ */ jsx("a-dialog-close", { slot: "close", children: /* @__PURE__ */ jsx(
53
59
  Button,
54
60
  {
55
61
  priority: "tertiary",
@@ -61,7 +67,7 @@ const Dialog = ({
61
67
  ) }),
62
68
  header != null && /* @__PURE__ */ jsx("div", { slot: "header", children: header }),
63
69
  children,
64
- footer != null && /* @__PURE__ */ jsx("span", { slot: "footer", style: { display: "contents" }, children: footer })
70
+ footer != null && /* @__PURE__ */ jsx("a-dialog-footer", { slot: "footer", children: footer })
65
71
  ]
66
72
  }
67
73
  );
@@ -46,11 +46,9 @@ export interface ExpanderProps extends Omit<BaseProps, "title"> {
46
46
  * expander keeps it open. `actions` stay live; disable them
47
47
  * separately if needed. */
48
48
  disabled?: boolean;
49
- /** Round corners sized to half the folded (header) height: a pill when folded,
50
- * and — when expanded — the same corner radius rather than a bigger stadium.
51
- * The element measures the header, so it tracks custom header content. Pass a
52
- * `number` (px) or CSS length string for a *fixed* radius that overrides the
53
- * measurement (and skips it) in both states. */
49
+ /** Use a `1rem` corner radius in both folded and expanded states, independent
50
+ * of title level or header height. Pass a number (px) or CSS length string
51
+ * for a custom radius. */
54
52
  round?: boolean | number | string;
55
53
  /** Controlled open state. When provided, the consumer owns open/close. Selecting
56
54
  * the summary requests a change through `onStateChange`; omit `open` for
@@ -68,20 +66,7 @@ export interface ExpanderProps extends Omit<BaseProps, "title"> {
68
66
  prev: boolean;
69
67
  }) => void;
70
68
  }
71
- /**
72
- * `<Expander>` — a collapsible disclosure section.
73
- *
74
- * A pure, stateless pass-through to `<a-expander>`: the element handles all
75
- * interaction (toggle, keyboard, ARIA, animation), so the wrapper holds
76
- * no state and grabs no ref — it only maps props to attributes (safe
77
- * wherever the host DOM is reconciled, incl. off the UI thread).
78
- *
79
- * Uncontrolled (`defaultOpen`): the element updates its open state. The wrapper
80
- * emits `default-state="open"` — never `state` — so the DOM carries no stale
81
- * controlled attribute. Controlled (`open` + `onStateChange`): the wrapper
82
- * emits `state="open" | "closed"`; the element treats the attribute as the
83
- * source of truth and clicks only dispatch the cancelable `statechange` event,
84
- * giving real controlled semantics (a consumer can reject a toggle). See
85
- * STATEFUL-COMPONENTS.md.
86
- */
69
+ /** Disclosure with named light-DOM regions and external styling. The element
70
+ * owns its native summary button, applied state, and collapsed accessibility.
71
+ * JSX owns region markup, attributes, and styles without DOM refs. */
87
72
  export declare const Expander: ({ title, level, tone, priority, outdent, actions, indicator, indicatorPlacement, disabled, round, open, defaultOpen, onStateChange, className, style, children, ...rest }: ExpanderProps) => any;