@antadesign/anta 0.3.19 → 0.3.21

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 (90) hide show
  1. package/README.md +1 -1
  2. package/dist/anta_helpers.d.ts +11 -2
  3. package/dist/anta_helpers.js +9 -0
  4. package/dist/components/Banner.d.ts +3 -3
  5. package/dist/components/Button.d.ts +1 -1
  6. package/dist/components/Calendar.d.ts +11 -12
  7. package/dist/components/Card.d.ts +1 -1
  8. package/dist/components/Card.js +1 -1
  9. package/dist/components/Checkbox.d.ts +8 -9
  10. package/dist/components/Dialog.d.ts +1 -1
  11. package/dist/components/Expander.d.ts +5 -6
  12. package/dist/components/Input.d.ts +2 -2
  13. package/dist/components/Input.js +1 -1
  14. package/dist/components/InputAutocomplete.d.ts +8 -8
  15. package/dist/components/InputAutocomplete.js +24 -18
  16. package/dist/components/InputTime.d.ts +3 -3
  17. package/dist/components/InputTime.js +1 -1
  18. package/dist/components/Menu.d.ts +2 -2
  19. package/dist/components/MenuItem.d.ts +1 -1
  20. package/dist/components/MenuItem.js +1 -1
  21. package/dist/components/Progress.d.ts +1 -1
  22. package/dist/components/RadioGroup.d.ts +14 -15
  23. package/dist/components/RadioGroup.js +5 -2
  24. package/dist/components/Select.d.ts +33 -40
  25. package/dist/components/Select.js +6 -2
  26. package/dist/components/SelectFaceted.d.ts +27 -33
  27. package/dist/components/SelectFaceted.js +5 -1
  28. package/dist/components/Slider.d.ts +5 -5
  29. package/dist/components/Switch.d.ts +4 -4
  30. package/dist/components/TabPanel.d.ts +1 -2
  31. package/dist/components/TabPanel.js +0 -1
  32. package/dist/components/Tabs.d.ts +9 -9
  33. package/dist/components/Tabs.js +5 -2
  34. package/dist/components/Text.d.ts +5 -3
  35. package/dist/components/Text.js +7 -3
  36. package/dist/components/Title.d.ts +1 -1
  37. package/dist/components/Toaster.d.ts +1 -1
  38. package/dist/components/Tooltip.d.ts +1 -1
  39. package/dist/elements/a-card.css +1 -1
  40. package/dist/elements/a-dialog.css +1 -1
  41. package/dist/elements/a-expander.css +1 -1
  42. package/dist/elements/a-expander.d.ts +1 -0
  43. package/dist/elements/a-expander.js +12 -2
  44. package/dist/elements/a-menu.d.ts +1 -8
  45. package/dist/elements/a-menu.js +1 -40
  46. package/dist/elements/a-progress.css +1 -1
  47. package/dist/elements/a-text.d.ts +4 -0
  48. package/dist/elements/a-text.js +10 -3
  49. package/dist/elements/a-tooltip.css +1 -1
  50. package/dist/elements/a-tooltip.d.ts +8 -1
  51. package/dist/elements/a-tooltip.js +88 -15
  52. package/dist/elements/index.d.ts +1 -1
  53. package/dist/general_types.d.ts +33 -22
  54. package/dist/index.d.ts +3 -3
  55. package/dist/theme-anta.css +1 -1
  56. package/docs/accessibility.md +4 -4
  57. package/docs/changelog.md +40 -13
  58. package/docs/comparison.md +1 -1
  59. package/docs/components/banner.md +17 -17
  60. package/docs/components/button.md +6 -6
  61. package/docs/components/card.md +5 -6
  62. package/docs/components/checkbox.md +17 -18
  63. package/docs/components/dialog.md +2 -2
  64. package/docs/components/expander.md +27 -30
  65. package/docs/components/icon.md +1 -1
  66. package/docs/components/input-autocomplete.md +15 -16
  67. package/docs/components/input-date.md +54 -59
  68. package/docs/components/input-time.md +7 -7
  69. package/docs/components/input.md +14 -14
  70. package/docs/components/menu.md +72 -8
  71. package/docs/components/progress.md +2 -2
  72. package/docs/components/radio.md +28 -25
  73. package/docs/components/select-faceted.md +95 -100
  74. package/docs/components/select.md +113 -115
  75. package/docs/components/slider.md +15 -10
  76. package/docs/components/switch.md +13 -13
  77. package/docs/components/tabs.md +23 -23
  78. package/docs/components/tag.md +5 -5
  79. package/docs/components/text.md +31 -7
  80. package/docs/components/title.md +1 -1
  81. package/docs/components/toaster.md +4 -4
  82. package/docs/components/tooltip.md +2 -2
  83. package/docs/credits.md +0 -4
  84. package/docs/index.md +1 -1
  85. package/docs/install-config.md +12 -7
  86. package/docs/normalization.md +6 -9
  87. package/docs/overview.md +1 -1
  88. package/docs/packages/stickers.md +1 -1
  89. package/docs/theming.md +15 -0
  90. package/package.json +3 -8
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @antadesign/anta
2
2
 
