@antadesign/anta 0.2.2 → 0.3.1
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.
- package/README.md +14 -0
- package/dist/anta_helpers.d.ts +39 -1
- package/dist/anta_helpers.js +30 -2
- package/dist/components/Button.d.ts +7 -4
- package/dist/components/Button.js +6 -11
- package/dist/components/Checkbox.d.ts +97 -0
- package/dist/components/Checkbox.js +77 -0
- package/dist/components/Expander.d.ts +74 -0
- package/dist/components/Expander.js +53 -0
- package/dist/components/Input.d.ts +159 -0
- package/dist/components/Input.js +150 -0
- package/dist/components/Menu.d.ts +73 -0
- package/dist/components/Menu.js +42 -0
- package/dist/components/MenuGroup.d.ts +24 -0
- package/dist/components/MenuGroup.js +19 -0
- package/dist/components/MenuItem.d.ts +61 -0
- package/dist/components/MenuItem.js +50 -0
- package/dist/components/MenuSeparator.d.ts +14 -0
- package/dist/components/MenuSeparator.js +7 -0
- package/dist/components/Progress.d.ts +12 -6
- package/dist/components/Progress.js +7 -4
- package/dist/components/Radio.d.ts +37 -0
- package/dist/components/Radio.js +33 -0
- package/dist/components/RadioGroup.d.ts +119 -0
- package/dist/components/RadioGroup.js +108 -0
- package/dist/components/Tag.d.ts +38 -5
- package/dist/components/Tag.js +9 -5
- package/dist/components/Text.d.ts +27 -12
- package/dist/components/Text.js +6 -3
- package/dist/components/Title.d.ts +10 -1
- package/dist/elements/a-button.css +1 -1
- package/dist/elements/a-button.d.ts +56 -0
- package/dist/elements/a-button.js +13 -11
- package/dist/elements/a-checkbox.css +1 -0
- package/dist/elements/a-checkbox.d.ts +52 -0
- package/dist/elements/a-checkbox.js +130 -0
- package/dist/elements/a-expander.css +1 -0
- package/dist/elements/a-expander.d.ts +28 -0
- package/dist/elements/a-expander.js +237 -0
- package/dist/elements/a-icon.d.ts +14 -0
- package/dist/elements/a-icon.shapes.css +1 -1
- package/dist/elements/a-icon.shapes.d.ts +10 -1
- package/dist/elements/a-icon.shapes.js +11 -1
- package/dist/elements/a-input.css +1 -0
- package/dist/elements/a-input.d.ts +68 -0
- package/dist/elements/a-input.js +511 -0
- package/dist/elements/a-menu-group.css +1 -0
- package/dist/elements/a-menu-group.d.ts +13 -0
- package/dist/elements/a-menu-group.js +15 -0
- package/dist/elements/a-menu-item.css +1 -0
- package/dist/elements/a-menu-item.d.ts +47 -0
- package/dist/elements/a-menu-item.js +30 -0
- package/dist/elements/a-menu-separator.css +1 -0
- package/dist/elements/a-menu-separator.d.ts +13 -0
- package/dist/elements/a-menu-separator.js +15 -0
- package/dist/elements/a-menu.css +1 -0
- package/dist/elements/a-menu.d.ts +183 -0
- package/dist/elements/a-menu.js +763 -0
- package/dist/elements/a-progress.css +1 -1
- package/dist/elements/a-progress.d.ts +12 -0
- package/dist/elements/a-progress.js +1 -0
- package/dist/elements/a-radio-group.css +1 -0
- package/dist/elements/a-radio-group.d.ts +33 -0
- package/dist/elements/a-radio-group.js +160 -0
- package/dist/elements/a-radio.css +1 -0
- package/dist/elements/a-radio.d.ts +14 -0
- package/dist/elements/a-radio.js +46 -0
- package/dist/elements/a-tag.css +1 -1
- package/dist/elements/a-text.css +1 -1
- package/dist/elements/a-text.d.ts +42 -3
- package/dist/elements/a-text.js +73 -33
- package/dist/elements/a-tooltip.d.ts +43 -11
- package/dist/elements/a-tooltip.js +46 -51
- package/dist/elements/index.d.ts +9 -0
- package/dist/elements/index.js +27 -0
- package/dist/general_types.d.ts +468 -15
- package/dist/index.d.ts +16 -0
- package/dist/index.js +16 -0
- package/dist/jsx-runtime.d.ts +42 -7
- package/dist/jsx-runtime.js +14 -2
- package/dist/tokens.css +1 -1
- 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).
|
package/dist/anta_helpers.d.ts
CHANGED
|
@@ -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
|
-
|
|
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 {};
|
package/dist/anta_helpers.js
CHANGED
|
@@ -1,9 +1,37 @@
|
|
|
1
1
|
function hasChildren(children) {
|
|
2
2
|
return Array.isArray(children) ? children.length > 0 : children != null;
|
|
3
3
|
}
|
|
4
|
-
|
|
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
|
-
|
|
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' | '
|
|
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>`
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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;
|