@antadesign/anta 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +14 -0
  2. package/dist/anta_helpers.d.ts +39 -1
  3. package/dist/anta_helpers.js +30 -2
  4. package/dist/components/Button.d.ts +7 -4
  5. package/dist/components/Button.js +6 -11
  6. package/dist/components/Checkbox.d.ts +97 -0
  7. package/dist/components/Checkbox.js +77 -0
  8. package/dist/components/Expander.d.ts +74 -0
  9. package/dist/components/Expander.js +53 -0
  10. package/dist/components/Input.d.ts +159 -0
  11. package/dist/components/Input.js +150 -0
  12. package/dist/components/Menu.d.ts +70 -0
  13. package/dist/components/Menu.js +42 -0
  14. package/dist/components/MenuGroup.d.ts +24 -0
  15. package/dist/components/MenuGroup.js +19 -0
  16. package/dist/components/MenuItem.d.ts +50 -0
  17. package/dist/components/MenuItem.js +41 -0
  18. package/dist/components/MenuSeparator.d.ts +14 -0
  19. package/dist/components/MenuSeparator.js +7 -0
  20. package/dist/components/Progress.d.ts +12 -6
  21. package/dist/components/Progress.js +7 -4
  22. package/dist/components/Radio.d.ts +37 -0
  23. package/dist/components/Radio.js +33 -0
  24. package/dist/components/RadioGroup.d.ts +119 -0
  25. package/dist/components/RadioGroup.js +108 -0
  26. package/dist/components/Tag.d.ts +38 -5
  27. package/dist/components/Tag.js +9 -5
  28. package/dist/components/Text.d.ts +27 -12
  29. package/dist/components/Text.js +6 -3
  30. package/dist/components/Title.d.ts +10 -1
  31. package/dist/elements/a-button.css +1 -1
  32. package/dist/elements/a-button.d.ts +56 -0
  33. package/dist/elements/a-button.js +13 -11
  34. package/dist/elements/a-checkbox.css +1 -0
  35. package/dist/elements/a-checkbox.d.ts +52 -0
  36. package/dist/elements/a-checkbox.js +130 -0
  37. package/dist/elements/a-expander.css +1 -0
  38. package/dist/elements/a-expander.d.ts +28 -0
  39. package/dist/elements/a-expander.js +237 -0
  40. package/dist/elements/a-icon.d.ts +14 -0
  41. package/dist/elements/a-icon.shapes.css +1 -1
  42. package/dist/elements/a-icon.shapes.d.ts +9 -1
  43. package/dist/elements/a-icon.shapes.js +10 -1
  44. package/dist/elements/a-input.css +1 -0
  45. package/dist/elements/a-input.d.ts +68 -0
  46. package/dist/elements/a-input.js +511 -0
  47. package/dist/elements/a-menu-group.css +1 -0
  48. package/dist/elements/a-menu-group.d.ts +13 -0
  49. package/dist/elements/a-menu-group.js +15 -0
  50. package/dist/elements/a-menu-item.css +1 -0
  51. package/dist/elements/a-menu-item.d.ts +47 -0
  52. package/dist/elements/a-menu-item.js +30 -0
  53. package/dist/elements/a-menu-separator.css +1 -0
  54. package/dist/elements/a-menu-separator.d.ts +13 -0
  55. package/dist/elements/a-menu-separator.js +15 -0
  56. package/dist/elements/a-menu.css +1 -0
  57. package/dist/elements/a-menu.d.ts +171 -0
  58. package/dist/elements/a-menu.js +714 -0
  59. package/dist/elements/a-progress.css +1 -1
  60. package/dist/elements/a-progress.d.ts +12 -0
  61. package/dist/elements/a-progress.js +1 -0
  62. package/dist/elements/a-radio-group.css +1 -0
  63. package/dist/elements/a-radio-group.d.ts +33 -0
  64. package/dist/elements/a-radio-group.js +160 -0
  65. package/dist/elements/a-radio.css +1 -0
  66. package/dist/elements/a-radio.d.ts +14 -0
  67. package/dist/elements/a-radio.js +46 -0
  68. package/dist/elements/a-tag.css +1 -1
  69. package/dist/elements/a-text.css +1 -1
  70. package/dist/elements/a-text.d.ts +42 -3
  71. package/dist/elements/a-text.js +73 -33
  72. package/dist/elements/a-tooltip.d.ts +43 -11
  73. package/dist/elements/a-tooltip.js +46 -51
  74. package/dist/elements/index.d.ts +9 -0
  75. package/dist/elements/index.js +27 -0
  76. package/dist/general_types.d.ts +467 -15
  77. package/dist/index.d.ts +16 -0
  78. package/dist/index.js +16 -0
  79. package/dist/jsx-runtime.d.ts +42 -7
  80. package/dist/jsx-runtime.js +14 -2
  81. package/dist/tokens.css +1 -1
  82. package/package.json +1 -1
