@robr0/design-system 0.2.0 → 0.4.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 (99) hide show
  1. package/README.md +33 -28
  2. package/components/Accordion/Accordion.js +0 -1
  3. package/components/AgentStatus/AgentStatus.css +436 -0
  4. package/components/AgentStatus/AgentStatus.d.ts +37 -0
  5. package/components/AgentStatus/AgentStatus.js +53 -0
  6. package/components/AgentStatus/AgentStatusPatterns.d.ts +17 -0
  7. package/components/AgentStatus/AgentStatusPatterns.js +20 -0
  8. package/components/AlertDialog/AlertDialog.css +5 -5
  9. package/components/Avatar/Avatar.js +1 -0
  10. package/components/Breadcrumb/Breadcrumb.d.ts +7 -1
  11. package/components/Breadcrumb/Breadcrumb.js +3 -2
  12. package/components/Button/Button.css +18 -0
  13. package/components/Button/Button.d.ts +20 -2
  14. package/components/Button/Button.js +13 -3
  15. package/components/ButtonGroup/ButtonGroup.js +2 -9
  16. package/components/Card/Card.css +6 -0
  17. package/components/Checkbox/Checkbox.d.ts +17 -3
  18. package/components/CircularButton/CircularButton.css +11 -0
  19. package/components/CircularButton/CircularButton.d.ts +15 -3
  20. package/components/CircularButton/CircularButton.js +19 -6
  21. package/components/CodeBlock/CodeBlock.js +10 -1
  22. package/components/ColorPicker/ColorPicker.css +278 -0
  23. package/components/ColorPicker/ColorPicker.d.ts +50 -0
  24. package/components/ColorPicker/ColorPicker.js +338 -0
  25. package/components/Combobox/Combobox.css +0 -36
  26. package/components/Combobox/Combobox.d.ts +10 -2
  27. package/components/Combobox/Combobox.js +90 -82
  28. package/components/CommandPalette/CommandPalette.css +1 -15
  29. package/components/CommandPalette/CommandPalette.js +6 -5
  30. package/components/ContextMenu/ContextMenu.css +258 -0
  31. package/components/ContextMenu/ContextMenu.d.ts +26 -0
  32. package/components/ContextMenu/ContextMenu.js +350 -0
  33. package/components/DateInput/DateInput.css +0 -36
  34. package/components/DateInput/DateInput.d.ts +5 -1
  35. package/components/DateInput/DateInput.js +37 -33
  36. package/components/DatePicker/DatePicker.js +45 -34
  37. package/components/Dialog/Dialog.css +5 -5
  38. package/components/Dialog/Dialog.js +5 -4
  39. package/components/Drawer/Drawer.js +7 -2
  40. package/components/Dropdown/Dropdown.css +2 -40
  41. package/components/Dropdown/Dropdown.d.ts +10 -2
  42. package/components/Dropdown/Dropdown.js +119 -109
  43. package/components/DropdownMenu/DropdownMenu.css +2 -4
  44. package/components/DropdownMenu/DropdownMenu.js +10 -7
  45. package/components/Field/Field.css +80 -0
  46. package/components/Field/Field.d.ts +50 -0
  47. package/components/Field/Field.js +58 -0
  48. package/components/Field/FieldContext.d.ts +42 -0
  49. package/components/Field/FieldContext.js +7 -0
  50. package/components/FileInput/FileInput.css +0 -36
  51. package/components/FileInput/FileInput.d.ts +5 -1
  52. package/components/FileInput/FileInput.js +111 -103
  53. package/components/Input/Input.css +0 -43
  54. package/components/Input/Input.d.ts +5 -1
  55. package/components/Input/Input.js +51 -46
  56. package/components/Kbd/Kbd.css +28 -0
  57. package/components/Kbd/Kbd.d.ts +19 -0
  58. package/components/Kbd/Kbd.js +14 -0
  59. package/components/LinkList/LinkList.d.ts +1 -0
  60. package/components/Popover/Popover.d.ts +0 -6
  61. package/components/Popover/Popover.js +22 -16
  62. package/components/ProgressBar/ProgressBar.js +1 -1
  63. package/components/RadioButton/RadioButton.d.ts +17 -3
  64. package/components/Reasoning/Reasoning.css +152 -0
  65. package/components/Reasoning/Reasoning.d.ts +36 -0
  66. package/components/Reasoning/Reasoning.js +69 -0
  67. package/components/SelectionCard/SelectionCard.css +6 -2
  68. package/components/SelectionCard/SelectionCard.d.ts +5 -1
  69. package/components/Skeleton/Skeleton.css +2 -0
  70. package/components/Slider/Slider.d.ts +5 -1
  71. package/components/Spinner/Spinner.css +11 -0
  72. package/components/Spinner/Spinner.d.ts +2 -2
  73. package/components/Swatch/Swatch.css +52 -0
  74. package/components/Swatch/Swatch.d.ts +34 -0
  75. package/components/Swatch/Swatch.js +44 -0
  76. package/components/Table/Table.css +19 -0
  77. package/components/Table/Table.js +1 -1
  78. package/components/Textarea/Textarea.css +0 -43
  79. package/components/Textarea/Textarea.d.ts +5 -1
  80. package/components/Textarea/Textarea.js +37 -35
  81. package/components/Timeline/Timeline.d.ts +2 -0
  82. package/components/ToggleGroup/ToggleGroup.d.ts +10 -2
  83. package/components/ToggleSwitch/ToggleSwitch.css +7 -2
  84. package/components/ToggleSwitch/ToggleSwitch.d.ts +10 -2
  85. package/components/ToolCall/ToolCall.css +213 -0
  86. package/components/ToolCall/ToolCall.d.ts +43 -0
  87. package/components/ToolCall/ToolCall.js +89 -0
  88. package/components/registry.d.ts +47 -10
  89. package/components/registry.js +5 -1
  90. package/components/registry.json +523 -56
  91. package/components/registry.json.d.ts +523 -56
  92. package/components/registry.json.js +4 -1
  93. package/index.d.ts +12 -1
  94. package/index.js +24 -1
  95. package/package.json +8 -4
  96. package/tokens/registry.json +1 -0
  97. package/tokens/registry.json.d.ts +1 -0
  98. package/tokens/registry.json.js +1 -1
  99. package/tokens/tokens-motion.css +3 -1
