@antadesign/anta 0.3.21 → 0.3.23

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 (75) hide show
  1. package/dist/avatar-core.d.ts +160 -0
  2. package/dist/avatar-core.js +271 -0
  3. package/dist/bundle.css +1 -0
  4. package/dist/bundle.d.ts +11 -0
  5. package/dist/bundle.js +1239 -0
  6. package/dist/components/Avatar.d.ts +63 -0
  7. package/dist/components/Avatar.js +29 -0
  8. package/dist/components/Button.d.ts +1 -1
  9. package/dist/components/Button.js +12 -5
  10. package/dist/components/Checkbox.js +6 -2
  11. package/dist/components/Input.d.ts +2 -1
  12. package/dist/components/Input.js +1 -1
  13. package/dist/components/InputDate.js +1 -0
  14. package/dist/components/Loader.d.ts +5 -1
  15. package/dist/components/Loader.js +2 -1
  16. package/dist/components/Menu.d.ts +6 -1
  17. package/dist/components/Menu.js +2 -0
  18. package/dist/components/RadioGroup.js +21 -5
  19. package/dist/components/Select.d.ts +3 -1
  20. package/dist/components/Select.js +1 -0
  21. package/dist/components/SelectFaceted.d.ts +4 -2
  22. package/dist/components/Slider.d.ts +1 -1
  23. package/dist/components/Steps.css +1 -0
  24. package/dist/components/Steps.d.ts +93 -0
  25. package/dist/components/Steps.js +128 -0
  26. package/dist/components/Tabs.d.ts +10 -5
  27. package/dist/components/Tabs.js +5 -2
  28. package/dist/components/Tooltip.d.ts +5 -5
  29. package/dist/elements/a-avatar.css +1 -0
  30. package/dist/elements/a-avatar.d.ts +31 -0
  31. package/dist/elements/a-avatar.js +98 -0
  32. package/dist/elements/a-checkbox.css +1 -1
  33. package/dist/elements/a-icon.shapes.css +1 -1
  34. package/dist/elements/a-icon.shapes.d.ts +1 -8
  35. package/dist/elements/a-icon.shapes.js +0 -14
  36. package/dist/elements/a-input-time.css +1 -1
  37. package/dist/elements/a-input-time.js +1 -1
  38. package/dist/elements/a-input.css +1 -1
  39. package/dist/elements/a-input.js +1 -1
  40. package/dist/elements/a-loader.css +1 -1
  41. package/dist/elements/a-menu.d.ts +4 -2
  42. package/dist/elements/a-menu.js +122 -4
  43. package/dist/elements/a-radio.css +1 -1
  44. package/dist/elements/a-slider.css +1 -1
  45. package/dist/elements/a-switch.css +1 -1
  46. package/dist/elements/a-tabs.css +1 -1
  47. package/dist/elements/a-tooltip.d.ts +4 -4
  48. package/dist/elements/a-tooltip.js +18 -15
  49. package/dist/elements/index.d.ts +1 -0
  50. package/dist/elements/index.js +3 -0
  51. package/dist/general_types.d.ts +42 -0
  52. package/dist/index.d.ts +6 -0
  53. package/dist/index.js +28 -0
  54. package/dist/jsx-runtime.d.ts +6 -1
  55. package/dist/theme-anta.css +1 -1
  56. package/docs/changelog.md +68 -0
  57. package/docs/comparison.md +2 -2
  58. package/docs/components/banner.md +1 -1
  59. package/docs/components/button.md +3 -3
  60. package/docs/components/card.md +1 -1
  61. package/docs/components/expander.md +1 -1
  62. package/docs/components/input.md +3 -3
  63. package/docs/components/loader.md +1 -0
  64. package/docs/components/menu.md +34 -1
  65. package/docs/components/radio.md +1 -1
  66. package/docs/components/select-faceted.md +4 -2
  67. package/docs/components/select.md +3 -1
  68. package/docs/components/slider.md +1 -1
  69. package/docs/components/steps.md +324 -0
  70. package/docs/components/tabs.md +24 -8
  71. package/docs/components/tag.md +3 -3
  72. package/docs/components/tooltip.md +5 -5
  73. package/docs/index.md +1 -0
  74. package/docs/install-config.md +17 -0
  75. package/package.json +9 -3