package/README.md CHANGED
@@ -203,3 +203,17 @@ To use the Antithesis fonts, register your own `@font-face` declarations and ove
203
203
  --monospace: "Antithesis mono", monospace;
204
204
  }
205
205
  ```
206
+
207
+ ## Browser support
208
+
209
+ Anta targets evergreen browsers and ships **no polyfills and no feature detection** for its baseline. The floor is set by the [Popover API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API) (used by `<a-menu>` and `<a-tooltip>` for top-layer rendering):
210
+
211
+ | Browser | Minimum version |
212
+ | --- | --- |
213
+ | Chrome / Edge | 114 (May 2023) |
214
+ | Safari | 17 (Sep 2023) |
215
+ | Firefox | 125 (Apr 2024) |
216
+
217
+ This corresponds roughly to [Baseline 2024](https://web.dev/baseline). Within that floor, anta freely relies on: `popover` / `showPopover()` / `:popover-open`, `color-mix(in oklch, …)` and relative `oklch(from …)` colors, `:has()`, `dvh` units, CSS cascade layers, and constructable shadow DOM. On an older browser these fail hard (e.g. `showPopover()` throws) — there is no degraded mode by design; gate anta usage on your own support matrix instead.
218
+
219
+ Two features are used as **progressive enhancement** with explicit fallbacks: `checkVisibility()` (falls back to `getClientRects()`), and CSS typed `attr()` for `<a-icon size>` (Chrome 133+ / Safari 18.2+; elsewhere use the `<Icon size>` wrapper or the `--icon-size` variable, see the `a-icon` docs).
@@ -1,4 +1,26 @@
1
1
  export declare function hasChildren(children: React.ReactNode): boolean;
2
+ /**
3
+ * Unwrap a `statechange` (or any) `CustomEvent` a renderer may deliver wrapped:
4
+ * React hands a synthetic event with the real one on `.nativeEvent`; Preact passes
5
+ * the native event directly. Returns the native event and its `detail`. Shared by
6
+ * every stateful wrapper (`Menu`, `Expander`, `Checkbox`, `RadioGroup`) — don't
7
+ * re-implement it per component.
8
+ */
9
+ export declare function nativeStateChange<D>(e: CustomEvent<D> | {
10
+ nativeEvent: CustomEvent<D>;
11
+ }): {
12
+ event: CustomEvent<D>;
13
+ detail?: D;
14
+ };
15
+ /** The six named tones every toned component shares. Anything else is a literal
16
+ * CSS colour the element resolves through its `--{component}-tone-source` var. */
17
+ export declare const NAMED_TONES: Set<string>;
18
+ /**
19
+ * Inline-style helper for a custom (non-named) tone: hands the literal colour to
20
+ * the element via `varName` (e.g. `--radio-tone-source`) so the element's CSS
21
+ * derives the fill/text/border curve in oklch. Named tones return `base` unchanged.
22
+ */
23
+ export declare function toneStyle(tone: string | undefined, varName: string, base?: React.CSSProperties): React.CSSProperties | undefined;
2
24
  /**
3
25
  * `HTMLElement` in browsers, a noop class in Node/Worker environments.
4
26
  * Use this as the base for custom element classes so importing the
@@ -6,4 +28,20 @@ export declare function hasChildren(children: React.ReactNode): boolean;
6
28
  * Instantiation in non-DOM environments still fails, but no consumer
7
29
  * should be doing that.
8
30
  */
9
- export declare const HTMLElementBase: typeof HTMLElement;
31
+ declare const NativeHTMLElement: typeof HTMLElement;
32
+ /**
33
+ * Base for Anta custom elements. Adds realm-correct `view` / `doc` getters:
34
+ * the class may be defined in one realm while the element lives in another
35
+ * (the docs playground renders into an iframe but reuses the parent page's
36
+ * element class), so the module-global `window` / `document` can point at the
37
+ * wrong frame. Anything viewport- or document-scoped — clamping,
38
+ * `getComputedStyle`, scroll / key / pointer listeners — must go through these
39
+ * so it's correct in any frame. Shared by a-tooltip and a-menu.
40
+ */
41
+ export declare class HTMLElementBase extends NativeHTMLElement {
42
+ /** This element's own window (the iframe's window when nested). */
43
+ protected get view(): Window & typeof globalThis;
44
+ /** This element's own document (the iframe's document when nested). */
45
+ protected get doc(): Document;
46
+ }
47
+ export {};
@@ -1,9 +1,37 @@
1
1
  function hasChildren(children) {
2
2
  return Array.isArray(children) ? children.length > 0 : children != null;
3
3
  }
4
- const HTMLElementBase = typeof HTMLElement !== "undefined" ? HTMLElement : class {
4
+ function nativeStateChange(e) {
5
+ const event = "nativeEvent" in e ? e.nativeEvent : e;
6
+ return { event, detail: event?.detail };
7
+ }
8
+ const NAMED_TONES = /* @__PURE__ */ new Set([
9
+ "brand",
10
+ "neutral",
11
+ "info",
12
+ "success",
13
+ "warning",
14
+ "critical"
15
+ ]);
16
+ function toneStyle(tone, varName, base) {
17
+ return tone != null && !NAMED_TONES.has(tone) ? { ...base, [varName]: tone } : base;
18
+ }
19
+ const NativeHTMLElement = typeof HTMLElement !== "undefined" ? HTMLElement : class {
5
20
  };
21
+ class HTMLElementBase extends NativeHTMLElement {
22
+ /** This element's own window (the iframe's window when nested). */
23
+ get view() {
24
+ return this.ownerDocument?.defaultView ?? window;
25
+ }
26
+ /** This element's own document (the iframe's document when nested). */
27
+ get doc() {
28
+ return this.ownerDocument ?? document;
29
+ }
30
+ }
6
31
  export {
7
32
  HTMLElementBase,
8
- hasChildren
33
+ NAMED_TONES,
34
+ hasChildren,
35
+ nativeStateChange,
36
+ toneStyle
9
37
  };
@@ -7,13 +7,14 @@ export type BaseButtonProps = {
7
7
  * tertiary, and quaternary take its hue and pin lightness/chroma to the
8
8
  * brand curve so any input stays legible.
9
9
  * @defaultValue neutral */
10
- tone?: 'neutral' | 'brand' | 'critical' | 'info' | 'success' | 'warning' | (string & {});
10
+ tone?: 'neutral' | 'brand' | 'info' | 'success' | 'warning' | 'critical' | (string & {});
11
11
  /** Size variant. small=24px, medium=28px, large=32px. Omit the
12
12
  * attribute or pass `'medium'` for the default — both render
13
13
  * identically and emit no DOM attribute.
14
14
  * @defaultValue medium */
15
15
  size?: 'small' | 'medium' | 'large';
16
- /** Show a rotating loading indicator. Blocks clicks. */
16
+ /** Show a rotating loading indicator. Blocks clicks and keyboard
17
+ * activation, and removes the button from the tab order while active. */
17
18
  loading?: boolean;
18
19
  /** Disable the button. */
19
20
  disabled?: boolean;
@@ -22,8 +23,10 @@ export type BaseButtonProps = {
22
23
  /** Click handler. */
23
24
  onClick?: (e: any) => void;
24
25
  /** Tab order. The button is keyboard-focusable by default (`0`) and
25
- * becomes `-1` automatically while `disabled` — `<a-button>` and
26
- * `<a role="button">` aren't focusable without an explicit tabindex.
26
+ * becomes `-1` automatically while `disabled` or `loading` — `<a-button>`
27
+ * and `<a role="button">` aren't focusable without an explicit tabindex,
28
+ * and a loading button must stay out of the tab order so Enter/Space can't
29
+ * fire it mid-flight.
27
30
  * @defaultValue 0 */
28
31
  tabIndex?: number;
29
32
  };
@@ -1,12 +1,5 @@
1
1
  import { Fragment, jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
- const NAMED_TONES = /* @__PURE__ */ new Set([
3
- "brand",
4
- "neutral",
5
- "critical",
6
- "info",
7
- "success",
8
- "warning"
9
- ]);
2
+ import { toneStyle } from "../anta_helpers";
10
3
  const wrapChildren = (kids) => {
11
4
  if (kids == null) return kids;
12
5
  const arr = Array.isArray(kids) ? kids : [kids];
@@ -42,8 +35,7 @@ const Button = ({
42
35
  ...rest
43
36
  }) => {
44
37
  const toneAttr = tone || void 0;
45
- const isCustomTone = toneAttr != null && !NAMED_TONES.has(toneAttr);
46
- const computedStyle = isCustomTone ? { ...style, ["--button-tone-source"]: toneAttr } : style;
38
+ const computedStyle = toneStyle(toneAttr, "--button-tone-source", style);
47
39
  const isIconOnly = icon != null && label == null && children == null && iconTrailing == null;
48
40
  const sharedAttrs = {
49
41
  priority,
@@ -60,7 +52,10 @@ const Button = ({
60
52
  loading: loading ? "" : void 0,
61
53
  disabled: disabled ? "" : void 0,
62
54
  selected: selected ? "" : void 0,
63
- tabIndex: disabled ? -1 : 0,
55
+ // Disabled AND loading both leave the keyboard tab order — a loading
56
+ // button blocks the mouse (pointer-events), so it must block Enter/Space
57
+ // activation too, else the loading guard would be mouse-only.
58
+ tabIndex: disabled || loading ? -1 : 0,
64
59
  "aria-disabled": disabled || loading ? "true" : void 0,
65
60
  "aria-busy": loading ? "true" : void 0,
66
61
  "aria-pressed": selected ? "true" : void 0,
@@ -0,0 +1,97 @@
1
+ import type { BaseProps } from "../general_types";
2
+ /** The wrapper-level checked value: a boolean for the binary axis, the string
3
+ * `'indeterminate'` for the third state. Mirrors Radix's `Checkbox.Root`. */
4
+ export type CheckboxValue = boolean | 'indeterminate';
5
+ /** The element-level state enum (the string vocabulary on `<a-checkbox>`'s
6
+ * `state` / `default-state` attributes and its `statechange` event detail). */
7
+ type CheckboxState = 'checked' | 'unchecked' | 'indeterminate';
8
+ /** The element's `statechange` event payload. */
9
+ type StateDetail = {
10
+ next: CheckboxState;
11
+ prev: CheckboxState;
12
+ };
13
+ type StateChangeEvent = CustomEvent<StateDetail>;
14
+ /** Snapshot passed as the 2nd argument to `onAnyChange` — the new value plus
15
+ * form-relevant fields, so you don't poke at `event.target`. Mirrors `Input`'s
16
+ * `onAnyChange` convention. */
17
+ export interface CheckboxChangeAttrs {
18
+ checked: boolean;
19
+ indeterminate: boolean;
20
+ name?: string;
21
+ value: string;
22
+ }
23
+ export interface CheckboxProps extends BaseProps {
24
+ /** Visible label — the *value* of the checkbox (clicked along with the box).
25
+ * Convenience for the common single-string case; for richer content (markup,
26
+ * a link, an info icon) use `children`. When both are supplied, `label`
27
+ * renders first. Required unless `children` or `aria-label` is provided
28
+ * (a `role="checkbox"` takes its name from the author, not the markup). */
29
+ label?: string;
30
+ /** Secondary text rendered under the label — explanatory copy, like
31
+ * Input's hint. Not part of the accessible name. */
32
+ hint?: React.ReactNode;
33
+ /** Controlled checked state. When provided the checkbox is controlled — it
34
+ * renders exactly this and never self-applies; `onStateChange` is a *request*
35
+ * the consumer accepts by updating this prop. Use `defaultChecked` for an
36
+ * uncontrolled checkbox. `'indeterminate'` shows the minus glyph and takes
37
+ * visual precedence; clicking it requests `true`. */
38
+ checked?: CheckboxValue;
39
+ /** Initial checked state for an uncontrolled checkbox. Read once; later
40
+ * changes ignored (the element then owns the state).
41
+ * @defaultValue false */
42
+ defaultChecked?: CheckboxValue;
43
+ /** Disable the checkbox (no interaction, dropped from the tab order). */
44
+ disabled?: boolean;
45
+ /** Form field name. Inside a `<form>` the checkbox submits under this name,
46
+ * contributing `value` when checked — like a native checkbox. */
47
+ name?: string;
48
+ /** Value submitted with the form when checked — like a native checkbox.
49
+ * @defaultValue "on" */
50
+ value?: string;
51
+ /** Colour variant, or any literal CSS color (`'#ff1493'`, `'rebeccapurple'`)
52
+ * for a one-off custom tone. Tints fill, label, hint, and the unselected box
53
+ * border. Named tones track light/dark mode automatically via the theme-aware
54
+ * role tokens; a custom colour keeps its hue + chroma and pins lightness to the
55
+ * fill curve.
56
+ * @defaultValue 'neutral' */
57
+ tone?: 'brand' | 'neutral' | 'info' | 'success' | 'warning' | 'critical' | (string & {});
58
+ /** Size variant. small=14px, medium=16px, large=18px box.
59
+ * @defaultValue 'medium' */
60
+ size?: 'small' | 'medium' | 'large';
61
+ /** Visual priority. `primary` fills the checked box with the tone colour and
62
+ * draws a white checkmark; `secondary` keeps the box unfilled and draws the
63
+ * border + checkmark in the tone colour (an outlined look).
64
+ * @defaultValue 'primary' */
65
+ priority?: 'primary' | 'secondary';
66
+ /** Fired on click / Space *before* the element applies any change. Event-first
67
+ * so `event.preventDefault()` is the synchronous veto (uncontrolled mode);
68
+ * `detail` carries `{ next, prev }`. In controlled mode the element never
69
+ * self-applies — answer by updating `checked`, reject by doing nothing. */
70
+ onStateChange?: (event: StateChangeEvent, detail: {
71
+ next: CheckboxValue;
72
+ prev: CheckboxValue;
73
+ }) => void;
74
+ /** Fired *after* the checked state changes — a native `change` event (the
75
+ * post-apply counterpart to `onStateChange`). Not cancelable. For a controlled
76
+ * checkbox this fires once you've updated `checked`. */
77
+ onChange?: (event: Event) => void;
78
+ /** Like `onChange`, but with a `{ checked, indeterminate, name, value }` snapshot
79
+ * as the 2nd argument — the ergonomic "just give me the new value" callback
80
+ * (mirrors `Input`'s `onAnyChange`). */
81
+ onAnyChange?: (event: Event, attrs: CheckboxChangeAttrs) => void;
82
+ }
83
+ /**
84
+ * Checkbox. Renders an `<a-checkbox>` web component that owns the visual
85
+ * state; controlled via `checked` + `onStateChange`, or uncontrolled via
86
+ * `defaultChecked`. Label is `label` (or `children`); `hint` adds secondary
87
+ * text under the label. Requires `@antadesign/anta/elements`.
88
+ *
89
+ * ```tsx
90
+ * <Checkbox checked={agreed} onStateChange={(e, { next }) => setAgreed(next)}>
91
+ * I agree
92
+ * </Checkbox>
93
+ * <Checkbox defaultChecked label="Remember me" hint="On this device" />
94
+ * ```
95
+ */
96
+ export declare const Checkbox: ({ checked, defaultChecked, disabled, tone, size, priority, onStateChange, onChange, onAnyChange, label, hint, className, style, children, tabIndex, ...rest }: CheckboxProps) => any;
97
+ export {};
@@ -0,0 +1,77 @@
1
+ import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
+ import { nativeStateChange, toneStyle } from "../anta_helpers";
3
+ const valueToState = (v) => v === "indeterminate" ? "indeterminate" : v ? "checked" : "unchecked";
4
+ const stateToValue = (s) => s === "indeterminate" ? "indeterminate" : s === "checked";
5
+ const checkboxAttrsOf = (el) => ({
6
+ checked: !!el?.checked,
7
+ indeterminate: !!el?.indeterminate,
8
+ name: el?.getAttribute?.("name") ?? void 0,
9
+ value: el?.getAttribute?.("value") ?? "on"
10
+ });
11
+ const Checkbox = ({
12
+ checked,
13
+ defaultChecked,
14
+ disabled,
15
+ tone,
16
+ size,
17
+ priority,
18
+ onStateChange,
19
+ onChange,
20
+ onAnyChange,
21
+ label,
22
+ hint,
23
+ className,
24
+ style,
25
+ children,
26
+ tabIndex,
27
+ ...rest
28
+ }) => {
29
+ const computedStyle = toneStyle(tone, "--checkbox-tone-source", style);
30
+ const explicitAriaLabel = rest["aria-label"];
31
+ const ariaLabel = (typeof explicitAriaLabel === "string" ? explicitAriaLabel : void 0) ?? label ?? (typeof children === "string" ? children : void 0);
32
+ const onstatechange = onStateChange ? (e) => {
33
+ const { event, detail } = nativeStateChange(e);
34
+ if (!detail) return;
35
+ onStateChange(event, {
36
+ next: stateToValue(detail.next),
37
+ prev: stateToValue(detail.prev)
38
+ });
39
+ } : void 0;
40
+ const onchange = onChange || onAnyChange ? (e) => {
41
+ onChange?.(e);
42
+ onAnyChange?.(e, checkboxAttrsOf(e.currentTarget));
43
+ } : void 0;
44
+ const stateAttr = checked !== void 0 ? valueToState(checked) : void 0;
45
+ const defaultStateAttr = checked === void 0 && defaultChecked !== void 0 ? valueToState(defaultChecked) : void 0;
46
+ const hasLabel = label != null || children != null;
47
+ return /* @__PURE__ */ jsxs(
48
+ "a-checkbox",
49
+ {
50
+ role: "checkbox",
51
+ "aria-disabled": disabled ? "true" : void 0,
52
+ "aria-label": ariaLabel,
53
+ ...rest,
54
+ state: stateAttr,
55
+ "default-state": defaultStateAttr,
56
+ disabled: disabled ? "" : void 0,
57
+ tone: tone && tone !== "neutral" ? tone : void 0,
58
+ size: size && size !== "medium" ? size : void 0,
59
+ priority: priority && priority !== "primary" ? priority : void 0,
60
+ tabIndex: disabled ? -1 : tabIndex ?? 0,
61
+ onstatechange,
62
+ onchange,
63
+ class: className,
64
+ style: computedStyle,
65
+ children: [
66
+ hasLabel && /* @__PURE__ */ jsxs("a-checkbox-label", { children: [
67
+ label,
68
+ children
69
+ ] }),
70
+ hint != null && /* @__PURE__ */ jsx("a-checkbox-hint", { children: hint })
71
+ ]
72
+ }
73
+ );
74
+ };
75
+ export {
76
+ Checkbox
77
+ };
@@ -0,0 +1,74 @@
1
+ import type { BaseProps } from "../general_types";
2
+ /** Public props for the `<Expander>` disclosure. `title` is the always-
3
+ * visible summary; `children` is the collapsible body. */
4
+ export interface ExpanderProps extends Omit<BaseProps, "title"> {
5
+ /** Summary (header) content. A string is rendered with the `level`
6
+ * type scale; pass a node (e.g. a `<Title>`) for full control or real
7
+ * heading semantics in the document outline. */
8
+ title: React.ReactNode;
9
+ /** Heading type scale applied to a string `title` (mirrors `<Title>`'s
10
+ * levels). Visual only — for outline semantics, pass a `<Title>` as
11
+ * the title.
12
+ * @defaultValue 5 */
13
+ level?: 1 | 2 | 3 | 4 | 5 | 6;
14
+ /** Semantic tone, or any literal CSS color (`'#ff1493'`, `'rebeccapurple'`)
15
+ * for a one-off custom tone. Named tones re-point the text and (on filled
16
+ * priorities) the surface to the matching palette; a custom color keeps
17
+ * its hue while lightness/chroma are pinned. `'neutral'` (the default) is
18
+ * the same as omitting it.
19
+ * @defaultValue 'neutral' */
20
+ tone?: "neutral" | "brand" | "info" | "success" | "warning" | "critical" | (string & {});
21
+ /** Surface emphasis. `secondary` (the default) is a subtle fill;
22
+ * `primary` is a more pronounced card; `tertiary` is transparent (the
23
+ * bare disclosure).
24
+ * @defaultValue 'secondary' */
25
+ priority?: "primary" | "secondary" | "tertiary";
26
+ /** Outdent the chevron into the left gutter so the title and body sit
27
+ * flush with surrounding content (the docs-header layout). Only takes
28
+ * effect with `priority="tertiary"` — on the filled priorities the
29
+ * container edge has to bound the chevron, so it's a no-op there. */
30
+ outdent?: boolean;
31
+ /** Header actions (e.g. buttons, tags) rendered at the end of the
32
+ * header row, OUTSIDE the toggle trigger — clicking them never
33
+ * toggles, they're separately focusable, and screen readers see them
34
+ * as separate controls. */
35
+ actions?: React.ReactNode;
36
+ /** Disables the header: not clickable or focusable, hover affordance
37
+ * off, text dimmed. The open state freezes as-is — disabling an open
38
+ * expander keeps it open. `actions` stay live; disable them
39
+ * separately if needed. */
40
+ disabled?: boolean;
41
+ /** Controlled open state. When provided, the consumer owns open/close:
42
+ * the expander only follows this prop, and clicking the summary just
43
+ * requests a change via `onStateChange` (so a toggle can be rejected by
44
+ * not updating). Leave undefined for uncontrolled. */
45
+ open?: boolean;
46
+ /** Initial open state for the uncontrolled case. */
47
+ defaultOpen?: boolean;
48
+ /** Fired before the open state changes. `event` is the cancelable
49
+ * `statechange` — call `event.preventDefault()` to veto an *uncontrolled*
50
+ * toggle (e.g. confirm before closing). `detail.next` is the requested
51
+ * open state, `detail.prev` the current one (booleans). In controlled
52
+ * mode, apply `detail.next` to `open` to accept, or do nothing to reject. */
53
+ onStateChange?: (event: CustomEvent, detail: {
54
+ next: boolean;
55
+ prev: boolean;
56
+ }) => void;
57
+ }
58
+ /**
59
+ * `<Expander>` — a collapsible disclosure section.
60
+ *
61
+ * A pure, stateless pass-through to `<a-expander>`: the element owns all
62
+ * interaction (toggle, keyboard, ARIA, animation), so the wrapper holds
63
+ * no state and grabs no ref — it only maps props to attributes (safe
64
+ * wherever the host DOM is reconciled, incl. off the UI thread).
65
+ *
66
+ * Uncontrolled (`defaultOpen`): the element owns its open state. The wrapper
67
+ * emits `default-state="open"` — never `state` — so the DOM carries no stale
68
+ * controlled attribute. Controlled (`open` + `onStateChange`): the wrapper
69
+ * emits `state="open" | "closed"`; the element treats the attribute as the
70
+ * source of truth and clicks only dispatch the cancelable `statechange` event,
71
+ * giving real controlled semantics (a consumer can reject a toggle). See
72
+ * STATEFUL-COMPONENTS.md.
73
+ */
74
+ export declare const Expander: ({ title, level, tone, priority, outdent, actions, disabled, open, defaultOpen, onStateChange, className, style, children, ...rest }: ExpanderProps) => any;
@@ -0,0 +1,53 @@
1
+ import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
+ import { nativeStateChange, toneStyle } from "../anta_helpers";
3
+ const Expander = ({
4
+ title,
5
+ level,
6
+ tone,
7
+ priority,
8
+ outdent,
9
+ actions,
10
+ disabled,
11
+ open,
12
+ defaultOpen,
13
+ onStateChange,
14
+ className,
15
+ style,
16
+ children,
17
+ ...rest
18
+ }) => {
19
+ const controlled = open !== void 0;
20
+ const computedStyle = toneStyle(tone, "--expander-tone-source", style);
21
+ 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 });
22
+ return /* @__PURE__ */ jsxs(
23
+ "a-expander",
24
+ {
25
+ state: controlled ? open ? "open" : "closed" : void 0,
26
+ "default-state": !controlled && defaultOpen ? "open" : void 0,
27
+ level: level != null ? String(level) : void 0,
28
+ tone: tone && tone !== "neutral" ? tone : void 0,
29
+ priority: priority && priority !== "secondary" ? priority : void 0,
30
+ outdent: outdent ? "" : void 0,
31
+ disabled: disabled ? "" : void 0,
32
+ onstatechange: onStateChange ? (e) => {
33
+ const { event, detail } = nativeStateChange(e);
34
+ if (detail)
35
+ onStateChange(event, {
36
+ next: detail.next === "open",
37
+ prev: detail.prev === "open"
38
+ });
39
+ } : void 0,
40
+ class: className,
41
+ style: computedStyle,
42
+ ...rest,
43
+ children: [
44
+ titleNode,
45
+ actions != null && /* @__PURE__ */ jsx("span", { slot: "actions", style: { display: "contents" }, children: actions }),
46
+ /* @__PURE__ */ jsx("a-expander-details", { children })
47
+ ]
48
+ }
49
+ );
50
+ };
51
+ export {
52
+ Expander
53
+ };
@@ -0,0 +1,159 @@
1
+ import type { BaseProps, DOMEventHandlers } from '../general_types';
2
+ import type { IconShape } from '../elements/a-icon.shapes';
3
+ /** Convenience snapshot passed as the 2nd argument to `onAnyChange`. */
4
+ export interface InputChangeAttrs {
5
+ /** Current value. */
6
+ value: string;
7
+ /** The field's `name` — handy for keyed updates: `s => ({ ...s, [name]: value })`.
8
+ * The one caller-provided field carried here, for that pattern; read anything
9
+ * else (`id`, `type`, `className`) off `event.target`. */
10
+ name?: string;
11
+ /** `true` when the value is empty. */
12
+ empty: boolean;
13
+ /** Whether the field currently passes validation (native + `error`). `undefined`
14
+ * where `ElementInternals` is unsupported. */
15
+ valid?: boolean;
16
+ /** Current validation message (`''` when valid). */
17
+ validationMessage: string;
18
+ }
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 just 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. */
25
+ 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). */
30
+ label?: React.ReactNode;
31
+ /** Message below the field. Neutral helper text by default; `status` recolors
32
+ * it and prefixes the matching glyph. */
33
+ 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. */
38
+ 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. */
43
+ statusIcon?: IconShape | (string & {}) | false;
44
+ /** Custom accent colour — any literal CSS colour tints the resting + hover
45
+ * border (focus ring stays the global `--focus-ring`). For consistency with the
46
+ * other controls' custom-tone knob; a `status` still overrides for validation. */
47
+ tone?: string;
48
+ /** Size variant. small=24px, medium=28px, large=32px tall; the type scale and
49
+ * icon track the size (small 13/16 + 14px icon · medium 15/20 + 16px ·
50
+ * large 17/24 + 18px).
51
+ * @defaultValue medium */
52
+ size?: 'small' | 'medium' | 'large';
53
+ /** Controlled value. Pair with `onChange` / `onInput`. */
54
+ value?: string;
55
+ /** Initial value for the uncontrolled case. */
56
+ defaultValue?: string;
57
+ /** Render a `<textarea>` instead of an `<input>`. Without `rows` it grows
58
+ * with its content from one line (capped by `maxRows` if set). Autogrow uses
59
+ * CSS `field-sizing` where supported (Chrome/Edge, Safari ≥ 26.2) and falls
60
+ * back to a built-in JS resize elsewhere (Firefox, older Safari), so it grows
61
+ * in every browser. */
62
+ multiline?: boolean;
63
+ /** Fixed visible row count — a constant-height `<textarea>` (implies
64
+ * `multiline`). */
65
+ rows?: number;
66
+ /** Cap the autogrow height (in rows) of a `multiline` field with no `rows`.
67
+ * Omit for unbounded growth. */
68
+ maxRows?: number;
69
+ /** Show a clear button as the first trailing item once the field has a
70
+ * value. */
71
+ clearable?: boolean;
72
+ /** Content pinned to the start of the field (e.g. an icon). */
73
+ leading?: React.ReactNode;
74
+ /** Content pinned to the end of the field (e.g. icons, buttons), after the
75
+ * clear button when `clearable`. */
76
+ trailing?: React.ReactNode;
77
+ /** Single-line input type. Ignored when `multiline`. (`search` is omitted
78
+ * deliberately — it triggers browser-injected clear/search affordances.)
79
+ * @defaultValue text */
80
+ type?: 'text' | 'email' | 'password' | 'tel' | 'url' | 'number';
81
+ /** Native autocomplete token. Overrides the value derived from `type`
82
+ * (`email` / `tel` / `url`) — set it for the cases `type` can't express, e.g.
83
+ * `username`, `current-password`, `new-password`, `one-time-code`, or `off`. */
84
+ autoComplete?: 'off' | 'on' | 'name' | 'username' | 'email' | 'current-password' | 'new-password' | 'one-time-code' | 'tel' | 'url' | (string & {});
85
+ /** Virtual-keyboard hint. Overrides the value derived from `type`. */
86
+ inputMode?: 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url';
87
+ /** Form field name — submitted with the form via ElementInternals. */
88
+ name?: string;
89
+ /** Placeholder shown when empty. */
90
+ placeholder?: string;
91
+ /** Disable the field. */
92
+ disabled?: boolean;
93
+ /** Make the field read-only. */
94
+ readOnly?: boolean;
95
+ /** Mark the field required (drives native validity). */
96
+ required?: boolean;
97
+ /** Dim the `leading` / `trailing` adornments at rest; they brighten to full
98
+ * when the field is hovered or focused (a quiet-until-engaged affordance for
99
+ * trailing actions). */
100
+ dimActions?: boolean;
101
+ /** Toggle native spell-checking. */
102
+ spellCheck?: boolean;
103
+ /** Max input length. */
104
+ maxLength?: number;
105
+ /** Min input length. */
106
+ minLength?: number;
107
+ /** Validation pattern (single-line). */
108
+ pattern?: string;
109
+ /** Min / max / step — for `type="number"`. */
110
+ min?: number | string;
111
+ max?: number | string;
112
+ step?: number | string;
113
+ /** Fires on every keystroke. Read `e.target.value`. */
114
+ onInput?: (e: any) => void;
115
+ /** Fires on **commit** (blur / Enter) — the platform `change` semantics, **not**
116
+ * React's per-keystroke `onChange`. This is a web component, so `onChange` keeps
117
+ * the native meaning; reach for `onInput` (every keystroke) or `onAnyChange`
118
+ * (both) for live updates. Read `e.target.value`. */
119
+ onChange?: (e: any) => void;
120
+ /** Unified value-change handler — the easy path for state. Fires on `input`
121
+ * *and* `change` (and on clear), with the native `event` plus a convenience
122
+ * `attrs` snapshot (`value`, `name`, `empty`, `valid`, `validationMessage`) so
123
+ * you can do `setForm(s => ({ ...s, [attrs.name]: attrs.value }))` without
124
+ * digging into the event. Use `event.type` to tell a live edit (`input`) from
125
+ * a commit (`change`); read `id` / `type` / `className` off `event.target`. */
126
+ onAnyChange?: (event: any, attrs: InputChangeAttrs) => void;
127
+ /** Fires when the built-in clear button (`clearable`) is clicked, *before*
128
+ * the field is cleared. Call `e.preventDefault()` to keep the current value
129
+ * — the clear is cancelled and `onClearInput` won't fire. Backed by the
130
+ * element's cancelable, bubbling `clearclick` event. */
131
+ onClearClick?: (e: CustomEvent) => void;
132
+ /** Fires after the built-in clear button (`clearable`) has cleared the field
133
+ * — so `onInput` / `onChange` fire too — making this useful for reacting
134
+ * specifically to a clear. Doesn't fire if `onClearClick` cancelled the
135
+ * clear. Backed by the element's bubbling `clearinput` event. */
136
+ onClearInput?: (e: CustomEvent) => void;
137
+ /** Fires when the field gains focus. */
138
+ onFocus?: (e: any) => void;
139
+ /** Fires when the field loses focus. */
140
+ onBlur?: (e: any) => void;
141
+ }
142
+ /**
143
+ * `<Input>` — a text field. Renders an `<a-input>` web component whose real
144
+ * `<input>` / `<textarea>` lives in shadow DOM, so it's self-contained: focus,
145
+ * forms (via `ElementInternals`), IME, and autofill are all native, and the
146
+ * control is reachable for styling through `::part(input)`.
147
+ *
148
+ * The wrapper is stateless — it maps props to attributes and slots. The clear
149
+ * button is dropped into `slot="trailing"`; its click finds the host and calls
150
+ * `clear()`, and its visibility is CSS off the element's `:state(filled)`.
151
+ *
152
+ * Requires `@antadesign/anta/elements` to be imported (client-side only).
153
+ *
154
+ * @example
155
+ * ```tsx
156
+ * <Input label="Email" type="email" placeholder="you@example.com" clearable />
157
+ * ```
158
+ */
159
+ export declare const Input: ({ label, hint, status, statusIcon, tone, size, value, defaultValue, multiline, rows, maxRows, clearable, leading, trailing, type, autoComplete, inputMode, name, placeholder, disabled, readOnly, required, dimActions, spellCheck, maxLength, minLength, pattern, min, max, step, onInput, onChange, onAnyChange, onClearClick, onClearInput, children, className, style, ...rest }: InputProps) => any;