@@ -0,0 +1,53 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import "./AgentStatus.css";
4
+ const DOT_COUNT = 12;
5
+ const DEFAULT_LABELS = {
6
+ idle: "Idle",
7
+ thinking: "Thinking",
8
+ working: "Working",
9
+ waiting: "Waiting for you",
10
+ done: "Done",
11
+ error: "Failed"
12
+ };
13
+ const ACTIVE_STATES = ["thinking", "working", "waiting"];
14
+ const AgentStatus = React.forwardRef(
15
+ ({
16
+ state = "thinking",
17
+ label,
18
+ pattern = "orbit",
19
+ shimmer,
20
+ size = "sm",
21
+ variant = "inline",
22
+ className = "",
23
+ children,
24
+ ...rest
25
+ }, ref) => {
26
+ const baseClass = "ds-agent-status";
27
+ const isActive = ACTIVE_STATES.includes(state);
28
+ const isShimmering = shimmer ?? isActive;
29
+ const classes = [
30
+ baseClass,
31
+ `${baseClass}--${state}`,
32
+ `${baseClass}--${pattern}`,
33
+ `${baseClass}--${size}`,
34
+ `${baseClass}--${variant}`,
35
+ isActive ? `${baseClass}--animated` : "",
36
+ className
37
+ ].filter(Boolean).join(" ");
38
+ return /* @__PURE__ */ jsxs("div", { ...rest, ref, className: classes, role: "status", children: [
39
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__matrix`, "aria-hidden": "true", children: Array.from({ length: DOT_COUNT }, (_, i) => /* @__PURE__ */ jsx("span", { className: `${baseClass}__dot` }, i)) }),
40
+ /* @__PURE__ */ jsx(
41
+ "span",
42
+ {
43
+ className: `${baseClass}__label${isShimmering ? ` ${baseClass}__label--shimmer` : ""}`,
44
+ children: children ?? label ?? DEFAULT_LABELS[state]
45
+ }
46
+ )
47
+ ] });
48
+ }
49
+ );
50
+ AgentStatus.displayName = "AgentStatus";
51
+ export {
52
+ AgentStatus
53
+ };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * AgentStatus's vocabulary, kept out of the component file so that module
3
+ * exports only a component (react-refresh) — and exported rather than kept
4
+ * private so a consumer building a pattern picker, and this repo's own docs
5
+ * gallery, can iterate the list instead of restating it.
6
+ *
7
+ * `.tsx` despite holding no JSX: the barrel generator and the
8
+ * `./components/*` export subpath both address `.tsx`, so that extension is
9
+ * what makes this reachable as `@robr0/design-system/components/AgentStatus/AgentStatusPatterns`.
10
+ */
11
+ /**
12
+ * The dot-matrix animations. Each pattern is a named choreography over the
13
+ * same 4x3 grid, so switching pattern never changes the indicator's footprint.
14
+ */
15
+ export declare const agentStatusPatterns: readonly ["braille", "orbit", "breathe", "snake", "fill-sweep", "pulse", "columns", "checkerboard", "scan", "rain", "cascade", "sparkle", "wave-rows", "helix", "diagonal-swipe"];
16
+ export type AgentStatusPattern = (typeof agentStatusPatterns)[number];
17
+ export type AgentStatusState = 'idle' | 'thinking' | 'working' | 'waiting' | 'done' | 'error';
@@ -0,0 +1,20 @@
1
+ const agentStatusPatterns = [
2
+ "braille",
3
+ "orbit",
4
+ "breathe",
5
+ "snake",
6
+ "fill-sweep",
7
+ "pulse",
8
+ "columns",
9
+ "checkerboard",
10
+ "scan",
11
+ "rain",
12
+ "cascade",
13
+ "sparkle",
14
+ "wave-rows",
15
+ "helix",
16
+ "diagonal-swipe"
17
+ ];
18
+ export {
19
+ agentStatusPatterns
20
+ };
@@ -81,11 +81,11 @@
81
81
  ============================================ */
82
82
 
83
83
  .ds-alert-dialog__title {
84
- font-family: var(--font-heading-6-family);
85
- font-size: var(--font-heading-6-size);
86
- font-weight: var(--font-heading-6-weight);
87
- line-height: var(--font-heading-6-line-height);
88
- letter-spacing: var(--font-heading-6-letter-spacing);
84
+ font-family: var(--font-heading-3-family);
85
+ font-size: var(--font-heading-3-size);
86
+ font-weight: var(--font-heading-3-weight);
87
+ line-height: var(--font-heading-3-line-height);
88
+ letter-spacing: var(--font-heading-3-letter-spacing);
89
89
  color: var(--color-text-primary);
90
90
  margin: 0;
91
91
  }
@@ -46,6 +46,7 @@ const Avatar = ({
46
46
  "span",
47
47
  {
48
48
  className: `${baseClass}__status ${baseClass}__status--${status}`,
49
+ role: "img",
49
50
  "aria-label": status
50
51
  }
51
52
  )
@@ -11,9 +11,15 @@ export interface BreadcrumbProps {
11
11
  maxItems?: number;
12
12
  /** Additional CSS classes */
13
13
  className?: string;
14
+ /**
15
+ * Accessible name for the nav landmark. Override when more than one
16
+ * Breadcrumb can appear on a page — identically-named landmarks are
17
+ * indistinguishable to assistive technology.
18
+ */
19
+ ariaLabel?: string;
14
20
  }
15
21
  /**
16
22
  * Breadcrumb shows the user's location within a page hierarchy.
17
23
  * Supports automatic collapsing of middle items when the list exceeds maxItems.
18
24
  */
19
- export declare const Breadcrumb: ({ items, maxItems, className, }: BreadcrumbProps) => import("react").JSX.Element;
25
+ export declare const Breadcrumb: ({ items, maxItems, className, ariaLabel, }: BreadcrumbProps) => import("react").JSX.Element;
@@ -4,7 +4,8 @@ import "../../fonts/material-symbols.css";
4
4
  const Breadcrumb = ({
5
5
  items,
6
6
  maxItems,
7
- className = ""
7
+ className = "",
8
+ ariaLabel
8
9
  }) => {
9
10
  const baseClass = "ds-breadcrumb";
10
11
  const classes = [baseClass, className].filter(Boolean).join(" ");
@@ -20,7 +21,7 @@ const Breadcrumb = ({
20
21
  } else {
21
22
  visibleItems = [...items];
22
23
  }
23
- return /* @__PURE__ */ jsx("nav", { className: classes, "aria-label": "Breadcrumb", children: /* @__PURE__ */ jsx("ol", { className: `${baseClass}__list`, children: visibleItems.map((item, index) => {
24
+ return /* @__PURE__ */ jsx("nav", { className: classes, "aria-label": ariaLabel || "Breadcrumb", children: /* @__PURE__ */ jsx("ol", { className: `${baseClass}__list`, children: visibleItems.map((item, index) => {
24
25
  const isLast = index === visibleItems.length - 1;
25
26
  if (item === "ellipsis") {
26
27
  return /* @__PURE__ */ jsxs("li", { className: `${baseClass}__item`, "aria-hidden": "true", children: [
@@ -371,6 +371,24 @@
371
371
  color: var(--color-status-error-bg);
372
372
  }
373
373
 
374
+ /* ============================================
375
+ LOADING
376
+ Keeps the variant's full-colour appearance (no disabled dim);
377
+ the spinner occupies the left icon slot and inherits its colour.
378
+ ============================================ */
379
+
380
+ .ds-button--loading {
381
+ cursor: progress;
382
+ pointer-events: none;
383
+ }
384
+
385
+ /* The generic icon-slot svg sizing must not distort the spinner —
386
+ it sizes itself from the Spinner size prop */
387
+ .ds-button .ds-button__icon .ds-spinner__circle {
388
+ width: 100%;
389
+ height: 100%;
390
+ }
391
+
374
392
  /* ============================================
375
393
  FOCUS
376
394
  ============================================ */
@@ -13,6 +13,12 @@ type ButtonOwnProps = {
13
13
  size?: 'default' | 'compact';
14
14
  /** Whether the button is disabled */
15
15
  disabled?: boolean;
16
+ /**
17
+ * Shows a spinner in the left icon slot and blocks interaction while an
18
+ * async action runs. Keeps the variant's full-colour appearance (unlike
19
+ * `disabled`) and sets `aria-busy` on the rendered element.
20
+ */
21
+ loading?: boolean;
16
22
  /** Optional href — renders as <a> instead of <button> */
17
23
  href?: string;
18
24
  /** Optional target attribute for links */
@@ -23,9 +29,15 @@ type ButtonOwnProps = {
23
29
  ariaCurrent?: boolean;
24
30
  /** Additional CSS classes */
25
31
  className?: string;
26
- /** @deprecated Use `variant` instead. */
32
+ /**
33
+ * Legacy alias for `variant`.
34
+ *
35
+ * @deprecated Use `variant` instead.
36
+ */
27
37
  priority?: 'primary' | 'secondary' | 'tertiary' | 'destructive';
28
38
  /**
39
+ * Documentation-only interaction state.
40
+ *
29
41
  * @deprecated Use `disabled` for the disabled state.
30
42
  *
31
43
  * Documentation-only affordance for rendering a *static* interaction state in
@@ -33,9 +45,15 @@ type ButtonOwnProps = {
33
45
  * pseudo-classes and needs no prop — for docs, prefer `className="ds-button--hover"`.
34
46
  */
35
47
  state?: 'default' | 'hover' | 'active' | 'disabled';
36
- /** @deprecated Use `iconLeft` instead. */
48
+ /**
49
+ * Legacy alias for `iconLeft`.
50
+ *
51
+ * @deprecated Use `iconLeft` instead.
52
+ */
37
53
  icon?: string;
38
54
  /**
55
+ * Legacy toggle for showing the text label.
56
+ *
39
57
  * @deprecated Will be removed once `label` loses its default in the next major;
40
58
  * an icon-only button will simply omit `label`.
41
59
  */
@@ -1,5 +1,6 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import React from "react";
3
+ import { Spinner } from "../Spinner/Spinner.js";
3
4
  import "./Button.css";
4
5
  import "../../fonts/material-symbols.css";
5
6
  const Button = React.forwardRef(
@@ -10,6 +11,7 @@ const Button = React.forwardRef(
10
11
  variant,
11
12
  size = "default",
12
13
  disabled,
14
+ loading,
13
15
  href,
14
16
  target,
15
17
  rel,
@@ -25,27 +27,32 @@ const Button = React.forwardRef(
25
27
  const resolvedVariant = variant ?? priority ?? "primary";
26
28
  const isDisabled = disabled ?? state === "disabled";
27
29
  const resolvedState = state ?? (isDisabled ? "disabled" : "default");
30
+ const isLoading = Boolean(loading);
28
31
  const classes = [
29
32
  baseClass,
30
33
  `${baseClass}--${resolvedVariant}`,
31
34
  `${baseClass}--${resolvedState}`,
32
35
  `${baseClass}--${size}`,
36
+ isLoading && `${baseClass}--loading`,
33
37
  className
34
38
  ].filter(Boolean).join(" ");
35
39
  const iconClass = "material-symbols-rounded";
36
- const leftIcon = iconLeft || icon;
40
+ const spinnerSize = size === "compact" ? "sm" : "md";
41
+ const leftIcon = isLoading ? /* @__PURE__ */ jsx(Spinner, { size: spinnerSize, variant: "inherit" }) : iconLeft || icon;
37
42
  const renderIcon = (iconProp) => {
38
43
  if (typeof iconProp === "string") {
39
44
  return /* @__PURE__ */ jsx("span", { className: `${baseClass}__icon ${iconClass}`, "aria-hidden": "true", children: iconProp });
40
45
  }
41
46
  return /* @__PURE__ */ jsx("span", { className: `${baseClass}__icon`, "aria-hidden": "true", children: iconProp });
42
47
  };
48
+ const needsAccessibleName = !text || !label;
49
+ const fallbackAriaLabel = needsAccessibleName && label && !rest["aria-label"] ? label : void 0;
43
50
  const content = /* @__PURE__ */ jsxs(Fragment, { children: [
44
51
  leftIcon && renderIcon(leftIcon),
45
52
  text && /* @__PURE__ */ jsx("span", { className: `${baseClass}__text`, children: label }),
46
53
  iconRight && renderIcon(iconRight)
47
54
  ] });
48
- if (href && !isDisabled) {
55
+ if (href && !isDisabled && !isLoading) {
49
56
  return /* @__PURE__ */ jsx(
50
57
  "a",
51
58
  {
@@ -56,6 +63,7 @@ const Button = React.forwardRef(
56
63
  target,
57
64
  rel,
58
65
  "aria-current": ariaCurrent ? "page" : void 0,
66
+ "aria-label": fallbackAriaLabel ?? rest["aria-label"],
59
67
  children: content
60
68
  }
61
69
  );
@@ -67,7 +75,9 @@ const Button = React.forwardRef(
67
75
  ref,
68
76
  type: "button",
69
77
  className: classes,
70
- disabled: isDisabled,
78
+ disabled: isDisabled || isLoading,
79
+ "aria-busy": isLoading || void 0,
80
+ "aria-label": fallbackAriaLabel ?? rest["aria-label"],
71
81
  children: content
72
82
  }
73
83
  );
@@ -17,15 +17,8 @@ const ButtonGroup = ({
17
17
  children: buttons.map((button, index) => /* @__PURE__ */ jsx(
18
18
  Button,
19
19
  {
20
- priority: button.priority || "tertiary",
21
- state: button.state || "default",
22
- label: button.label,
23
- iconLeft: button.iconLeft,
24
- iconRight: button.iconRight,
25
- icon: button.icon,
26
- onClick: button.onClick,
27
- href: button.href,
28
- ariaCurrent: button.ariaCurrent
20
+ ...button,
21
+ variant: button.variant ?? button.priority ?? "tertiary"
29
22
  },
30
23
  index
31
24
  ))
@@ -25,6 +25,8 @@
25
25
 
26
26
  /* Interaction */
27
27
  transition: transform var(--motion-duration-slow) var(--motion-ease-standard), box-shadow var(--motion-duration-slow) var(--motion-ease-standard);
28
+ /* ds-allow(color): transparent resting state of the sanctioned Card
29
+ hover-lift shadow (see design.md — Do's and Don'ts). */
28
30
  box-shadow: 0 0 0 rgba(0, 0, 0, 0);
29
31
  }
30
32
 
@@ -34,6 +36,8 @@
34
36
 
35
37
  .ds-card--interactive:hover {
36
38
  transform: translateY(-4px);
39
+ /* ds-allow(color): the sanctioned Card hover-lift shadow — deliberately
40
+ heavier than --shadow-floating (see design.md — Do's and Don'ts). */
37
41
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
38
42
  }
39
43
 
@@ -80,6 +84,8 @@
80
84
 
81
85
  .ds-card--case-study:hover {
82
86
  transform: translateY(-4px);
87
+ /* ds-allow(color): same sanctioned hover-lift shadow as the interactive
88
+ variant. */
83
89
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
84
90
  }
85
91
 
@@ -15,11 +15,21 @@ type CheckboxOwnProps = {
15
15
  onCheckedChange?: (checked: boolean) => void;
16
16
  /** Additional CSS classes */
17
17
  className?: string;
18
- /** @deprecated Use `onCheckedChange` instead. */
18
+ /**
19
+ * Legacy change handler, kept for backwards compatibility.
20
+ *
21
+ * @deprecated Use `onCheckedChange` instead.
22
+ */
19
23
  onChange?: (checked: boolean) => void;
20
- /** @deprecated Pass the native `aria-label` attribute instead. */
24
+ /**
25
+ * Legacy accessible-name prop.
26
+ *
27
+ * @deprecated Pass the native `aria-label` attribute instead.
28
+ */
21
29
  ariaLabel?: string;
22
30
  /**
31
+ * Legacy form-field name.
32
+ *
23
33
  * @deprecated No-op. This component renders a `<div role="checkbox">`, not a
24
34
  * native `<input>`, so it cannot participate in native form submission.
25
35
  * Declared only so the attribute is not forwarded to an element that rejects it.
@@ -48,7 +58,11 @@ type CheckboxGroupOwnProps = {
48
58
  onValuesChange?: (values: string[]) => void;
49
59
  /** Additional CSS classes */
50
60
  className?: string;
51
- /** @deprecated Use `onValuesChange` instead. */
61
+ /**
62
+ * Legacy change handler, kept for backwards compatibility.
63
+ *
64
+ * @deprecated Use `onValuesChange` instead.
65
+ */
52
66
  onChange?: (values: string[]) => void;
53
67
  };
54
68
  export interface CheckboxGroupProps extends CheckboxGroupOwnProps, Omit<React.ComponentPropsWithoutRef<'div'>, keyof CheckboxGroupOwnProps> {
@@ -196,6 +196,17 @@
196
196
  background-color: var(--color-action-passive-bg-active);
197
197
  }
198
198
 
199
+ /* ============================================
200
+ LOADING
201
+ Keeps the variant's full-colour appearance (no disabled dim);
202
+ the spinner replaces the icon and inherits its colour.
203
+ ============================================ */
204
+
205
+ .ds-circular-button--loading {
206
+ cursor: progress;
207
+ pointer-events: none;
208
+ }
209
+
199
210
  /* ============================================
200
211
  FOCUS
201
212
  ============================================ */
@@ -1,12 +1,24 @@
1
1
  export interface CircularButtonProps {
2
2
  /** Material Symbol icon name */
3
3
  icon: string;
4
- /** Button priority/variant */
4
+ /** Visual treatment */
5
+ variant?: 'primary' | 'secondary' | 'tertiary';
6
+ /**
7
+ * Legacy alias for `variant`.
8
+ *
9
+ * @deprecated Use `variant` instead.
10
+ */
5
11
  priority?: 'primary' | 'secondary' | 'tertiary';
6
12
  /** Button state */
7
13
  state?: 'default' | 'hover' | 'active' | 'disabled';
8
14
  /** Button size */
9
15
  size?: 'default' | 'compact';
16
+ /**
17
+ * Shows a spinner in place of the icon and blocks interaction while an
18
+ * async action runs. Keeps the variant's full-colour appearance (unlike
19
+ * the disabled state) and sets `aria-busy` on the rendered element.
20
+ */
21
+ loading?: boolean;
10
22
  /** Accessible label */
11
23
  ariaLabel: string;
12
24
  /** Optional click handler */
@@ -25,6 +37,6 @@ export interface CircularButtonProps {
25
37
  /**
26
38
  * Circular icon button component.
27
39
  * A round button containing a single icon, available in
28
- * primary and secondary variants with default and compact sizes.
40
+ * primary, secondary and tertiary variants with default and compact sizes.
29
41
  */
30
- export declare const CircularButton: ({ icon, priority, state, size, ariaLabel, onClick, href, target, rel, "aria-describedby": ariaDescribedby, className, }: CircularButtonProps) => import("react").JSX.Element;
42
+ export declare const CircularButton: ({ icon, variant, priority, state, size, loading, ariaLabel, onClick, href, target, rel, "aria-describedby": ariaDescribedby, className, }: CircularButtonProps) => import("react").JSX.Element;
@@ -1,11 +1,14 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
+ import { Spinner } from "../Spinner/Spinner.js";
2
3
  import "./CircularButton.css";
3
4
  import "../../fonts/material-symbols.css";
4
5
  const CircularButton = ({
5
6
  icon,
6
- priority = "primary",
7
+ variant,
8
+ priority,
7
9
  state = "default",
8
10
  size = "default",
11
+ loading,
9
12
  ariaLabel,
10
13
  onClick,
11
14
  href,
@@ -15,13 +18,22 @@ const CircularButton = ({
15
18
  className = ""
16
19
  }) => {
17
20
  const baseClass = "ds-circular-button";
18
- const variantClass = `${baseClass}--${priority}`;
21
+ const resolvedVariant = variant ?? priority ?? "primary";
22
+ const variantClass = `${baseClass}--${resolvedVariant}`;
19
23
  const stateClass = `${baseClass}--${state}`;
20
24
  const sizeClass = `${baseClass}--${size}`;
21
- const classes = [baseClass, variantClass, stateClass, sizeClass, className].filter(Boolean).join(" ");
25
+ const isLoading = Boolean(loading);
26
+ const classes = [
27
+ baseClass,
28
+ variantClass,
29
+ stateClass,
30
+ sizeClass,
31
+ isLoading && `${baseClass}--loading`,
32
+ className
33
+ ].filter(Boolean).join(" ");
22
34
  const isDisabled = state === "disabled";
23
- const children = /* @__PURE__ */ jsx("span", { className: `${baseClass}__icon material-symbols-rounded`, "aria-hidden": "true", children: icon });
24
- if (href && !isDisabled) {
35
+ const children = isLoading ? /* @__PURE__ */ jsx("span", { className: `${baseClass}__icon`, "aria-hidden": "true", children: /* @__PURE__ */ jsx(Spinner, { size: size === "compact" ? "sm" : "md", variant: "inherit" }) }) : /* @__PURE__ */ jsx("span", { className: `${baseClass}__icon material-symbols-rounded`, "aria-hidden": "true", children: icon });
36
+ if (href && !isDisabled && !isLoading) {
25
37
  return /* @__PURE__ */ jsx(
26
38
  "a",
27
39
  {
@@ -42,7 +54,8 @@ const CircularButton = ({
42
54
  type: "button",
43
55
  className: classes,
44
56
  onClick,
45
- disabled: isDisabled,
57
+ disabled: isDisabled || isLoading,
58
+ "aria-busy": isLoading || void 0,
46
59
  "aria-label": ariaLabel,
47
60
  "aria-describedby": ariaDescribedby,
48
61
  children
@@ -77,7 +77,16 @@ const CodeBlock = ({
77
77
  }
78
78
  )
79
79
  ] }),
80
- /* @__PURE__ */ jsx("div", { id: panelId, className: `${baseClass}__panel`, "aria-hidden": collapsed || void 0, children: /* @__PURE__ */ jsx("div", { className: `${baseClass}__panel-inner`, children: /* @__PURE__ */ jsx("pre", { className: `${baseClass}__pre`, children: /* @__PURE__ */ jsx("code", { className: `${baseClass}__code`, children: code }) }) }) })
80
+ /* @__PURE__ */ jsx("div", { id: panelId, className: `${baseClass}__panel`, "aria-hidden": collapsed || void 0, children: /* @__PURE__ */ jsx("div", { className: `${baseClass}__panel-inner`, children: /* @__PURE__ */ jsx(
81
+ "pre",
82
+ {
83
+ className: `${baseClass}__pre`,
84
+ tabIndex: collapsed ? -1 : 0,
85
+ role: "region",
86
+ "aria-label": filename ? `Code: ${filename}` : language ? `Code: ${language}` : "Code",
87
+ children: /* @__PURE__ */ jsx("code", { className: `${baseClass}__code`, children: code })
88
+ }
89
+ ) }) })
81
90
  ] });
82
91
  };
83
92
  export {