@@ -0,0 +1,63 @@
1
+ import type { BaseProps } from "../general_types";
2
+ import type { AvatarGenConfig } from "../avatar-core";
3
+ export interface AvatarProps extends Omit<BaseProps, 'children'> {
4
+ /** Alphanumeric seed that deterministically drives the generated userpic. The
5
+ * same seed always renders the same avatar. Falls back to `name` when omitted,
6
+ * so a stable user id or name is a good value. */
7
+ seed?: string;
8
+ /** Person or entity name. Supplies the initials fallback (up to 3 letters from
9
+ * the first 3 words) and the accessible name, and seeds generation when `seed`
10
+ * is absent. */
11
+ name?: string;
12
+ /** Image URL. When set, the image is shown instead of a generated userpic,
13
+ * cropped to fill the container. */
14
+ src?: string;
15
+ /** Size of the square container. A number is a pixel size.
16
+ * @defaultValue medium */
17
+ size?: 'small' | 'medium' | 'large' | number;
18
+ /** Corner badge, colored by tone. Pass a named tone or any literal CSS color;
19
+ * the application decides what each tone means (`success` for online,
20
+ * `critical` for busy, `neutral` for offline). Omit for no badge. */
21
+ badge?: 'neutral' | 'brand' | 'info' | 'success' | 'warning' | 'critical' | (string & {});
22
+ /** Fully-round (circular) frame. Pass a `number` (px) or a CSS length string
23
+ * for a custom radius instead. */
24
+ round?: boolean | number | string;
25
+ /** Brand generation constraints — each dimension is OFF / ANY / RANGE / LIST.
26
+ * Colors cap OKLCH ranges or pass an explicit palette. Omit for the default
27
+ * varied figure. Define one config for the app and reuse it across avatars. */
28
+ generator?: AvatarGenConfig;
29
+ }
30
+ /**
31
+ * Avatar — a userpic container. Shows a supplied image, a seed-generated
32
+ * placeholder figure, or initials, in that priority order.
33
+ *
34
+ * Renders an `<a-avatar>` web component. Generation runs in `avatar-core`; pass a
35
+ * `generator` config to cap the space to your brand (see the color and dimension
36
+ * options on `AvatarGenConfig`). With no `generator`, a varied figure is drawn;
37
+ * a config whose head and body shapes are both `off` renders initials instead.
38
+ *
39
+ * Requires `@antadesign/anta/elements` to be imported (client-side only) to
40
+ * register the underlying custom element.
41
+ *
42
+ * @example Generated userpic
43
+ * ```tsx
44
+ * import { Avatar } from '@antadesign/anta'
45
+ * import '@antadesign/anta/elements'
46
+ *
47
+ * <Avatar seed="user-42" name="Vlad Korobov" />
48
+ * ```
49
+ *
50
+ * @example Image with a badge
51
+ * ```tsx
52
+ * <Avatar src="/vlad.jpg" name="Vlad Korobov" badge="success" />
53
+ * ```
54
+ *
55
+ * @example Initials (no figure shape configured)
56
+ * ```tsx
57
+ * <Avatar
58
+ * name="Vlad Korobov"
59
+ * generator={{ headRadiusTop: { mode: 'off' }, headRadiusBottom: { mode: 'off' }, bodyBorderRadius: { mode: 'off' } }}
60
+ * />
61
+ * ```
62
+ */
63
+ export declare const Avatar: ({ seed, name, src, size, badge, round, generator, className, style, ...rest }: AvatarProps) => any;
@@ -0,0 +1,29 @@
1
+ import { jsx } from "@antadesign/anta/jsx-runtime";
2
+ import { lengthStyle, roundStyle, roundAttr, toneStyle } from "../anta_helpers";
3
+ const Avatar = ({ seed, name, src, size, badge, round, generator, className, style, ...rest }) => {
4
+ const numericSize = typeof size === "number";
5
+ const sized = numericSize ? lengthStyle(size, "--avatar-size", style) : style;
6
+ const rounded = roundStyle(round, "--avatar-radius", sized);
7
+ const mergedStyle = toneStyle(badge, "--avatar-badge-tone-source", rounded);
8
+ return /* @__PURE__ */ jsx(
9
+ "a-avatar",
10
+ {
11
+ seed,
12
+ name,
13
+ src,
14
+ size: !numericSize && size && size !== "medium" ? size : void 0,
15
+ badge,
16
+ round: roundAttr(round),
17
+ config: generator ? JSON.stringify(generator) : void 0,
18
+ role: name ? "img" : void 0,
19
+ "aria-label": name || void 0,
20
+ "aria-hidden": name ? void 0 : "true",
21
+ class: className,
22
+ style: mergedStyle,
23
+ ...rest
24
+ }
25
+ );
26
+ };
27
+ export {
28
+ Avatar
29
+ };
@@ -135,4 +135,4 @@ export type ButtonProps = BaseButtonProps & PriorityMode & ContentMode & SubmitM
135
135
  * <Button href="/docs" target="_blank" label="Read the docs" />