3
- **Anta** is [Antithesis](https://antithesis.com)'s design system. It provides
3
+ **Anta** is an opinionated design system. It provides
4
4
  design tokens, declarative web components, and typed JSX wrappers. Use the same
5
5
  components in React, Preact via compat, plain HTML, and custom JSX runtimes via
6
6
  `configure()`.
@@ -1,5 +1,14 @@
1
1
  import type { IconShape } from "./elements/a-icon.shapes";
2
+ import type { OptionPresentationProps } from "./general_types";
2
3
  export declare function hasChildren(children: React.ReactNode): boolean;
4
+ /**
5
+ * Select the safe presentation attributes from a data-rendered option. Option
6
+ * objects often carry application data, so wrappers must never spread them onto
7
+ * their rendered element. Menu rows also own `data-menu-*`: those attributes
8
+ * control closing and combobox navigation, and letting an option replace them
9
+ * would desynchronize its selection behavior.
10
+ */
11
+ export declare function optionPresentationAttrs(option: OptionPresentationProps, menu?: boolean): OptionPresentationProps;
3
12
  /**
4
13
  * Normalize a wrapper's label content the way `Button` / `Tabs` do: a bare string
5
14
  * or number becomes a `<tag>` — the ellipsis-capable label part (`a-button-label`,
@@ -65,10 +74,10 @@ export declare function parseOpenState(v: string | null): 'open' | 'closed';
65
74
  * (e.g. skipping `[disabled]` triggers) lands for every element at once. */
66
75
  export declare function installDismissTrigger(host: HTMLElement, attr: string, onDismiss: () => void): void;
67
76
  /** The six named tones every toned component shares. Anything else is a literal
68
- * CSS colour the element resolves through its `--{component}-tone-source` var. */
77
+ * CSS color the element resolves through its `--{component}-tone-source` var. */
69
78
  export declare const NAMED_TONES: Set<string>;
70
79
  /**
71
- * Inline-style helper for a custom (non-named) tone: hands the literal colour to
80
+ * Inline-style helper for a custom (non-named) tone: hands the literal color to
72
81
  * the element via `varName` (e.g. `--radio-tone-source`) so the element's CSS
73
82
  * derives the fill/text/border curve in oklch. Named tones return `base` unchanged.
74
83
  */
@@ -2,6 +2,14 @@ import { jsx, useState, useMemo } from "./jsx-runtime";
2
2
  function hasChildren(children) {
3
3
  return Array.isArray(children) ? children.length > 0 : children != null;
4
4
  }
5
+ function optionPresentationAttrs(option, menu = false) {
6
+ const attrs = {};
7
+ for (const [name, value] of Object.entries(option)) {
8
+ if (name === "className" || name === "style" || name.startsWith("data-") && (!menu || !name.startsWith("data-menu-") && name !== "data-anta-menu-item"))
9
+ attrs[name] = value;
10
+ }
11
+ return attrs;
12
+ }
5
13
  function wrapLabel(kids, tag) {
6
14
  if (kids == null) return kids;
7
15
  const arr = Array.isArray(kids) ? kids : [kids];
@@ -158,6 +166,7 @@ export {
158
166
  isMenuOpen,
159
167
  lengthStyle,
160
168
  nativeStateChange,
169
+ optionPresentationAttrs,
161
170
  parseOpenState,
162
171
  roundAttr,
163
172
  roundStyle,
@@ -12,7 +12,7 @@ export interface BannerProps extends BaseProps {
12
12
  children?: React.ReactNode;
13
13
  /** Semantic tone, or any literal CSS color (`'#ff1493'`, `'rebeccapurple'`) for
14
14
  * a one-off custom tone. Named tones re-point the surface, text, and border
15
- * colour (used if you opt into a border); a custom color keeps its hue while
15
+ * color (used if you opt into a border); a custom color keeps its hue while
16
16
  * lightness/chroma are pinned. `'neutral'` (the default) is the same as omitting it.
17
17
  * @defaultValue 'neutral' */
18
18
  tone?: "neutral" | "brand" | "info" | "success" | "warning" | "critical" | (string & {});
@@ -22,7 +22,7 @@ export interface BannerProps extends BaseProps {
22
22
  round?: boolean | number | string;
23
23
  /** Whether the trailing ✕ dismiss button is present. `false` removes it — drive
24
24
  * dismissal yourself (a controlled `dismissed`, or your own control) instead.
25
- * It doesn't make the banner un-dismissible, it just drops the built-in ✕.
25
+ * It removes the built-in ✕ without making the banner un-dismissible.
26
26
  * @defaultValue true */
27
27
  closable?: boolean;
28
28
  /** ARIA role for the strip host. `'status'` (a polite live region, so the notice
@@ -39,7 +39,7 @@ export interface BannerProps extends BaseProps {
39
39
  /** Initial dismissed state for the uncontrolled case (read once on mount). */
40
40
  defaultDismissed?: boolean;
41
41
  /** Fired when the user dismisses the banner (clicks ✕). Uncontrolled, the banner
42
- * hides itself and this just notifies. Controlled, it's how you accept the
42
+ * hides itself and this notifies. Controlled, use it to accept the
43
43
  * request — set `dismissed` to `true` (or ignore to reject); pair it with
44
44
  * `dismissed`, or the banner can't be closed. */
45
45
  onDismiss?: () => void;
@@ -58,7 +58,7 @@ export type ContentMode = {
58
58
  * don't carry anchor props. (Copy is no longer a Button concern — `ButtonCopy`
59
59
  * composes a `<a-copy>` child; see `copy-props.ts`.)
60
60
  *
61
- * `href` is **optional** so a conditional URL just works: `href={maybeUrl}`
61
+ * `href` is **optional**, so you can pass a conditional URL: `href={maybeUrl}`
62
62
  * (`string | undefined`) type-checks, and a falsy href degrades to a plain
63
63
  * button — a definite string renders `<a role="button">`, an absent / `undefined`
64
64
  * href renders `<a-button>`, mirroring the `href != null` runtime check. Keeping
@@ -14,8 +14,8 @@ export interface CalendarChangeAttrs {
14
14
  }
15
15
  /** Public props for `<Calendar>` — the single-date month grid. */
16
16
  export interface CalendarProps extends Omit<BaseProps, "children" | "onChange"> {
17
- /** Controlled selected date — ISO `YYYY-MM-DD`. When provided, the consumer owns
18
- * selection: the grid follows this prop and a pick only *requests* a change via
17
+ /** Controlled selected date — ISO `YYYY-MM-DD`. When provided, the application
18
+ * controls selection: the grid follows this prop and a pick only *requests* a change via
19
19
  * `onStateChange`. Leave undefined for uncontrolled. */
20
20
  value?: string;
21
21
  /** Initial selected date for the uncontrolled case (ISO `YYYY-MM-DD`). */
@@ -53,23 +53,22 @@ export interface CalendarProps extends Omit<BaseProps, "children" | "onChange">
53
53
  /** Fired *after* the selection changes (post-apply). Not cancelable; for a
54
54
  * controlled calendar it fires once you've updated `value`. */
55
55
  onChange?: (event: Event) => void;
56
- /** Like `onChange`, but with a `{ value, name }` snapshot — the ergonomic
57
- * "just give me the new date" callback (mirrors `Input`). */
56
+ /** Like `onChange`, but with a `{ value, name }` snapshot as the second argument,
57
+ * matching `Input`. */
58
58
  onValueChange?: (event: Event, attrs: CalendarChangeAttrs) => void;
59
59
  }
60
60
  /**
61
- * `<Calendar>` — a single-date month grid you pick a day from, **composed from
62
- * Anta components in light DOM** (nothing is hidden in a shadow root): days and
63
- * the prev/next chevrons are `<Button>`s, so they inherit the design system's
64
- * states for free — a selected day is literally a `tertiary` Button in its
65
- * `selected` state, toned `brand` so the active date reads in the brand color.
61
+ * `<Calendar>` is a single-date month grid. It uses Anta components in light DOM.
62
+ * Days and the previous/next chevrons are `<Button>`s, so they use the design
63
+ * system's button states. A selected day is a `tertiary` Button in its `selected`
64
+ * state, using the `brand` tone so the active date uses the brand color.
66
65
  * All date math runs on the Temporal engine
67
66
  * (`@antadesign/anta` exports `buildMonth` & friends), and the value is an ISO
68
67
  * `YYYY-MM-DD` string.
69
68
  *
70
- * The grid lives in a form-associated `<a-calendar>` element (which owns the
71
- * submitted value + reset/restore); the wrapper owns view-month, selection,
72
- * keyboard, and roving `tabindex` — the `RadioGroup` model. The month switcher
69
+ * The grid lives in a form-associated `<a-calendar>` element, which manages the
70
+ * submitted value, reset, and restore. The wrapper manages view month, selection,
71
+ * keyboard, and roving `tabindex`. The month switcher
73
72
  * is a sibling rendered *outside* the grid, so it can be extended (month/year
74
73
  * pickers) independently.
75
74
  *
@@ -93,7 +93,7 @@ export interface CardProps extends BaseProps {
93
93
  * @example Link card
94
94
  * ```tsx
95
95
  * <Card href="/docs/ci" header="CI integration guide">
96
- * Wire Antithesis into your pipeline in a few minutes.
96
+ * Connect your CI pipeline in a few minutes.
97
97
  * </Card>
98
98
  * ```
99
99
  */
@@ -75,7 +75,7 @@ const Card = ({
75
75
  /* @__PURE__ */ jsx(Text, { slot: "header", size: "small", priority: "tertiary", tone: toneAttr, children: subtitle }),
76
76
  typeof children === "string" || typeof children === "number" ? (
77
77
  // A string / number body becomes a <Text> at secondary priority (text-2,
78
- // the body colour) — stated explicitly rather than leaning on the default —
78
+ // the body color) — stated explicitly rather than leaning on the default —
79
79
  // following the card's tone and size. Anything else is a node the caller
80
80
  // owns, passed through untouched.
81
81
  /* @__PURE__ */ jsx(Text, { priority: "secondary", tone: toneAttr, size, children })
@@ -37,7 +37,7 @@ export interface CheckboxProps extends BaseProps {
37
37
  * visual precedence; clicking it requests `true`. */
38
38
  checked?: CheckboxValue;
39
39
  /** Initial checked state for an uncontrolled checkbox. Read once; later
40
- * changes ignored (the element then owns the state).
40
+ * changes are ignored and the element updates its state after interaction.
41
41
  * @defaultValue false */
42
42
  defaultChecked?: CheckboxValue;
43
43
  /** Disable the checkbox (no interaction, dropped from the tab order). */
@@ -51,16 +51,16 @@ export interface CheckboxProps extends BaseProps {
51
51
  /** Value submitted with the form when checked — like a native checkbox.
52
52
  * @defaultValue "on" */
53
53
  value?: string;
54
- /** Colour of the **mark** in every state — the checked-box fill *and* the
54
+ /** Color of the **mark** in every state — the checked-box fill *and* the
55
55
  * unselected box border. A named tone or any literal CSS color (`'#ff1493'`,
56
56
  * `'rebeccapurple'`) for a one-off custom tone. Named tones track light/dark mode
57
- * automatically; a custom colour keeps its hue + chroma and pins lightness to the
57
+ * automatically; a custom color keeps its hue + chroma and pins lightness to the
58
58
  * fill curve. Use `toneSelected` instead to tone only the checked mark and leave
59
- * the empty box neutral. The label + hint stay neutral — recolour them in plain
59
+ * the empty box neutral. The label + hint stay neutral — recolor them in plain
60
60
  * CSS via the theme-aware `--text-N-{tone}` tokens.
61
61
  * @defaultValue 'neutral' */
62
62
  tone?: 'brand' | 'neutral' | 'info' | 'success' | 'warning' | 'critical' | (string & {});
63
- /** Like `tone`, but coloured onto the **checked mark only** — the empty box stays
63
+ /** Like `tone`, but colored onto the **checked mark only** — the empty box stays
64
64
  * neutral grey until it's checked. Prefer this over `tone` when a resting tinted
65
65
  * border would read as a validation state. Same value set as `tone`; if both are
66
66
  * set, `tone` governs the off-state border and `toneSelected` the checked fill.
@@ -82,13 +82,12 @@ export interface CheckboxProps extends BaseProps {
82
82
  * checkbox this fires once you've updated `checked`. */
83
83
  onChange?: (event: Event) => void;
84
84
  /** Like `onChange`, but with a `{ checked, indeterminate, name, value }` snapshot
85
- * as the 2nd argument — the ergonomic "just give me the new value" callback
86
- * (mirrors `Input`'s `onValueChange`). */
85
+ * as the second argument, matching `Input`'s `onValueChange`. */
87
86
  onValueChange?: (event: Event, attrs: CheckboxChangeAttrs) => void;
88
87
  }
89
88
  /**
90
- * Checkbox. Renders an `<a-checkbox>` web component that owns the visual
91
- * state; controlled via `checked` + `onStateChange`, or uncontrolled via
89
+ * Checkbox. Renders an `<a-checkbox>` web component that provides visual
90
+ * state; use `checked` with `onStateChange` for controlled use, or
92
91
  * `defaultChecked`. Label is `label` (or `children`); `hint` adds secondary
93
92
  * text under the label. Requires `@antadesign/anta/elements`.
94
93
  *
@@ -18,7 +18,7 @@ export interface DialogProps extends Omit<BaseProps, "title"> {
18
18
  position?: "center" | "left" | "right" | "top" | "bottom" | "fullscreen";
19
19
  /** Whether the top-right ✕ button is present. It's one way to close the dialog,
20
20
  * alongside Esc, the backdrop, a `data-dialog-close` / footer action, and your
21
- * own code; `false` just removes the ✕, it doesn't make the dialog un-closable.
21
+ * own code; `false` removes the ✕ without making the dialog unclosable.
22
22
  * @defaultValue true */
23
23
  closable?: boolean;
24
24
  /** Turn off light-dismiss: a backdrop click and Esc no longer close the dialog.
@@ -44,10 +44,9 @@ export interface ExpanderProps extends Omit<BaseProps, "title"> {
44
44
  * `number` (px) or CSS length string for a *fixed* radius that overrides the
45
45
  * measurement (and skips it) in both states. */
46
46
  round?: boolean | number | string;
47
- /** Controlled open state. When provided, the consumer owns open/close:
48
- * the expander only follows this prop, and clicking the summary just
49
- * requests a change via `onStateChange` (so a toggle can be rejected by
50
- * not updating). Leave undefined for uncontrolled. */
47
+ /** Controlled open state. When provided, the consumer owns open/close. Selecting
48
+ * the summary requests a change through `onStateChange`; omit `open` for
49
+ * uncontrolled use. */
51
50
  open?: boolean;
52
51
  /** Initial open state for the uncontrolled case. */
53
52
  defaultOpen?: boolean;
@@ -64,12 +63,12 @@ export interface ExpanderProps extends Omit<BaseProps, "title"> {
64
63
  /**
65
64
  * `<Expander>` — a collapsible disclosure section.
66
65
  *
67
- * A pure, stateless pass-through to `<a-expander>`: the element owns all
66
+ * A pure, stateless pass-through to `<a-expander>`: the element handles all
68
67
  * interaction (toggle, keyboard, ARIA, animation), so the wrapper holds
69
68
  * no state and grabs no ref — it only maps props to attributes (safe
70
69
  * wherever the host DOM is reconciled, incl. off the UI thread).
71
70
  *
72
- * Uncontrolled (`defaultOpen`): the element owns its open state. The wrapper
71
+ * Uncontrolled (`defaultOpen`): the element updates its open state. The wrapper
73
72
  * emits `default-state="open"` — never `state` — so the DOM carries no stale
74
73
  * controlled attribute. Controlled (`open` + `onStateChange`): the wrapper
75
74
  * emits `state="open" | "closed"`; the element treats the attribute as the
@@ -19,7 +19,7 @@ export interface InputChangeAttrs {
19
19
  export interface InputProps extends BaseProps, DOMEventHandlers {
20
20
  /** Extra content rendered directly under the field, above the hint/error (it
21
21
  * pushes the message down). A no-box child like an Anta `<Tooltip>` takes no
22
- * space and just anchors to the field — consistent with how tooltips attach
22
+ * space and anchors to the field — consistent with how tooltips attach
23
23
  * to any other element. Use the named `leading` / `trailing` props for
24
24
  * in-field content. */
25
25
  children?: React.ReactNode;
@@ -41,7 +41,7 @@ export interface InputProps extends BaseProps, DOMEventHandlers {
41
41
  * success → `circle-check`, info → `info`, brand → `circle-small-solid`); pass a
42
42
  * shape to override, or `false` to drop it. `neutral` has no default glyph. */
43
43
  statusIcon?: IconShape | (string & {}) | false;
44
- /** Custom accent colour — any literal CSS colour tints the resting + hover
44
+ /** Custom accent color — any literal CSS color tints the resting + hover
45
45
  * border (focus ring stays the global `--focus-ring`). For consistency with the
46
46
  * other controls' custom-tone knob; a `status` still overrides for validation. */
47
47
  tone?: string;
@@ -126,7 +126,7 @@ const Input = ({
126
126
  label != null && (isStringish(label) ? /* @__PURE__ */ jsx("span", { slot: "label", children: label }) : /* @__PURE__ */ jsx("span", { slot: "label", style: { display: "contents" }, children: label })),
127
127
  resolvedLeading != null && /* @__PURE__ */ jsx("span", { slot: "leading", style: { display: "contents" }, children: resolvedLeading }),
128
128
  resolvedClearable && // A real <a-button> (light DOM → fully styled, keyboard-focusable) in
129
- // the element's `clear` slot — the element owns its visibility (shown
129
+ // the element's `clear` slot — the element controls its visibility (shown
130
130
  // only when filled + editable). It fires the bubbling `clearrequest`
131
131
  // event via a-button's global listener, so clearing works even without
132
132
  // framework hydration; the element turns that into clearclick→clear().
@@ -8,7 +8,7 @@ export interface InputAutocompleteProps extends Omit<BaseProps, 'children'> {
8
8
  /** Controlled value: the field's text, a **free** string not constrained to a
9
9
  * suggestion. Leave undefined for uncontrolled. */
10
10
  value?: string;
11
- /** Initial value for the uncontrolled case (the wrapper then owns it). */
11
+ /** Initial value for uncontrolled use. */
12
12
  defaultValue?: string;
13
13
  /** Fires as the text changes (typing, picking a suggestion, or clearing) with
14
14
  * the new field text. */
@@ -17,8 +17,8 @@ export interface InputAutocompleteProps extends Omit<BaseProps, 'children'> {
17
17
  onSelect?: (option: SelectOption) => void;
18
18
  /** How suggestions match the text. `true` uses the built-in case-insensitive
19
19
  * substring match on `value` / `label` / `hint`; a function `(option, query) =>
20
- * boolean` does custom matching; `false` shows `suggestions` verbatim so you
21
- * can filter them yourself (async / remote).
20
+ * boolean` uses a custom matcher; `false` shows `suggestions` unchanged for
21
+ * application-provided async or remote filtering.
22
22
  * @defaultValue true */
23
23
  filter?: boolean | ((option: SelectOption, query: string) => boolean);
24
24
  /** Placeholder text. */
@@ -30,7 +30,7 @@ export interface InputAutocompleteProps extends Omit<BaseProps, 'children'> {
30
30
  status?: 'neutral' | 'brand' | 'info' | 'success' | 'warning' | 'critical';
31
31
  /** Status icon override, or `false` to drop it. */
32
32
  statusIcon?: IconShape | (string & {}) | false;
33
- /** Custom accent colour — a named tone or any CSS colour. */
33
+ /** Custom accent color — a named tone or any CSS color. */
34
34
  tone?: string;
35
35
  /** Show a clear button while the field has text. */
36
36
  clearable?: boolean;
@@ -48,10 +48,10 @@ export interface InputAutocompleteProps extends Omit<BaseProps, 'children'> {
48
48
  trailing?: React.ReactNode;
49
49
  }
50
50
  /**
51
- * `<InputAutocomplete>` — a text field whose value is a free string, with a
52
- * suggestion dropdown that assists without constraining. Typing something not in
53
- * the list is valid and stays the value; picking a suggestion just fills the
54
- * field. For a value that MUST be one of the options, use `Select` with `filter`.
51
+ * `<InputAutocomplete>` is a text field with a suggestion dropdown. Its value is
52
+ * a free-form string. Text not in the list remains valid, and choosing a
53
+ * suggestion fills the field. Use `Select` with `filter` when the value must be
54
+ * one of the options.
55
55
  *
56
56
  * Controlled (`value` + `onValueChange`) or uncontrolled (`defaultValue`).
57
57
  * Requires `@antadesign/anta/elements` (client-side only).
@@ -1,6 +1,6 @@
1
1
  import { Fragment, jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
2
  import { useState, useMemo, useId } from "../jsx-runtime";
3
- import { nativeStateChange } from "../anta_helpers";
3
+ import { nativeStateChange, optionPresentationAttrs } from "../anta_helpers";
4
4
  import { normalizeOpt, matchQueryRegex, matchesQuery, highlight } from "./select-options";
5
5
  import { Input } from "./Input";
6
6
  import { Menu } from "./Menu";
@@ -69,23 +69,29 @@ const InputAutocomplete = (props) => {
69
69
  if (!next) setActiveId(null);
70
70
  },
71
71
  onactivedescendant: (e) => setActiveId(nativeStateChange(e).detail?.id ?? null),
72
- children: visible.map((opt, i) => /* @__PURE__ */ jsx(
73
- MenuItem,
74
- {
75
- id: `${rid}-o${i}`,
76
- role: "option",
77
- "aria-selected": activeId === `${rid}-o${i}` ? "true" : "false",
78
- icon: opt.icon,
79
- label: highlight(opt.label ?? opt.value, queryRe),
80
- hint: opt.hint ? highlight(opt.hint, queryRe) : opt.hint,
81
- tone: opt.tone,
82
- disabled: opt.disabled,
83
- value: opt.value,
84
- onMouseDown: (e) => e.preventDefault(),
85
- onSelect: () => pick(opt)
86
- },
87
- `${rid}-${opt.value}-${i}`
88
- ))
72
+ children: visible.map((opt, i) => {
73
+ const { className: optionClassName, style: optionStyle, ...optionAttrs } = optionPresentationAttrs(opt, true);
74
+ return /* @__PURE__ */ jsx(
75
+ MenuItem,
76
+ {
77
+ ...optionAttrs,
78
+ id: `${rid}-o${i}`,
79
+ role: "option",
80
+ "aria-selected": activeId === `${rid}-o${i}` ? "true" : "false",
81
+ icon: opt.icon,
82
+ label: highlight(opt.label ?? opt.value, queryRe),
83
+ hint: opt.hint ? highlight(opt.hint, queryRe) : opt.hint,
84
+ tone: opt.tone,
85
+ disabled: opt.disabled,
86
+ value: opt.value,
87
+ onMouseDown: (e) => e.preventDefault(),
88
+ onSelect: () => pick(opt),
89
+ className: optionClassName,
90
+ style: optionStyle
91
+ },
92
+ `${rid}-${opt.value}-${i}`
93
+ );
94
+ })
89
95
  }
90
96
  )
91
97
  ] });
@@ -16,7 +16,7 @@ export interface InputTimeChangeAttrs {
16
16
  }
17
17
  export interface InputTimeProps extends BaseProps, DOMEventHandlers {
18
18
  /** Extra content rendered under the field, above the hint (a no-box child like
19
- * a `<Tooltip>` takes no space and just anchors to the field). */
19
+ * a `<Tooltip>` takes no space and anchors to the field). */
20
20
  children?: React.ReactNode;
21
21
  /** Field label, shown above the control and used as the segment group's
22
22
  * accessible name. */
@@ -29,7 +29,7 @@ export interface InputTimeProps extends BaseProps, DOMEventHandlers {
29
29
  /** Glyph before the `hint` when `status` is set (per-status default; pass a
30
30
  * shape to override, or `false` to drop it). */
31
31
  statusIcon?: IconShape | (string & {}) | false;
32
- /** Custom accent colour — any literal CSS colour tints the resting + hover
32
+ /** Custom accent color — any literal CSS color tints the resting + hover
33
33
  * border (focus ring stays `--focus-ring`); `status` overrides for validation. */
34
34
  tone?: string;
35
35
  /** Size variant. small=24px, medium=28px, large=32px tall.
@@ -87,7 +87,7 @@ export interface InputTimeProps extends BaseProps, DOMEventHandlers {
87
87
  * behave as one control: each is a native text input — ↑/↓ steps with wrap,
88
88
  * ←/→ crosses segments at caret boundaries, typing digits auto-advances, and
89
89
  * AM/PM accepts its locale text or `a`/`p`. Renders an `<a-input-time>` web
90
- * component that owns the segments, navigation, and form value (via
90
+ * component that manages the segments, navigation, and form value (via
91
91
  * `ElementInternals`).
92
92
  *
93
93
  * The clock and layout follow the locale (`en-US` → 12-hour, most others 24),
@@ -79,7 +79,7 @@ const InputTime = ({
79
79
  children: [
80
80
  label != null && (isStringish(label) ? /* @__PURE__ */ jsx("span", { slot: "label", children: label }) : /* @__PURE__ */ jsx("span", { slot: "label", style: { display: "contents" }, children: label })),
81
81
  icon !== false && /* @__PURE__ */ jsx("span", { slot: "leading", style: { display: "contents" }, children: /* @__PURE__ */ jsx(Icon, { shape: icon ?? "clock" }) }),
82
- clearable && // Real <a-button> in the element's `clear` slot; the element owns its
82
+ clearable && // Real <a-button> in the element's `clear` slot; the element controls its
83
83
  // visibility (shown only when filled). It fires `clearrequest`, which the
84
84
  // element turns into clear(). CONTRACT: `data-custom-event` MUST match
85
85
  // CLEAR_TRIGGER in src/elements/a-input-time.ts (string duplicated, not
@@ -57,7 +57,7 @@ export interface MenuProps extends BaseProps {
57
57
  /** Combobox-mode cursor report (raw element event). Fires when the active
58
58
  * option changes as arrow keys move the cursor while focus stays in a
59
59
  * `[data-menu-search]` filter field; `detail.id` is the active option's `id`
60
- * (`null` when none). The reactive layer that owns the field reflects this
60
+ * (`null` when none). The reactive layer that renders the field reflects this
61
61
  * onto its `aria-activedescendant` — the element can't write that light-DOM
62
62
  * attribute itself. Passed straight through to the element. */
63
63
  onactivedescendant?: (e: CustomEvent<{
@@ -78,7 +78,7 @@ export interface MenuProps extends BaseProps {
78
78
  *
79
79
  * Open state is uncontrolled by default — listen for `onStateChange`
80
80
  * (`detail: { next, prev }`) to observe it, or pass `open` to control it. You
81
- * can also grab a `ref` and call `.open()` / `.close()` / `.toggle()`.
81
+ * can also keep a `ref` and call `.open()` / `.close()` / `.toggle()`.
82
82
  * Selecting a `MenuItem` closes the menu;
83
83
  * arbitrary injected content does not. Add `data-menu-open` to any item /
84
84
  * container to keep it open, or `data-menu-close` to a custom element to let it
@@ -111,7 +111,7 @@ export type MenuItemActionMode = {
111
111
  * `selectionIndicator` to keep the semantics — the row stays the control and
112
112
  * carries `role` + `aria-checked`; only the drawn mark changes. Suppresses the
113
113
  * built-in checkbox / radio and the trailing `check` glyph. The node is made
114
- * passive (aria-hidden, no pointer events) so the row owns the click. */
114
+ * passive (aria-hidden, no pointer events) so the row receives the click. */
115
115
  indicator?: React.ReactNode;
116
116
  };
117
117
  export type MenuItemProps = MenuItemCommonProps & (MenuItemLinkMode | MenuItemActionMode);
@@ -81,7 +81,7 @@ const MenuItem = ({
81
81
  ...rest,
82
82
  children: [
83
83
  indicator != null ? (
84
- // `min-height: 1lh` makes the wrapper one text line tall and centres the node
84
+ // `min-height: 1lh` makes the wrapper one text line tall and centers the node
85
85
  // in it, so a small indicator aligns with the label's first line in a hinted
86
86
  // (top-aligned) row instead of riding at the top.
87
87
  /* @__PURE__ */ jsx(
@@ -6,7 +6,7 @@ export interface ProgressProps extends BaseProps {
6
6
  /** Upper bound of the range.
7
7
  * @defaultValue 100 */
8
8
  max?: number;
9
- /** Colour variant, or any literal CSS colour for a one-off custom tone (the
9
+ /** Color variant, or any literal CSS color for a one-off custom tone (the
10
10
  * surface / indicator / text are derived from it in oklch). Named tones track
11
11
  * light/dark automatically.
12
12
  * @defaultValue 'neutral' */
@@ -1,4 +1,4 @@
1
- import type { BaseProps } from "../general_types";
1
+ import type { BaseProps, OptionPresentationProps } from "../general_types";
2
2
  /** The element's `statechange` payload. `next`/`prev` are values (`null` = nothing
3
3
  * selected); `reason` distinguishes a user pick from a form reset / bfcache restore. */
4
4
  type StateReason = "user" | "reset" | "restore";
@@ -15,7 +15,7 @@ export interface RadioChangeAttrs {
15
15
  name?: string;
16
16
  }
17
17
  /** One option in a `<RadioGroup>`. The wrapper renders an `<a-radio>` per entry. */
18
- export interface RadioOption {
18
+ export interface RadioOption extends OptionPresentationProps {
19
19
  /** This option's identity and the value submitted when it's selected.
20
20
  * Must be unique within the group (a dev-only warning fires on duplicates). */
21
21
  value: string;
@@ -37,12 +37,11 @@ export interface RadioGroupProps extends Omit<BaseProps, "children" | "onChange"
37
37
  /** The options. The wrapper renders one `<a-radio>` per entry and computes its
38
38
  * `selected` / roving `tabindex` / `role` declaratively. */
39
39
  options: RadioOption[];
40
- /** Controlled selected value. When provided, the consumer owns selection: the
41
- * group follows this prop and a pick only *requests* a change via
42
- * `onStateChange` (reject by not updating). Leave undefined for uncontrolled. */
40
+ /** Controlled selected value. The group follows this prop and reports a
41
+ * requested change through `onStateChange`. Leave it undefined for
42
+ * uncontrolled use. */
43
43
  value?: string;
44
- /** Initial selected value for the uncontrolled case (the wrapper then owns the
45
- * selection in local state). */
44
+ /** Initial selected value for uncontrolled use. */
46
45
  defaultValue?: string;
47
46
  /** Fired whenever selection changes — event-first. `detail` is
48
47
  * `{ next, prev, reason }`: `next`/`prev` are values (`null` = nothing selected);
@@ -56,8 +55,8 @@ export interface RadioGroupProps extends Omit<BaseProps, "children" | "onChange"
56
55
  * counterpart to `onStateChange`). Not cancelable; for a controlled group it
57
56
  * fires once you've updated `value`. */
58
57
  onChange?: (event: Event) => void;
59
- /** Like `onChange`, but with a `{ value, name }` snapshot as the 2nd argument —
60
- * the ergonomic "just give me the new value" callback (mirrors `Input`). */
58
+ /** Like `onChange`, but with a `{ value, name }` snapshot as the second argument,
59
+ * matching `Input`. */
61
60
  onValueChange?: (event: Event, attrs: RadioChangeAttrs) => void;
62
61
  /** Fired when focus enters the group (any option) — wired to `focusin`, since
63
62
  * focus lands on an individual option, not the group element itself. */
@@ -73,19 +72,19 @@ export interface RadioGroupProps extends Omit<BaseProps, "children" | "onChange"
73
72
  * the options) — typically instructional copy. Per-option helper text goes on
74
73
  * the option's own `hint` instead. */
75
74
  hint?: string;
76
- /** Validation/feedback tone for the group `hint` — recolours it (same tone set
75
+ /** Validation/feedback tone for the group `hint` — recolors it (same tone set
77
76
  * as `Input`'s `status`). Use `critical` for an error message, etc.; omit for
78
77
  * the neutral default.
79
78
  * @defaultValue 'neutral' */
80
79
  status?: "neutral" | "brand" | "info" | "success" | "warning" | "critical";
81
80
  /** Mark tone applied to every option (an option's own `tone` wins), or any literal
82
- * CSS color for a one-off custom tone. Colours the selected-ring fill + dot *and*
81
+ * CSS color for a one-off custom tone. Colors the selected-ring fill + dot *and*
83
82
  * the unselected ring border. Named tones track light/dark mode. Use `toneSelected`
84
83
  * instead to tone only the selected option and leave the rest neutral. The option
85
- * text stays neutral — recolour it in plain CSS via the `--text-N-{tone}` tokens.
84
+ * text stays neutral — recolor it in plain CSS via the `--text-N-{tone}` tokens.
86
85
  * @defaultValue 'neutral' */
87
86
  tone?: "brand" | "neutral" | "info" | "success" | "warning" | "critical" | (string & {});
88
- /** Like `tone`, but coloured onto the **selected option only** — every unselected
87
+ /** Like `tone`, but colored onto the **selected option only** — every unselected
89
88
  * ring stays neutral grey. Applied to every option (an option's own `toneSelected`
90
89
  * wins). Prefer this over `tone` when a resting tinted border would read as a
91
90
  * validation state.
@@ -104,8 +103,8 @@ export interface RadioGroupProps extends Omit<BaseProps, "children" | "onChange"
104
103
  * `<RadioGroup>` — a single-select radio control, rendered from `options`.
105
104
  *
106
105
  * This wrapper is convenience over the web components: it renders an `<a-radio>`
107
- * per option and, crucially, owns the two **declarative** DOM concerns the
108
- * elements deliberately don't touch — the roving **`tabindex`** (so keyboard Tab
106
+ * per option and manages the two **declarative** DOM concerns the elements
107
+ * deliberately do not touch: the roving **`tabindex`** (so keyboard Tab
109
108
  * lands on the right option) and each radio's **`role`**. Selection itself lives
110
109
  * in `<a-radio-group>` off-DOM (it sets each radio's `selected` property), so the
111
110
  * elements never mutate the DOM; this wrapper just reflects the current value into
@@ -1,6 +1,6 @@
1
1
  import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
2
  import { useId, useState } from "../jsx-runtime";
3
- import { nativeStateChange, toneStyle } from "../anta_helpers";
3
+ import { nativeStateChange, optionPresentationAttrs, toneStyle } from "../anta_helpers";
4
4
  const radioAttrsOf = (el) => ({
5
5
  value: el?.value ?? null,
6
6
  name: el?.getAttribute?.("name") ?? void 0
@@ -79,9 +79,11 @@ const RadioGroup = ({
79
79
  hint && /* @__PURE__ */ jsx("a-radio-group-hint", { id: hintId, children: hint }),
80
80
  /* @__PURE__ */ jsx("a-radio-list", { children: options.map((o) => {
81
81
  const optDisabled = disabled || o.disabled;
82
+ const { className: optionClassName, style: optionStyle, ...optionAttrs } = optionPresentationAttrs(o);
82
83
  return /* @__PURE__ */ jsxs(
83
84
  "a-radio",
84
85
  {
86
+ ...optionAttrs,
85
87
  role: "radio",
86
88
  value: o.value,
87
89
  "aria-disabled": optDisabled ? "true" : void 0,
@@ -90,7 +92,8 @@ const RadioGroup = ({
90
92
  "tone-selected": o.toneSelected && o.toneSelected !== "neutral" ? o.toneSelected : void 0,
91
93
  size: o.size && o.size !== "medium" ? o.size : void 0,
92
94
  disabled: o.disabled ? "" : void 0,
93
- style: toneStyle(o.toneSelected, "--radio-tone-source", toneStyle(o.tone, "--radio-tone-source")),
95
+ class: optionClassName,
96
+ style: toneStyle(o.toneSelected, "--radio-tone-source", toneStyle(o.tone, "--radio-tone-source", optionStyle)),
94
97
  children: [
95
98
  /* @__PURE__ */ jsx("a-radio-label", { children: o.label }),
96
99
  o.hint != null && /* @__PURE__ */ jsx("a-radio-hint", { children: o.hint })