@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,5 +1,6 @@
1
- import { Fragment, jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
- import { nativeStateChange, neutralToneAttr, toneStyle, roundStyle, roundAttr } from "../anta_helpers";
1
+ import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
+ import { hasChildren, nativeStateChange, neutralToneAttr, toneStyle, roundStyle, roundAttr } from "../anta_helpers";
3
+ import { useNativeEventProps } from "../native-event-props";
3
4
  const Expander = ({
4
5
  title,
5
6
  level,
@@ -20,44 +21,44 @@ const Expander = ({
20
21
  ...rest
21
22
  }) => {
22
23
  const controlled = open !== void 0;
23
- const presetIndicator = typeof indicator === "string" && (indicator === "chevron" || indicator === "triangle" || indicator === "plus") ? indicator : void 0;
24
+ const eventProps = useNativeEventProps({
25
+ onstatechange: onStateChange ? (e) => {
26
+ const { event, detail, isOwn } = nativeStateChange(e);
27
+ if (isOwn && detail)
28
+ onStateChange(event, {
29
+ next: detail.next === "open",
30
+ prev: detail.prev === "open"
31
+ });
32
+ } : void 0
33
+ });
34
+ const hasActions = hasChildren(actions) && typeof actions !== "boolean" && (typeof actions !== "string" || Boolean(actions.trim()));
35
+ const presetIndicator = indicator == null ? "chevron" : typeof indicator === "string" && (indicator === "chevron" || indicator === "triangle" || indicator === "plus") ? indicator : void 0;
24
36
  const pairedIndicator = indicator != null && typeof indicator === "object" && "closed" in indicator && "open" in indicator ? indicator : void 0;
25
- const useCustomSlot = indicator != null && !presetIndicator && !pairedIndicator;
37
+ const useCustomIndicator = indicator != null && !presetIndicator && !pairedIndicator;
26
38
  const customIndicator = typeof indicator === "object" ? indicator : null;
39
+ const indicatorNodes = pairedIndicator ? [
40
+ /* @__PURE__ */ jsx("a-expander-indicator", { "data-when": "closed", "aria-hidden": "true", inert: true, children: pairedIndicator.closed }, "indicator-closed"),
41
+ /* @__PURE__ */ jsx("a-expander-indicator", { "data-when": "open", "aria-hidden": "true", inert: true, children: pairedIndicator.open }, "indicator-open")
42
+ ] : [/* @__PURE__ */ jsx("a-expander-indicator", { shape: presetIndicator, "aria-hidden": "true", inert: true, children: useCustomIndicator ? customIndicator : null }, "indicator")];
27
43
  const computedStyle = roundStyle(round, "--expander-round", toneStyle(tone, "--expander-tone-source", style));
28
- const titleNode = typeof title === "object" && title !== null ? /* @__PURE__ */ jsx("span", { slot: "title", style: { display: "contents" }, children: title }) : /* @__PURE__ */ jsx("a-expander-summary", { slot: "title", children: title });
44
+ const titleNode = typeof title === "object" && title !== null ? /* @__PURE__ */ jsx("a-expander-title", { children: title }, "title") : /* @__PURE__ */ jsx("a-expander-summary", { level: level != null ? String(level) : void 0, children: title }, "title");
45
+ const actionsNode = hasActions ? /* @__PURE__ */ jsx("a-expander-actions", { children: actions }, "actions") : null;
29
46
  return /* @__PURE__ */ jsxs(
30
47
  "a-expander",
31
48
  {
32
49
  state: controlled ? open ? "open" : "closed" : void 0,
33
50
  "default-state": !controlled && defaultOpen ? "open" : void 0,
34
- level: level != null ? String(level) : void 0,
35
51
  tone: neutralToneAttr(tone),
36
52
  priority: priority && priority !== "secondary" ? priority : void 0,
37
53
  outdent: outdent ? "" : void 0,
38
54
  round: roundAttr(round),
39
55
  disabled: disabled ? "" : void 0,
40
- indicator: presetIndicator,
41
- "indicator-placement": indicatorPlacement && indicatorPlacement !== "start" ? indicatorPlacement : void 0,
42
- onstatechange: onStateChange ? (e) => {
43
- const { event, detail, isOwn } = nativeStateChange(e);
44
- if (isOwn && detail)
45
- onStateChange(event, {
46
- next: detail.next === "open",
47
- prev: detail.prev === "open"
48
- });
49
- } : void 0,
56
+ ...eventProps,
50
57
  class: className,
51
58
  style: computedStyle,
52
59
  ...rest,
53
60
  children: [
54
- titleNode,
55
- actions != null && /* @__PURE__ */ jsx("span", { slot: "actions", children: actions }),
56
- pairedIndicator && /* @__PURE__ */ jsxs(Fragment, { children: [
57
- /* @__PURE__ */ jsx("span", { slot: "indicator", "data-when": "closed", "aria-hidden": "true", inert: true, children: pairedIndicator.closed }),
58
- /* @__PURE__ */ jsx("span", { slot: "indicator", "data-when": "open", "aria-hidden": "true", inert: true, children: pairedIndicator.open })
59
- ] }),
60
- useCustomSlot && /* @__PURE__ */ jsx("span", { slot: "indicator", "aria-hidden": "true", inert: true, children: customIndicator }),
61
+ indicatorPlacement === "end" ? [titleNode, actionsNode, ...indicatorNodes] : [...indicatorNodes, titleNode, actionsNode],
61
62
  /* @__PURE__ */ jsx("a-expander-details", { children })
62
63
  ]
63
64
  }
@@ -16,26 +16,20 @@ export interface IconProps extends BaseProps {
16
16
  label?: string;
17
17
  }
18
18
  /**
19
- * Renders an `<a-icon>` web component. Color follows `currentColor`;
20
- * size is set via the `size` prop (defaults to 16).
19
+ * Displays an icon in the current text color. Pass `size` for custom dimensions
20
+ * and `label` when the icon conveys meaning without accompanying text.
21
21
  *
22
- * Requires `@antadesign/anta/elements` to be imported (client-side only)
23
- * to register the underlying custom element and load the icon
24
- * stylesheets.
22
+ * Icon needs CSS without element registration. See
23
+ * [Install and configure](https://anta.design/install/) for stylesheet setup.
25
24
  *
26
- * @example Decorative icon paired with text — no label needed
25
+ * @example Decorative icon beside status text
27
26
  * ```tsx
28
- * <button>
29
- * <Icon shape="trash" />
30
- * Delete
31
- * </button>
27
+ * <Text><Icon shape="check" /> Saved</Text>
32
28
  * ```
33
29
  *
34
- * @example Meaningful icon-only control — pass a label
30
+ * @example Standalone icon with an accessible name
35
31
  * ```tsx
36
- * <button aria-label="Delete">
37
- * <Icon shape="trash" label="Delete" />
38
- * </button>
32
+ * <Icon shape="warning-triangle" label="Warning" />
39
33
  * ```
40
34
  */
41
35
  export declare const Icon: ({ shape, size, label, className, style, ...rest }: IconProps) => any;
@@ -10,40 +10,33 @@ export interface InputChangeAttrs {
10
10
  name?: string;
11
11
  /** `true` when the value is empty. */
12
12
  empty: boolean;
13
- /** Whether the field currently passes validation (native + `error`). `undefined`
13
+ /** Whether the field currently passes validation (native constraints and `status`). `undefined`
14
14
  * where `ElementInternals` is unsupported. */
15
15
  valid?: boolean;
16
16
  /** Current validation message (`''` when valid). */
17
17
  validationMessage: string;
18
18
  }
19
19
  export interface InputProps extends BaseProps, DOMEventHandlers {
20
- /** Extra content rendered directly under the field, above the hint/error (it
21
- * pushes the message down). A no-box child like an Anta `<Tooltip>` takes no
22
- * space and anchors to the field — consistent with how tooltips attach
23
- * to any other element. Use the named `leading` / `trailing` props for
24
- * in-field content. */
20
+ /** Extra content between the field and hint. A `Tooltip` child anchors to the
21
+ * field. Use `leading` and `trailing` for content inside the field. */
25
22
  children?: React.ReactNode;
26
- /** Field label, shown above the control. A string is rendered with the
27
- * label type scale; pass a node for full control. Associated with the
28
- * control as its accessible name (the element mirrors the label text to
29
- * `aria-label`, since `<label for>` can't cross the shadow boundary). */
23
+ /** Explain what to enter. Accepts text or JSX and also names the field for
24
+ * screen readers. */
30
25
  label?: React.ReactNode;
31
- /** Message below the field. Neutral helper text by default; `status` recolors
32
- * it and prefixes the matching glyph. */
26
+ /** Persistent help or feedback below the field. `status` colors the message and
27
+ * adds a status icon. */
33
28
  hint?: React.ReactNode;
34
- /** Validation / feedback tone — colors the border + `hint` and prefixes a
35
- * glyph. Only `critical` marks the field invalid (`aria-invalid`, blocks form
36
- * submission, `:state(invalid)`); `success` / `warning` / `info` / `brand`
37
- * are advisory and stay valid. Omit (or `neutral`) for a plain field. */
29
+ /** Feedback for the field and hint. Use `critical` for an error that must be
30
+ * fixed before submission. Other statuses provide feedback without adding a
31
+ * validation error. Required fields and other validation rules still apply. */
38
32
  status?: 'neutral' | 'brand' | 'info' | 'success' | 'warning' | 'critical';
39
- /** Glyph shown before the `hint` when `status` is set. Each status has a
40
- * default (critical → `warning-diamond`, warning → `warning-triangle`,
41
- * success → `circle-check`, info → `info`, brand → `circle-small-solid`); pass a
42
- * shape to override, or `false` to drop it. `neutral` has no default glyph. */
33
+ /** Icon before the hint. The defaults are `warning-diamond` for critical,
34
+ * `warning-triangle` for warning, `circle-check` for success, `info` for info,
35
+ * and `circle-small-solid` for brand. Pass a shape to replace it or `false` to
36
+ * hide it. Neutral has no default icon. */
43
37
  statusIcon?: IconShape | (string & {}) | false;
44
- /** Custom accent color — any literal CSS color tints the border when shown
45
- * (focus ring stays the global `--focus-ring`). For consistency with the
46
- * other controls' custom-tone knob; a `status` still overrides for validation. */
38
+ /** Custom CSS border color. A non-neutral `status` takes precedence. The focus
39
+ * ring uses the global `--focus-ring`. */
47
40
  tone?: string;
48
41
  /** Field emphasis. `primary` has a fill and border, `secondary` keeps the
49
42
  * border without a fill, and `tertiary` makes the border transparent until
@@ -51,52 +44,49 @@ export interface InputProps extends BaseProps, DOMEventHandlers {
51
44
  * Status borders remain visible at rest.
52
45
  * @defaultValue primary */
53
46
  priority?: 'primary' | 'secondary' | 'tertiary';
54
- /** Size variant. small=24px, medium=28px, large=32px tall; the type scale and
55
- * icon track the size (small 13/16 + 14px icon · medium 15/20 + 16px ·
56
- * large 17/22 + 18px).
57
- * @defaultValue medium */
47
+ /** Adjust the field, text, icons, and action buttons together to suit the
48
+ * surrounding interface.
49
+ * @defaultValue medium */
58
50
  size?: 'small' | 'medium' | 'large';
59
- /** Controlled value. Pair with `onChange` / `onInput`. */
51
+ /** Controlled value. Update it with `onInput` or `onValueChange` for live
52
+ * editing. */
60
53
  value?: string;
61
- /** Initial value for the uncontrolled case. */
54
+ /** Initial value for an uncontrolled field, restored on form reset. */
62
55
  defaultValue?: string;
63
- /** Render a `<textarea>` instead of an `<input>`. Without `rows` it grows
64
- * with its content from one line (capped by `maxRows` if set). Autogrow uses
65
- * CSS `field-sizing` where supported (Chrome/Edge, Safari ≥ 26.2) and falls
66
- * back to a built-in JS resize elsewhere (Firefox, older Safari), so it grows
67
- * in every browser. */
56
+ /** Allow longer text and grow the field as someone types. Use `maxRows` to
57
+ * limit growth or `rows` to keep a fixed height. */
68
58
  multiline?: boolean;
69
- /** Fixed visible row count — a constant-height `<textarea>` (implies
70
- * `multiline`). */
59
+ /** Fixed visible row count. Also enables `multiline`. */
71
60
  rows?: number;
72
- /** Cap the autogrow height (in rows) of a `multiline` field with no `rows`.
73
- * Omit for unbounded growth. */
61
+ /** Maximum row count for autogrow when `rows` is unset. Omit for unlimited
62
+ * growth. */
74
63
  maxRows?: number;
75
- /** Show a clear button as the first trailing item once the field has a
76
- * value. */
64
+ /** Show a clear button before trailing content when an editable field has a
65
+ * value. */
77
66
  clearable?: boolean;
78
- /** Content pinned to the start of the field (e.g. an icon). */
67
+ /** Content at the start of the field, such as an icon or text. */
79
68
  leading?: React.ReactNode;
80
- /** Content pinned to the end of the field (e.g. icons, buttons), after the
81
- * clear button when `clearable`. */
69
+ /** Content at the end of the field, after the clear button. Anta buttons fit
70
+ * the field height and keep their padding. Icon-only Buttons are square; text
71
+ * buttons keep their natural width. Pass buttons directly or in
72
+ * a fragment for compact spacing. Other wrapping elements keep the text inset.
73
+ * Override either inset with the `--input-trailing-inset` CSS property. */
82
74
  trailing?: React.ReactNode;
83
- /** Single-line input type. Ignored when `multiline`. `search` is a
84
- * **wrapper-only** shorthand: it defaults a leading search icon and a clear
85
- * button (both overridable — pass your own `leading`, or `clearable={false}`)
86
- * and sets `inputmode="search"`, but the DOM input stays `type="text"`. The
87
- * native `search` type never reaches the element, so the browser's own
88
- * clear/search affordances never appear — Anta owns that chrome.
89
- * @defaultValue text */
75
+ /** Choose the kind of information to enter. Ignored for multiline fields.
76
+ * `search` adds a search icon and clear button. Replace the icon with
77
+ * `leading` or hide the button with `clearable={false}`.
78
+ * @defaultValue text */
90
79
  type?: 'text' | 'search' | 'email' | 'password' | 'tel' | 'url' | 'number';
91
- /** Native autocomplete token. Overrides the value derived from `type`
92
- * (`email` / `tel` / `url`) — set it for the cases `type` can't express, e.g.
93
- * `username`, `current-password`, `new-password`, `one-time-code`, or `off`. */
80
+ /** Native autocomplete token describing the field purpose. Overrides the token
81
+ * derived from the email, tel, or url type. Set an explicit token for other
82
+ * purposes, such as `username`, `current-password`, or `one-time-code`. */
94
83
  autoComplete?: 'off' | 'on' | 'name' | 'username' | 'email' | 'current-password' | 'new-password' | 'one-time-code' | 'tel' | 'url' | (string & {});
95
- /** Virtual-keyboard hint. Overrides the value derived from `type`. */
84
+ /** Virtual keyboard hint. Overrides the value derived from `type` without
85
+ * changing validation. */
96
86
  inputMode?: 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url';
97
87
  /** Focus this field when its containing `Dialog` opens. */
98
88
  autoFocus?: boolean;
99
- /** Form field name — submitted with the form via ElementInternals. */
89
+ /** Form field name for submission, named form lookup, and attribute selectors. */
100
90
  name?: string;
101
91
  /** Placeholder shown when empty. */
102
92
  placeholder?: string;
@@ -114,71 +104,62 @@ export interface InputProps extends BaseProps, DOMEventHandlers {
114
104
  button?: boolean;
115
105
  /** Mark the field required (drives native validity). */
116
106
  required?: boolean;
117
- /** Dim the `leading` / `trailing` adornments at rest; they brighten to full
118
- * when the field is hovered or focused (a quiet-until-engaged affordance for
119
- * trailing actions). */
107
+ /** Keep leading, trailing, and clear content subtle until someone hovers over
108
+ * or focuses the field. */
120
109
  dimActions?: boolean;
121
- /** Fully-round the field (`border-radius: 999px`). Pass a `number` (px) or a CSS
122
- * length string for a custom radius. The `clearable` × button always rounds to
123
- * a circle to match (it isn't sized by a custom field value). */
110
+ /** Give the field fully rounded corners. Pass a number in pixels or a CSS
111
+ * length for a custom radius. The clear button stays circular. */
124
112
  round?: boolean | number | string;
125
113
  /** Toggle native spell-checking. */
126
114
  spellCheck?: boolean;
127
- /** Max input length. */
115
+ /** Maximum text length for native types that support it. */
128
116
  maxLength?: number;
129
- /** Min input length. */
117
+ /** Minimum text length for native types that support it. */
130
118
  minLength?: number;
131
- /** Validation pattern (single-line). */
119
+ /** Validation pattern for supported single-line types. Checks validity without
120
+ * preventing typing. */
132
121
  pattern?: string;
133
- /** Min / max / step — for `type="number"`. */
122
+ /** Minimum value for `type="number"`. */
134
123
  min?: number | string;
124
+ /** Maximum value for `type="number"`. */
135
125
  max?: number | string;
126
+ /** Allowed increment for `type="number"`. */
136
127
  step?: number | string;
137
- /** Fires on every keystroke. Read `e.target.value`. */
128
+ /** Fires when editing changes the value or the field is cleared. Read
129
+ * `e.target.value`. */
138
130
  onInput?: (e: any) => void;
139
- /** Fires on **commit** (blur / Enter) — the platform `change` semantics, **not**
140
- * React's per-keystroke `onChange`. This is a web component, so `onChange` keeps
141
- * the native meaning; reach for `onInput` (every keystroke) or `onValueChange`
142
- * (both) for live updates. Read `e.target.value`. */
131
+ /** Fires on native commit, such as blur or Enter, and on clear. Use `onInput` or
132
+ * `onValueChange` for live edits. Read `e.target.value`. */
143
133
  onChange?: (e: any) => void;
144
- /** Unified value-change handler — the easy path for state. Fires on `input`
145
- * *and* `change` (and on clear), with the native `event` plus a convenience
146
- * `attrs` snapshot (`value`, `name`, `empty`, `valid`, `validationMessage`) so
147
- * you can do `setForm(s => ({ ...s, [attrs.name]: attrs.value }))` without
148
- * digging into the event. Use `event.type` to tell a live edit (`input`) from
149
- * a commit (`change`); read `id` / `type` / `className` off `event.target`. */
134
+ /** Receive the current text, field name, and validation result while editing
135
+ * and when an edit is committed, including clear. Can run more than once for
136
+ * the same value. Use `event.type` to distinguish the events. */
150
137
  onValueChange?: (event: any, attrs: InputChangeAttrs) => void;
151
- /** Fires when the built-in clear button (`clearable`) is clicked, *before*
152
- * the field is cleared. Call `e.preventDefault()` to keep the current value
153
- * — the clear is cancelled and `onClearInput` won't fire. Backed by the
154
- * element's cancelable, bubbling `clearclick` event. */
138
+ /** Fires before the built-in clear action. Call `e.preventDefault()`
139
+ * synchronously to keep the value and cancel `onClearInput`. Corresponds to the
140
+ * cancelable, bubbling `clearclick` event. */
155
141
  onClearClick?: (e: CustomEvent) => void;
156
- /** Fires after the built-in clear button (`clearable`) has cleared the field
157
- * — so `onInput` / `onChange` fire too — making this useful for reacting
158
- * specifically to a clear. Doesn't fire if `onClearClick` cancelled the
159
- * clear. Backed by the element's bubbling `clearinput` event. */
142
+ /** Fires after clearing, following `onInput` and `onChange`. Does not fire when
143
+ * clearing is canceled. Corresponds to the bubbling `clearinput` event. */
160
144
  onClearInput?: (e: CustomEvent) => void;
161
145
  /** Fires when the field gains focus. */
162
146
  onFocus?: (e: any) => void;
163
147
  /** Fires when the field loses focus. */
164
148
  onBlur?: (e: any) => void;
165
- /** ARIA `role` for the field — e.g. `combobox` when the input drives a
166
- * suggestion `listbox` (see `InputAutocomplete`). The custom element
167
- * delegates it, together with standard `aria-*` props, to the focused native
168
- * shadow control. Left unset by default. */
149
+ /** ARIA role for the native control, such as `combobox` for an InputAutocomplete
150
+ * trigger. The element delegates this and standard `aria-*` props to its native
151
+ * shadow control. */
169
152
  role?: string;
170
153
  }
171
154
  /**
172
- * `<Input>` — a text field. Renders an `<a-input>` web component whose real
173
- * `<input>` / `<textarea>` lives in shadow DOM, so it's self-contained: focus,
174
- * forms (via `ElementInternals`), IME, and autofill are all native, and the
175
- * control is reachable for styling through `::part(input)`.
155
+ * Text field for single-line or multiline input, with labels, hints, and actions.
156
+ * Supports native form submission, validation, autofill, and input methods.
157
+ * Pass `multiline` for a textarea and `clearable` to offer a clear action.
176
158
  *
177
- * The wrapper is stateless — it maps props to attributes and slots. The clear
178
- * button is dropped into `slot="trailing"`; its click finds the host and calls
179
- * `clear()`, and its visibility is CSS off the element's `:state(filled)`.
159
+ * Use `leading` and `trailing` for content inside the field. Style the editable
160
+ * control through `::part(input)`.
180
161
  *
181
- * Requires `@antadesign/anta/elements` to be imported (client-side only).
162
+ * See [Install and configure](https://anta.design/install/) for runtime setup.
182
163
  *
183
164
  * @example
184
165
  * ```tsx
@@ -1,15 +1,16 @@
1
1
  import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
+ import { CLEAR_TRIGGER } from "../input-events";
2
3
  import { nativeStateChange, neutralToneAttr, toneStyle, roundStyle, roundAttr } from "../anta_helpers";
3
4
  import { Button } from "./Button";
4
5
  import { Icon } from "./Icon";
6
+ import { useNativeEventProps } from "../native-event-props";
5
7
  const presence = (on) => on ? "" : void 0;
6
- const isStringish = (n) => typeof n === "string" || typeof n === "number";
7
8
  const attrsOf = (e) => {
8
9
  const el = e?.target ?? {};
9
10
  const value = el.value ?? "";
10
11
  return {
11
12
  value,
12
- name: el.getAttribute?.("name") ?? void 0,
13
+ name: el.name || void 0,
13
14
  empty: !value,
14
15
  valid: el.validity ? el.validity.valid : void 0,
15
16
  validationMessage: el.validationMessage ?? ""
@@ -77,6 +78,18 @@ const Input = ({
77
78
  style,
78
79
  ...rest
79
80
  }) => {
81
+ const eventProps = useNativeEventProps({
82
+ oninput: onInput || onValueChange ? (e) => {
83
+ onInput?.(e);
84
+ onValueChange?.(e, attrsOf(e));
85
+ } : void 0,
86
+ onchange: onChange || onValueChange ? (e) => {
87
+ onChange?.(e);
88
+ onValueChange?.(e, attrsOf(e));
89
+ } : void 0,
90
+ onclearclick: onClearClick ? (e) => onClearClick(nativeStateChange(e).event) : void 0,
91
+ onclearinput: onClearInput ? (e) => onClearInput(nativeStateChange(e).event) : void 0
92
+ });
80
93
  const statusTone = status && status !== "neutral" ? status : void 0;
81
94
  const glyph = statusIcon === void 0 ? statusTone ? STATUS_ICON[statusTone] : void 0 : statusIcon;
82
95
  const isSearch = type === "search";
@@ -117,32 +130,15 @@ const Input = ({
117
130
  step,
118
131
  "aria-invalid": status === "critical" ? "true" : void 0,
119
132
  role: role ?? (isSearch ? "searchbox" : void 0),
120
- oninput: onInput || onValueChange ? (e) => {
121
- onInput?.(e);
122
- onValueChange?.(e, attrsOf(e));
123
- } : void 0,
124
- onchange: onChange || onValueChange ? (e) => {
125
- onChange?.(e);
126
- onValueChange?.(e, attrsOf(e));
127
- } : void 0,
128
- onclearclick: onClearClick ? (e) => onClearClick(nativeStateChange(e).event) : void 0,
129
- onclearinput: onClearInput ? (e) => onClearInput(nativeStateChange(e).event) : void 0,
133
+ ...eventProps,
130
134
  class: className,
131
135
  style: roundStyle(round, "--input-round", toneStyle(tone, "--input-tone-source", style)),
132
136
  ...rest,
133
137
  children: [
134
- label != null && (isStringish(label) ? /* @__PURE__ */ jsx("span", { slot: "label", children: label }) : /* @__PURE__ */ jsx("span", { slot: "label", style: { display: "contents" }, children: label })),
135
- resolvedLeading != null && /* @__PURE__ */ jsx("span", { slot: "leading", style: { display: "contents" }, children: resolvedLeading }),
136
- resolvedClearable && // A real <a-button> (light DOM → fully styled, keyboard-focusable) in
137
- // the element's `clear` slot — the element controls its visibility (shown
138
- // only when filled + editable). It fires the bubbling `clearrequest`
139
- // event via a-button's global listener, so clearing works even without
140
- // framework hydration; the element turns that into clearclick→clear().
141
- // CONTRACT: the `data-custom-event` value below MUST match `CLEAR_TRIGGER`
142
- // in the element (src/elements/a-input.ts). The string is duplicated, not
143
- // shared — importing the element module here would self-register it and
144
- // break the wrapper/element decoupling. Rename in both places.
145
- /* @__PURE__ */ jsx("span", { slot: "clear", style: { display: "contents" }, children: /* @__PURE__ */ jsx(
138
+ label != null && /* @__PURE__ */ jsx("a-input-label", { slot: "label", children: label }),
139
+ resolvedLeading != null && /* @__PURE__ */ jsx("a-input-leading", { slot: "leading", children: resolvedLeading }),
140
+ resolvedClearable && // The UI element handles the clear lifecycle before framework hydration.
141
+ /* @__PURE__ */ jsx("a-input-clear", { slot: "clear", children: /* @__PURE__ */ jsx(
146
142
  Button,
147
143
  {
148
144
  priority: "tertiary",
@@ -150,11 +146,11 @@ const Input = ({
150
146
  round: !!round,
151
147
  icon: "x",
152
148
  "aria-label": "Clear",
153
- "data-custom-event": "clearrequest"
149
+ "data-custom-event": CLEAR_TRIGGER
154
150
  }
155
151
  ) }),
156
- trailing != null && /* @__PURE__ */ jsx("span", { slot: "trailing", style: { display: "contents" }, children: trailing }),
157
- hint != null && /* @__PURE__ */ jsxs("span", { slot: "hint", style: { display: "contents" }, children: [
152
+ trailing != null && /* @__PURE__ */ jsx("a-input-trailing", { slot: "trailing", children: trailing }),
153
+ hint != null && /* @__PURE__ */ jsxs("a-input-hint", { slot: "hint", children: [
158
154
  glyph && /* @__PURE__ */ jsx(Icon, { shape: glyph, "aria-hidden": "true" }),
159
155
  /* @__PURE__ */ jsx("span", { children: hint })
160
156
  ] }),
@@ -1,24 +1,31 @@
1
- import type { BaseProps } from '../general_types';
1
+ import type { BaseProps, DOMEventHandlers } from '../general_types';
2
2
  import type { IconShape } from '../elements/a-icon.shapes';
3
3
  import type { SelectOption } from './Select';
4
- export interface InputAutocompleteProps extends Omit<BaseProps, 'children'> {
4
+ export interface InputAutocompleteProps extends Omit<BaseProps, 'children'>, Omit<DOMEventHandlers, 'onSelect'> {
5
5
  /** The suggestions: bare strings or `SelectOption`s (`value` / `label` / `hint` /
6
6
  * `icon` / `tone` / `disabled`). A flat list; picking one fills the field. */
7
7
  suggestions: (string | SelectOption)[];
8
- /** Controlled value: the field's text, a **free** string not constrained to a
9
- * suggestion. Leave undefined for uncontrolled. */
8
+ /** The controlled field text. Any string is valid, including `''` for an empty
9
+ * field. Omit or pass `undefined` for uncontrolled use. Parent updates do not
10
+ * call `onValueChange`. Releasing control retains the latest controlled text. */
10
11
  value?: string;
11
- /** Initial value for uncontrolled use. */
12
+ /** Initial uncontrolled text and the value restored by a form reset. Resetting
13
+ * an unfocused field does not open suggestions or move focus. */
12
14
  defaultValue?: string;
13
- /** Fires as the text changes (typing, picking a suggestion, or clearing) with
14
- * the new field text. */
15
+ /** Reports text changes from typing, picking a suggestion, clearing, or form
16
+ * reset. */
15
17
  onValueChange?: (value: string) => void;
16
- /** Fires only when a suggestion is chosen, with that option. */
18
+ /** Fires after `onValueChange` when a suggestion is chosen, with the normalized
19
+ * option. This reports the choice even if a controlled parent rejects the value.
20
+ * Typing and clearing do not call it. */
17
21
  onSelect?: (option: SelectOption) => void;
18
22
  /** How suggestions match the text. `true` uses the built-in case-insensitive
19
23
  * substring match on `value` / `label` / `hint`; a function `(option, query) =>
20
- * boolean` uses a custom matcher; `false` shows `suggestions` unchanged for
21
- * application-provided async or remote filtering.
24
+ * boolean` receives the original field text, including whitespace, once per
25
+ * option on each render, including when empty. Custom matching has no automatic
26
+ * highlighting. `false` shows `suggestions` unchanged for application-provided
27
+ * async or remote filtering, without automatic highlighting. Replacing results
28
+ * updates an open list but does not reopen a dismissed list.
22
29
  * @defaultValue true */
23
30
  filter?: boolean | ((option: SelectOption, query: string) => boolean);
24
31
  /** Placeholder text. */
@@ -46,6 +53,18 @@ export interface InputAutocompleteProps extends Omit<BaseProps, 'children'> {
46
53
  leading?: React.ReactNode;
47
54
  /** Trailing adornment inside the field. */
48
55
  trailing?: React.ReactNode;
56
+ /** Observe focus before the suggestion list opens. */
57
+ onFocus?: (event: any) => void;
58
+ /** Observe focus loss. The suggestion list closes without changing the text. */
59
+ onBlur?: (event: any) => void;
60
+ /** Observe editing before `onValueChange`. The input event reports text that
61
+ * has already changed; `preventDefault()` does not reject that text. */
62
+ onInput?: (event: any) => void;
63
+ /** Observe keys before the wrapper's plain-Enter dismissal. `preventDefault()`
64
+ * keeps that dismissal from running. Menu handles navigation and active-option
65
+ * activation earlier, in document capture; this callback cannot veto those
66
+ * actions. Composing keys remain with the input method. */
67
+ onKeyDown?: (event: any) => void;
49
68
  }
50
69
  /**
51
70
  * `<InputAutocomplete>` is a text field with a suggestion dropdown. Its value is
@@ -1,6 +1,6 @@
1
1
  import { Fragment, jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
2
  import { useState, useMemo } from "../jsx-runtime";
3
- import { optionPresentationAttrs } from "../anta_helpers";
3
+ import { isComposingKey, optionPresentationAttrs, useSelectionMirror } 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";
@@ -12,13 +12,18 @@ const InputAutocomplete = (props) => {
12
12
  defaultValue,
13
13
  onValueChange,
14
14
  onSelect,
15
+ onFocus,
16
+ onBlur,
17
+ onInput,
18
+ onKeyDown,
15
19
  filter = true,
16
20
  ...rest
17
21
  } = props;
18
22
  const controlled = value !== void 0;
19
- const [internal, setInternal] = useState(defaultValue ?? "");
20
- const text = controlled ? value : internal;
21
- const [open, setOpen] = useState(false);
23
+ const [mirroredText, setInternal] = useSelectionMirror(value, defaultValue ?? "");
24
+ const text = mirroredText ?? "";
25
+ const [{ open }, setPopup] = useState({ open: false, focused: false });
26
+ const setOpen = (next) => setPopup((prev) => ({ ...prev, open: next }));
22
27
  const options = useMemo(() => suggestions.map(normalizeOpt), [suggestions]);
23
28
  const custom = typeof filter === "function";
24
29
  const queryRe = filter !== false && !custom ? matchQueryRegex(text) : null;
@@ -39,18 +44,29 @@ const InputAutocomplete = (props) => {
39
44
  {
40
45
  ...rest,
41
46
  value: text,
47
+ defaultValue,
42
48
  "data-menu-search": "",
43
49
  role: "combobox",
44
50
  "aria-haspopup": "listbox",
45
51
  "aria-expanded": menuOpen ? "true" : "false",
46
52
  "aria-autocomplete": "list",
47
- onFocus: () => setOpen(true),
53
+ onFocus: (e) => {
54
+ onFocus?.(e);
55
+ setPopup({ open: true, focused: true });
56
+ },
57
+ onBlur: (e) => {
58
+ onBlur?.(e);
59
+ setPopup({ open: false, focused: false });
60
+ },
48
61
  onInput: (e) => {
49
- setText(e.currentTarget.value);
50
- setOpen(true);
62
+ const next = e.currentTarget.value;
63
+ onInput?.(e);
64
+ setText(next);
65
+ setPopup((prev) => ({ ...prev, open: prev.focused }));
51
66
  },
52
67
  onKeyDown: (e) => {
53
- if (e.key === "Enter" && !e.defaultPrevented) setOpen(false);
68
+ onKeyDown?.(e);
69
+ if (e.key === "Enter" && !e.defaultPrevented && !isComposingKey(e)) setOpen(false);
54
70
  }
55
71
  }
56
72
  ),
@@ -69,6 +85,7 @@ const InputAutocomplete = (props) => {
69
85
  {
70
86
  ...optionAttrs,
71
87
  role: "option",
88
+ tabIndex: -1,
72
89
  icon: opt.icon,
73
90
  label: highlight(opt.label ?? opt.value, queryRe),
74
91
  hint: opt.hint ? highlight(opt.hint, queryRe) : opt.hint,
@@ -1,6 +1,6 @@
1
1
  import { Fragment, jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
2
  import { useMemo, useState } from "../jsx-runtime";
3
- import { isModifiedNavigationKey } from "../anta_helpers";
3
+ import { isModifiedNavigationKey, unwrapNativeEvent } from "../anta_helpers";
4
4
  import { Temporal } from "temporal-polyfill";
5
5
  import {
6
6
  parseISODate,
@@ -195,7 +195,7 @@ const InputDate = ({
195
195
  "aria-label": "Time",
196
196
  value: hasTime ? `${String(h24).padStart(2, "0")}:${curM}` : "",
197
197
  onValueChange: (e, { value: t }) => {
198
- const ev = "nativeEvent" in e ? e.nativeEvent : e;
198
+ const ev = unwrapNativeEvent(e);
199
199
  if (ev?.type !== "change") return;
200
200
  const base = dateISO || Temporal.Now.plainDateISO().toString();
201
201
  commit(t ? `${base}T${t}` : base);