136
136
  * ```
137
137
  */
138
- export declare const Button: ({ priority, tone, underline, icon, iconTrailing, paddingless, label, size, loading, disabled, selected, round, href, target, rel, download, ping, type, form, className, style, children, ...rest }: ButtonProps) => any;
138
+ export declare const Button: ({ priority, tone, underline, icon, iconTrailing, paddingless, label, size, loading, disabled, selected, round, onClick, href, target, rel, download, ping, type, form, className, style, children, ...rest }: ButtonProps) => any;
@@ -13,6 +13,7 @@ const Button = ({
13
13
  disabled,
14
14
  selected,
15
15
  round,
16
+ onClick,
16
17
  href,
17
18
  target,
18
19
  rel,
@@ -26,6 +27,7 @@ const Button = ({
26
27
  ...rest
27
28
  }) => {
28
29
  const toneAttr = tone || void 0;
30
+ const unavailable = disabled || loading;
29
31
  const computedStyle = roundStyle(round, "--button-round", toneStyle(toneAttr, "--button-tone-source", style));
30
32
  const isIconOnly = icon != null && label == null && children == null && iconTrailing == null;
31
33
  const sharedAttrs = {
@@ -53,8 +55,8 @@ const Button = ({
53
55
  // Disabled AND loading both leave the keyboard tab order — a loading
54
56
  // button blocks the mouse (pointer-events), so it must block Enter/Space
55
57
  // activation too, else the loading guard would be mouse-only.
56
- tabIndex: disabled || loading ? -1 : 0,
57
- "aria-disabled": disabled || loading ? "true" : void 0,
58
+ tabIndex: unavailable ? -1 : 0,
59
+ "aria-disabled": unavailable ? "true" : void 0,
58
60
  "aria-busy": loading ? "true" : void 0,
59
61
  "aria-pressed": selected ? "true" : void 0,
60
62
  // Icon-only buttons get an accessible name from the icon shape. Consumer's
@@ -62,7 +64,8 @@ const Button = ({
62
64
  // ButtonCopy uses to name an icon-only copy button "Copy".
63
65
  "aria-label": isIconOnly ? icon : void 0,
64
66
  class: className,
65
- style: computedStyle
67
+ style: computedStyle,
68
+ onClick: unavailable ? void 0 : onClick
66
69
  };
67
70
  const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
68
71
  icon && /* @__PURE__ */ jsx("a-icon", { shape: icon, "aria-hidden": "true" }),
@@ -71,16 +74,20 @@ const Button = ({
71
74
  iconTrailing && /* @__PURE__ */ jsx("a-icon", { shape: iconTrailing, "aria-hidden": "true" })
72
75
  ] });
73
76
  if (href != null) {
77
+ const anchorAttrs = {
78
+ ...sharedAttrs,
79
+ disabled: unavailable || void 0
80
+ };
74
81
  return /* @__PURE__ */ jsx(
75
82
  "a",
76
83
  {
77
- href,
84
+ href: unavailable ? void 0 : href,
78
85
  "data-anta": "",
79
86
  target,
80
87
  rel,
81
88
  download,
82
89
  ping,
83
- ...sharedAttrs,
90
+ ...anchorAttrs,
84
91
  ...rest,
85
92
  children: inner
86
93
  }
@@ -33,7 +33,11 @@ const Checkbox = ({
33
33
  toneStyle(
34
34
  toneSelected,
35
35
  "--checkbox-tone-source",
36
- toneStyle(tone, "--checkbox-tone-source", style)
36
+ toneStyle(
37
+ tone,
38
+ "--checkbox-off-tone-source",
39
+ toneSelected == null ? toneStyle(tone, "--checkbox-tone-source", style) : style
40
+ )
37
41
  )
38
42
  );
39
43
  const explicitAriaLabel = rest["aria-label"];
@@ -64,7 +68,7 @@ const Checkbox = ({
64
68
  "default-state": defaultStateAttr,
65
69
  disabled: disabled ? "" : void 0,
66
70
  tone: tone && tone !== "neutral" ? tone : void 0,
67
- "tone-selected": toneSelected && toneSelected !== "neutral" ? toneSelected : void 0,
71
+ "tone-selected": toneSelected || void 0,
68
72
  size: size && size !== "medium" ? size : void 0,
69
73
  round: roundAttr(round),
70
74
  tabIndex: disabled ? -1 : tabIndex ?? 0,
@@ -93,7 +93,8 @@ export interface InputProps extends BaseProps, DOMEventHandlers {
93
93
  /** Placeholder shown when empty. */
94
94
  placeholder?: string;
95
95
  /** Ellipsize an overflowing single-line value. Read-only inputs already do
96
- * this; use `truncate` when an editable field should keep the same treatment. */
96
+ * this; pass `false` when an editable field should show the full value.
97
+ * @defaultValue true */
97
98
  truncate?: boolean;
98
99
  /** Disable the field. */
99
100
  disabled?: boolean;
@@ -51,7 +51,7 @@ const Input = ({
51
51
  inputMode,
52
52
  name,
53
53
  placeholder,
54
- truncate,
54
+ truncate = true,
55
55
  disabled,
56
56
  readOnly,
57
57
  required,
@@ -129,6 +129,7 @@ const InputDate = ({
129
129
  placeholder: pattern,
130
130
  inputMode: time ? "text" : "numeric",
131
131
  autoComplete: "off",
132
+ role: "combobox",
132
133
  "aria-haspopup": "dialog",
133
134
  "aria-expanded": open ? "true" : "false",
134
135
  onInput: (e) => {
@@ -1,5 +1,9 @@
1
1
  import type { BaseProps } from '../general_types';
2
+ /** Named Anta color tone for a loader. */
3
+ export type LoaderTone = 'neutral' | 'brand' | 'info' | 'success' | 'warning' | 'critical';
2
4
  export interface LoaderProps extends BaseProps {
5
+ /** Color tone for the loader. Omit it to use the standard text-3 color. */
6
+ tone?: LoaderTone;
3
7
  /** Current progress value. Omit this prop, or pass `false`, for an
4
8
  * indeterminate rotating gradient. */
5
9
  value?: number | false;
@@ -36,4 +40,4 @@ export interface LoaderProps extends BaseProps {
36
40
  * <Loader value={42} label="Uploading files" />
37
41
  * ```
38
42
  */
39
- export declare const Loader: ({ value, max, size, speed, label, className, style, ...rest }: LoaderProps) => any;
43
+ export declare const Loader: ({ tone, value, max, size, speed, label, className, style, ...rest }: LoaderProps) => any;
@@ -1,5 +1,5 @@
1
1
  import { jsx } from "@antadesign/anta/jsx-runtime";
2
- const Loader = ({ value, max = 100, size, speed, label, className, style, ...rest }) => {
2
+ const Loader = ({ tone, value, max = 100, size, speed, label, className, style, ...rest }) => {
3
3
  const determinate = typeof value === "number" && Number.isFinite(value);
4
4
  const safeMax = Number.isFinite(max) && max > 0 ? max : 100;
5
5
  const clampedValue = determinate ? Math.min(safeMax, Math.max(0, value)) : void 0;
@@ -20,6 +20,7 @@ const Loader = ({ value, max = 100, size, speed, label, className, style, ...res
20
20
  return /* @__PURE__ */ jsx(
21
21
  "a-loader",
22
22
  {
23
+ tone,
23
24
  ...determinate ? { value: clampedValue } : {},
24
25
  class: className,
25
26
  style: computedStyle,
@@ -43,6 +43,11 @@ export interface MenuProps extends BaseProps {
43
43
  * fully round. A `number` (px) or CSS length string tunes the container radius
44
44
  * only — items stay full pills. */
45
45
  round?: boolean | number | string;
46
+ /** Contain events so they don't bubble out of the menu to ancestor handlers
47
+ * (e.g. a clickable row the menu sits in). Selecting an item is always
48
+ * contained; this extends it to whole event types. `true` contains `click`;
49
+ * pass an event name or list (`"click pointerdown"`) to contain those. */
50
+ stopPropagation?: boolean | string | string[];
46
51
  /** Fired before the open state changes — on open, and on every dismiss (Esc,
47
52
  * outside-click, scroll, selecting an item). `event` is the cancelable
48
53
  * `statechange`; `detail.next`/`detail.prev` are the requested/previous open
@@ -96,4 +101,4 @@ export interface MenuProps extends BaseProps {
96
101
  * </Menu>
97
102
  * ```
98
103
  */
99
- export declare const Menu: ({ placement, context, coord, nohover, offset, autoWidth, inset, open, onStateChange, round, role, className, style, children, ...rest }: MenuProps) => any;
104
+ export declare const Menu: ({ placement, context, coord, nohover, offset, autoWidth, inset, open, onStateChange, round, stopPropagation, role, className, style, children, ...rest }: MenuProps) => any;
@@ -11,6 +11,7 @@ const Menu = ({
11
11
  open,
12
12
  onStateChange,
13
13
  round,
14
+ stopPropagation,
14
15
  role = "menu",
15
16
  className,
16
17
  style,
@@ -36,6 +37,7 @@ const Menu = ({
36
37
  });
37
38
  } : void 0,
38
39
  round: roundAttr(round),
40
+ "stop-propagation": stopPropagation === void 0 || stopPropagation === false ? void 0 : stopPropagation === true ? "" : Array.isArray(stopPropagation) ? stopPropagation.join(" ") : stopPropagation,
39
41
  role,
40
42
  "aria-orientation": "vertical",
41
43
  class: className,
@@ -64,7 +64,7 @@ const RadioGroup = ({
64
64
  name,
65
65
  status: status && status !== "neutral" ? status : void 0,
66
66
  tone: tone && tone !== "neutral" ? tone : void 0,
67
- "tone-selected": toneSelected && toneSelected !== "neutral" ? toneSelected : void 0,
67
+ "tone-selected": toneSelected || void 0,
68
68
  size: size && size !== "medium" ? size : void 0,
69
69
  disabled: disabled ? "" : void 0,
70
70
  orientation: orientation && orientation !== "vertical" ? orientation : void 0,
@@ -73,7 +73,15 @@ const RadioGroup = ({
73
73
  onfocusin: onFocus,
74
74
  onfocusout: onBlur,
75
75
  class: className,
76
- style: toneStyle(toneSelected, "--radio-tone-source", toneStyle(tone, "--radio-tone-source", style)),
76
+ style: toneStyle(
77
+ toneSelected,
78
+ "--radio-tone-source",
79
+ toneStyle(
80
+ tone,
81
+ "--radio-off-tone-source",
82
+ toneSelected == null ? toneStyle(tone, "--radio-tone-source", style) : style
83
+ )
84
+ ),
77
85
  children: [
78
86
  label && /* @__PURE__ */ jsx("a-radio-group-label", { id: labelId, children: label }),
79
87
  hint && /* @__PURE__ */ jsx("a-radio-group-hint", { id: hintId, children: hint }),
@@ -88,12 +96,20 @@ const RadioGroup = ({
88
96
  value: o.value,
89
97
  "aria-disabled": optDisabled ? "true" : void 0,
90
98
  tabIndex: o.value === tabStopValue ? 0 : -1,
91
- tone: o.tone && o.tone !== "neutral" ? o.tone : void 0,
92
- "tone-selected": o.toneSelected && o.toneSelected !== "neutral" ? o.toneSelected : void 0,
99
+ tone: o.tone || void 0,
100
+ "tone-selected": o.toneSelected || void 0,
93
101
  size: o.size && o.size !== "medium" ? o.size : void 0,
94
102
  disabled: o.disabled ? "" : void 0,
95
103
  class: optionClassName,
96
- style: toneStyle(o.toneSelected, "--radio-tone-source", toneStyle(o.tone, "--radio-tone-source", optionStyle)),
104
+ style: toneStyle(
105
+ o.toneSelected,
106
+ "--radio-tone-source",
107
+ toneStyle(
108
+ o.tone,
109
+ "--radio-off-tone-source",
110
+ o.toneSelected == null ? toneStyle(o.tone, "--radio-tone-source", optionStyle) : optionStyle
111
+ )
112
+ ),
97
113
  children: [
98
114
  /* @__PURE__ */ jsx("a-radio-label", { children: o.label }),
99
115
  o.hint != null && /* @__PURE__ */ jsx("a-radio-hint", { children: o.hint })
@@ -248,7 +248,9 @@ export interface SelectCommonProps<V extends OptionValue = string> extends Omit<
248
248
  * one focusable element, such as an Anta `Button`. The menu is positioned
249
249
  * relative to that element and opens when it is clicked. Do not return a
250
250
  * fragment, multiple siblings, or a non-focusable wrapper. Add
251
- * `aria-haspopup="menu"` and `aria-expanded={state.open}` to the element.
251
+ * `aria-haspopup="menu"` and `aria-expanded={state.open}` to the element, on a
252
+ * role that supports them (an Anta `Button` already carries `role="button"`;
253
+ * otherwise add `role="combobox"`).
252
254
  * Field props (`label`, `hint`, `size`, `status`, `placeholder`, and `round`) and
253
255
  * `className` / `style` apply only to the default field. Add styling and
254
256
  * attributes to the returned element instead. */
@@ -270,6 +270,7 @@ const Select = (props) => {
270
270
  status,
271
271
  statusIcon,
272
272
  round,
273
+ role: "combobox",
273
274
  "aria-haspopup": "menu",
274
275
  "aria-expanded": open ? "true" : "false",
275
276
  trailing: (
@@ -165,8 +165,10 @@ export interface SelectFacetedProps extends Omit<BaseProps, 'children'> {
165
165
  * Receives a `SelectFacetedTriggerState`. Return exactly one focusable element:
166
166
  * the menu is positioned relative to that element and opens when it is clicked.
167
167
  * Add `aria-haspopup="menu"` and `aria-expanded={state.open}` to the returned
168
- * element. `className`, `style`, and other trigger props apply only to the
169
- * default Button, so add styling and attributes to the returned element. */
168
+ * element, on a role that supports them (an Anta `Button` already carries
169
+ * `role="button"`; otherwise add `role="combobox"`). `className`, `style`, and
170
+ * other trigger props apply only to the default Button, so add styling and
171
+ * attributes to the returned element. */
170
172
  renderTrigger?: (state: SelectFacetedTriggerState) => React.ReactNode;
171
173
  }
172
174
  /**
@@ -52,7 +52,7 @@ export interface SliderProps extends BaseProps, DOMEventHandlers {
52
52
  trackSize?: number | string;
53
53
  /** Diameter of the thumb. Numbers use pixels; strings are CSS lengths. Keep
54
54
  * it at least as large as `trackSize`.
55
- * @defaultValue 18 */
55
+ * @defaultValue 16 */
56
56
  thumbSize?: number | string;
57
57
  /** Fill the thumb with its resolved border color. This follows `thumbTone`
58
58
  * and interactive states.
@@ -0,0 +1 @@
1
+ @layer anta{a-steps{--steps-marker-size: 28px;--steps-icon-size: 16px;--steps-marker-border-width: 1.5px;--steps-marker-rest-border: var(--border-4);--steps-item-gap: 6px;--steps-gap: 4px;--steps-connector-color: var(--border-3);--steps-connector-width: 1px;--steps-complete-connector-color: var(--border-2);--steps-tab-padding-x: 9px;--steps-tab-padding-block: 0px;--steps-hint-leading: 16px;display:flex;flex-direction:column;width:100%;min-width:0}a-steps:has(>a-tabs[size=small]){--steps-marker-size: 24px;--steps-icon-size: 14px;--steps-tab-padding-x: 7px;--steps-hint-leading: 14px}a-steps:has(>a-tabs[size=large]){--steps-marker-size: 32px;--steps-icon-size: 18px;--steps-tab-padding-x: 13px;--steps-hint-leading: 20px}a-tabs[data-steps]{--steps-marker-active-border: var(--border-1);--steps-primary-marker-lightness: .5;--steps-primary-marker-chroma: .02;--steps-primary-marker-fg: #ffffff;--tabs-gap: 0px;align-items:center;width:fit-content;align-self:flex-start;overflow-x:auto;scrollbar-width:thin}a-tabs[data-steps][fill]{width:100%;align-self:stretch;justify-content:space-between}a-tabs[data-steps]>a-tab[tone=brand]{--steps-marker-active-border: var(--border-1-brand)}a-tabs[data-steps]>a-tab[tone=info]{--steps-marker-active-border: var(--border-1-info)}a-tabs[data-steps]>a-tab[tone=success]{--steps-marker-active-border: var(--border-1-success)}a-tabs[data-steps]>a-tab[tone=warning]{--steps-marker-active-border: var(--border-1-warning)}a-tabs[data-steps]>a-tab[tone=critical]{--steps-marker-active-border: var(--border-1-critical)}a-tabs[data-steps]>a-tab[tone=neutral]{--steps-marker-active-border: var(--border-1)}a-tabs[data-steps]>a-tab[tone]:not([tone=""],[tone=neutral]){--steps-primary-marker-chroma: .17}.dark a-tabs[data-steps]{--steps-primary-marker-lightness: .4;--steps-primary-marker-chroma: .015}.dark a-tabs[data-steps]>a-tab[tone]:not([tone=""],[tone=neutral]){--steps-primary-marker-chroma: .16}a-tabs[data-steps]:not([priority=secondary],[priority=tertiary]){--steps-marker-border-width: 2px;--steps-marker-rest-border: var(--border-3);--steps-connector-color: var(--border-3);--steps-connector-width: 2px}a-tabs[data-steps][priority=tertiary]{--steps-marker-size: calc(var(--steps-icon-size) + 4px);--steps-marker-border-width: 0px}a-steps>a-tabs[data-steps]{background:transparent;border-radius:0;box-shadow:none}a-tabs[data-steps]>a-tab{--steps-complete-connector-color: var(--tab-selected-border);--steps-primary-marker-bg: oklch( from var(--tabs-tone-source) var(--steps-primary-marker-lightness) var(--steps-primary-marker-chroma) h );--steps-primary-completed-marker-bg: color-mix( in oklch, var(--steps-primary-marker-bg) 80%, transparent );position:relative;flex:1 1 0;min-width:0;max-width:max-content;padding-block:0;justify-content:flex-start;gap:var(--steps-item-gap);transition:color var(--tab-timing),background-color var(--tab-timing),box-shadow var(--tab-timing)}a-steps[data-orientation=vertical]:has(>a-tabs[data-steps]>a-tab:state(selected)[tone=brand]){--steps-complete-connector-color: var(--border-2-brand)}a-steps[data-orientation=vertical]:has(>a-tabs[data-steps]>a-tab:state(selected)[tone=info]){--steps-complete-connector-color: var(--border-2-info)}a-steps[data-orientation=vertical]:has(>a-tabs[data-steps]>a-tab:state(selected)[tone=success]){--steps-complete-connector-color: var(--border-2-success)}a-steps[data-orientation=vertical]:has(>a-tabs[data-steps]>a-tab:state(selected)[tone=warning]){--steps-complete-connector-color: var(--border-2-warning)}a-steps[data-orientation=vertical]:has(>a-tabs[data-steps]>a-tab:state(selected)[tone=critical]){--steps-complete-connector-color: var(--border-2-critical)}a-tabs[data-steps]:not([orientation=vertical])>a-tab:first-child{padding-inline-start:0}a-steps>a-tabs[data-steps][fill]>a-tab:not(:last-child){flex:1 1 0;max-width:none}a-steps>a-tabs[data-steps][fill]>a-tab:last-child{flex:0 1 auto;max-width:max-content}a-steps>a-tabs[data-steps]>a-tab:state(selected){--tab-bg: transparent;box-shadow:none}a-steps>a-tabs[data-steps][priority=tertiary]>a-tab:after{border:0}a-step-desc{display:inline-flex;flex:0 1 auto;flex-direction:column;min-width:0;max-width:100%}a-step-desc>a-tab-label{display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:500}a-step-hint{display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--tab-rest-tone, var(--text-3));font-size:calc(1em - 2px);font-weight:400;line-height:var(--steps-hint-leading)}a-step-marker{display:inline-flex;flex:none;align-items:center;justify-content:center;width:var(--steps-marker-size);height:var(--steps-marker-size);box-sizing:border-box;color:var(--text-4);border:var(--steps-marker-border-width) solid var(--steps-marker-rest-border);border-radius:999px;transition:color var(--tab-timing),background-color var(--tab-timing),border-color var(--tab-timing)}a-tabs[data-steps] a-step-marker :is(a-icon,a-loader){--icon-size: var(--steps-icon-size)}a-tabs[data-steps]>a-tab[tone]{--tab-secondary-bg: oklch( from var(--tabs-tone-source) var(--_track-l) var(--_track-c) h / var(--_sec-a) )}a-tabs[data-steps]>a-tab[tone] a-step-marker{color:var(--tab-selected-text)}a-tabs[data-steps]>a-tab:is(:state(selected),[data-step-state=completed]) a-step-marker{border-color:var(--steps-marker-active-border)}a-tabs[data-steps]>a-tab[data-step-state=error]:not(:state(selected)):not([disabled]) a-step-marker{border-color:var(--border-2-critical)}a-tabs[data-steps]:is([priority=secondary],[priority=tertiary])>a-tab[tone]:not([tone=""],[tone=neutral]) a-step-marker{color:var(--tab-fg)}a-tabs[data-steps][priority=secondary] a-tab:state(selected):not([disabled]) a-step-marker{color:var(--tab-selected-text);background-color:var(--tab-secondary-bg);border-color:var(--steps-marker-active-border)}a-tabs[data-steps]:not([priority=secondary],[priority=tertiary]) a-tab:state(selected):not([disabled]) a-step-marker{color:var(--steps-primary-marker-fg);background-color:var(--steps-primary-marker-bg);border-color:var(--steps-primary-marker-bg)}a-tabs[data-steps]:not([priority=secondary],[priority=tertiary]) a-tab[data-step-state=completed]:not(:state(selected)):not([disabled]) a-step-marker{color:var(--steps-primary-marker-fg);background-color:var(--steps-primary-completed-marker-bg);border-color:transparent}a-tabs[data-steps][priority=tertiary] a-tab:state(selected):not([disabled]) a-step-marker{color:var(--tab-selected-text);background-color:transparent;border-color:transparent}a-tabs[data-steps] a-step-marker>a-loader{transition:color var(--tab-timing)}a-tabs[data-steps]:is([priority=secondary],[priority=tertiary]) a-step-marker>a-loader:not([tone=neutral]){color:inherit}a-tabs[data-steps] a-tab:state(selected):not([disabled]) a-step-marker>a-loader{color:inherit}a-tabs[data-steps]:not([orientation=vertical])>a-tab:not(:last-child):before{content:"";order:3;flex:1 1 auto;align-self:center;min-width:calc(var(--steps-marker-size) / 2);height:var(--steps-connector-width);margin-inline-start:calc(1ch - var(--steps-item-gap));margin-inline-end:calc(1ch - 2 * var(--steps-tab-padding-x));background:var(--steps-connector-color);pointer-events:none}a-tabs[data-steps]:not([orientation=vertical])>a-tab[data-step-state=completed]:not(:last-child):before{background:var(--steps-complete-connector-color)}a-tabs[data-steps] a-tab[disabled]{--tab-fg: var(--text-5);opacity:1}a-tabs[data-steps] a-tab[disabled] a-step-marker,a-tabs[data-steps] a-tab[disabled] a-step-marker>a-loader{color:var(--text-5)}a-tabs[data-steps] a-tab[disabled] a-step-hint{color:var(--text-5)}a-steps[data-orientation=vertical]{--steps-content-indent: calc( var(--steps-marker-size) + var(--steps-item-gap) );--steps-vertical-gap: calc(var(--steps-gap) + 4px);display:grid;grid-template-columns:minmax(0,1fr);row-gap:var(--steps-vertical-gap)}a-steps[data-orientation=vertical]>a-tabs[orientation=vertical]{display:contents}a-steps[data-orientation=vertical]>a-tabs[orientation=vertical]>a-tab{flex:none;min-width:0;grid-column:1;width:max-content;max-width:100%;padding-inline-start:0}a-steps[data-orientation=vertical] a-tab:not(:last-child):before{content:"";position:absolute;inset-block-start:calc(var(--steps-tab-padding-block) + var(--steps-marker-size));inset-inline-start:calc(var(--steps-marker-size) / 2 - var(--steps-connector-width) / 2);inset-inline-end:auto;width:var(--steps-connector-width);height:calc(100% - var(--steps-marker-size) + var(--steps-vertical-gap));background:var(--steps-connector-color);transform:none;pointer-events:none}a-steps[data-orientation=vertical]>a-tabpanel{grid-column:1;grid-row:var(--steps-panel-row);width:calc(100% - var(--steps-content-indent));margin-inline-start:var(--steps-content-indent)}a-steps[data-orientation=vertical]:has(>a-tabpanel):has(>a-tabs[orientation=vertical]>a-tab:state(selected):not(:last-child)):after{content:"";grid-column:1;grid-row:var(--steps-panel-row);align-self:stretch;justify-self:start;width:var(--steps-connector-width);min-height:var(--steps-marker-size);margin-inline-start:calc(var(--steps-marker-size) / 2 - var(--steps-connector-width) / 2);margin-block-end:calc(-1 * var(--steps-vertical-gap));background:var(--steps-connector-color);pointer-events:none;z-index:1}a-steps[data-orientation=vertical]>a-tabs[data-steps]>a-tab[data-step-state=completed]:not(:last-child):before{background:var(--steps-complete-connector-color)}a-steps[data-orientation=vertical]:has(>a-tabpanel):has(>a-tabs[data-steps][orientation=vertical]>a-tab[data-step-state=completed]:state(selected):not(:last-child)):after{background:var(--steps-complete-connector-color)}@media(forced-colors:active){a-tabs[data-steps][priority=secondary] a-tab:state(selected){background-color:transparent;border:0;box-shadow:none}a-step-marker{color:CanvasText;background-color:Canvas;border-color:CanvasText}a-tabs[data-steps] a-tab:state(selected):not([disabled]) a-step-marker{border-color:Highlight}}}
@@ -0,0 +1,93 @@
1
+ import type { IconShape } from "../elements/a-icon.shapes";
2
+ import type { OptionPresentationProps } from "../general_types";
3
+ import type { TabsProps } from "./Tabs";
4
+ import "./Steps.css";
5
+ /** Named Anta tone used by Steps. */
6
+ export type StepTone = "neutral" | "brand" | "info" | "success" | "warning" | "critical";
7
+ /** Process state shown by one step. */
8
+ export type StepState = "incomplete" | "loading" | "completed" | "error";
9
+ /** Visual emphasis for the step markers and completed connectors. */
10
+ export type StepPriority = "primary" | "secondary" | "tertiary";
11
+ /** A static step marker: a number or any registered Anta icon shape. */
12
+ export type StepMarker = number | IconShape;
13
+ /** Snapshot passed to `renderMarker` for a step. */
14
+ export interface StepMarkerState {
15
+ /** This step's application-owned process state. */
16
+ state: StepState;
17
+ /** Whether this step is the selected tab. */
18
+ selected: boolean;
19
+ /** Whether this step cannot be selected. */
20
+ disabled: boolean;
21
+ }
22
+ /** One phase rendered by `<Steps>`. Inherited `className` and `style` land on
23
+ * the individual `<a-tab>`, not on the enclosing Steps sequence. */
24
+ export interface StepOption extends OptionPresentationProps {
25
+ /** Stable phase identity. Values must be unique within the sequence. */
26
+ value: string;
27
+ /** Visible phase label. It ellipsizes in a constrained step; its full content
28
+ * is available in a tooltip only when clipped. */
29
+ label: React.ReactNode;
30
+ /** Secondary text shown below the label. It ellipsizes in a constrained step;
31
+ * its full content is available in a tooltip only when clipped. */
32
+ hint?: React.ReactNode;
33
+ /** Application-owned process state. Selection and availability are separate;
34
+ * `error` keeps a critical icon and outline, and selection adds the fill. */
35
+ state: StepState;
36
+ /** Tone for this phase. It colors the label, hint, marker, and completed
37
+ * connector. Overrides the Steps `tone`; error steps stay critical and
38
+ * disabled steps stay neutral.
39
+ * @defaultValue inherits Steps `tone` */
40
+ tone?: StepTone | "";
41
+ /** Replaces the marker derived from `state`. A number is shown directly; an
42
+ * Anta icon shape is rendered as an `<Icon>`. */
43
+ marker?: StepMarker;
44
+ /** Disables this phase. A custom `marker` or `renderMarker` result is kept;
45
+ * otherwise it uses the incomplete marker. */
46
+ disabled?: boolean;
47
+ }
48
+ /** Public props for `<Steps>`. Selection, panels, events, orientation, size,
49
+ * and disabled behavior match `<Tabs>`. */
50
+ export interface StepsProps extends Omit<TabsProps, "options" | "priority" | "tone" | "noslide" | "round"> {
51
+ /** Ordered process phases. */
52
+ options: StepOption[];
53
+ /** Visual emphasis. Primary uses a solid selected marker and a stronger
54
+ * completed connector; secondary is the outlined default; tertiary is compact
55
+ * and borderless.
56
+ * @defaultValue 'secondary' */
57
+ priority?: StepPriority;
58
+ /** Tone applied to every step without its own `tone`. Error steps stay
59
+ * critical and disabled steps stay neutral.
60
+ * @defaultValue 'neutral' */
61
+ tone?: StepTone | "";
62
+ /** Builds a custom marker from a step and its current state. A returned node
63
+ * replaces `marker` and the built-in state marker; return `undefined` to use
64
+ * those fallbacks, or `null` for an empty ring. */
65
+ renderMarker?: (option: StepOption, state: StepMarkerState) => React.ReactNode;
66
+ }
67
+ /**
68
+ * A process stepper that switches tab-like content.
69
+ *
70
+ * The application owns phase states and transitions. `Steps` owns the tab
71
+ * interaction pattern, state presentation, and optional panels. Compose
72
+ * application-owned navigation controls beside it when the flow needs actions
73
+ * such as Back or Continue.
74
+ *
75
+ * It supports controlled (`value`) and uncontrolled (`defaultValue`) selection.
76
+ * When neither is supplied, the first option is selected. Explicitly disabled
77
+ * phases cannot be activated. Arrow-key behavior, panel pairing,
78
+ * duplicate-value warnings, and change events match `<Tabs>`.
79
+ *
80
+ * @example
81
+ * ```tsx
82
+ * <Steps
83
+ * defaultValue="setup"
84
+ * label="Setup progress"
85
+ * options={[
86
+ * { value: "build", label: "Build", state: "completed" },
87
+ * { value: "setup", label: "Setup", state: "loading" },
88
+ * { value: "next", label: "Next steps", state: "incomplete", disabled: true },
89
+ * ]}
90
+ * />
91
+ * ```
92
+ */
93
+ export declare const Steps: ({ options, value, defaultValue, onStateChange, onChange, onValueChange, onFocus, onBlur, label, priority, tone, renderMarker, size, orientation, fill, disabled, children, className, style, id, ...rest }: StepsProps) => any;
@@ -0,0 +1,128 @@
1
+ import { Fragment, jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
+ import { optionPresentationAttrs } from "../anta_helpers";
3
+ import { useState } from "../jsx-runtime";
4
+ import { Icon } from "./Icon";
5
+ import { Loader } from "./Loader";
6
+ import { Tabs } from "./Tabs";
7
+ import "./Steps.css";
8
+ const STATE_ICON = {
9
+ incomplete: "circle-large",
10
+ completed: "check",
11
+ error: "x"
12
+ };
13
+ const Steps = ({
14
+ options,
15
+ value,
16
+ defaultValue,
17
+ onStateChange,
18
+ onChange,
19
+ onValueChange,
20
+ onFocus,
21
+ onBlur,
22
+ label,
23
+ priority = "secondary",
24
+ tone,
25
+ renderMarker,
26
+ size,
27
+ orientation,
28
+ fill,
29
+ disabled,
30
+ children,
31
+ className,
32
+ style,
33
+ id,
34
+ ...rest
35
+ }) => {
36
+ const controlled = value !== void 0;
37
+ const [internalValue, setInternalValue] = useState(
38
+ defaultValue
39
+ );
40
+ const currentValue = controlled ? value : internalValue ?? options[0]?.value;
41
+ const handleStateChange = (event, detail) => {
42
+ onStateChange?.(event, detail);
43
+ if (controlled || event.defaultPrevented) return;
44
+ setInternalValue(detail.next ?? void 0);
45
+ };
46
+ const tabs = options.map((option) => {
47
+ const { className: optionClassName, style: optionStyle, ...optionAttrs } = optionPresentationAttrs(option);
48
+ const optionTone = option.tone || void 0;
49
+ const stepTone = option.disabled ? "neutral" : option.state === "error" ? "critical" : optionTone || tone || "neutral";
50
+ const customMarker = renderMarker?.(option, {
51
+ state: option.state,
52
+ selected: option.value === currentValue,
53
+ disabled: !!option.disabled
54
+ });
55
+ const builtInMarker = option.disabled ? /* @__PURE__ */ jsx(Icon, { shape: STATE_ICON.incomplete }) : option.state === "loading" ? /* @__PURE__ */ jsx(Loader, { tone: stepTone }) : /* @__PURE__ */ jsx(Icon, { shape: STATE_ICON[option.state] });
56
+ const marker = customMarker !== void 0 ? customMarker : option.marker !== void 0 ? typeof option.marker === "number" ? option.marker : /* @__PURE__ */ jsx(Icon, { shape: option.marker }) : builtInMarker;
57
+ return {
58
+ ...optionAttrs,
59
+ value: option.value,
60
+ disabled: option.disabled,
61
+ "data-step-state": option.state,
62
+ // Steps propagates its tone to each generated tab. A local option tone
63
+ // wins, while error and disabled states retain their fixed semantics.
64
+ tone: stepTone,
65
+ tooltip: /* @__PURE__ */ jsxs(Fragment, { children: [
66
+ option.label,
67
+ option.hint != null && /* @__PURE__ */ jsxs(Fragment, { children: [
68
+ " \u2014 ",
69
+ option.hint
70
+ ] })
71
+ ] }),
72
+ className: optionClassName,
73
+ style: optionStyle,
74
+ children: /* @__PURE__ */ jsxs(Fragment, { children: [
75
+ /* @__PURE__ */ jsx("a-step-marker", { "aria-hidden": "true", children: marker }),
76
+ /* @__PURE__ */ jsxs("a-step-desc", { children: [
77
+ /* @__PURE__ */ jsx("a-tab-label", { children: option.label }),
78
+ option.hint != null && /* @__PURE__ */ jsx("a-step-hint", { children: option.hint })
79
+ ] })
80
+ ] })
81
+ };
82
+ });
83
+ const currentIndex = options.findIndex(
84
+ (option) => option.value === currentValue
85
+ );
86
+ const vertical = orientation === "vertical";
87
+ const hasPanels = children != null && children !== false;
88
+ const hasVerticalPanel = vertical && hasPanels;
89
+ const panelRow = (currentIndex >= 0 ? currentIndex : options.length - 1) + 2;
90
+ const rootStyle = hasVerticalPanel ? {
91
+ ...style,
92
+ "--steps-panel-row": panelRow
93
+ } : style;
94
+ return /* @__PURE__ */ jsx(
95
+ "a-steps",
96
+ {
97
+ class: className,
98
+ style: rootStyle,
99
+ id,
100
+ ...rest,
101
+ "data-orientation": vertical ? "vertical" : void 0,
102
+ children: /* @__PURE__ */ jsx(
103
+ Tabs,
104
+ {
105
+ options: tabs,
106
+ value: currentValue,
107
+ onStateChange: handleStateChange,
108
+ onChange,
109
+ onValueChange,
110
+ onFocus,
111
+ onBlur,
112
+ label,
113
+ size,
114
+ orientation,
115
+ fill,
116
+ priority,
117
+ noslide: true,
118
+ disabled,
119
+ "data-steps": "",
120
+ children
121
+ }
122
+ )
123
+ }
124
+ );
125
+ };
126
+ export {
127
+ Steps
128
+ };