infinity-ui-elements 1.7.13 → 1.8.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.
Files changed (43) hide show
  1. package/dist/components/Alert/Alert.d.ts +60 -0
  2. package/dist/components/Alert/Alert.d.ts.map +1 -0
  3. package/dist/components/Alert/Alert.stories.d.ts +16 -0
  4. package/dist/components/Alert/Alert.stories.d.ts.map +1 -0
  5. package/dist/components/Alert/index.d.ts +3 -0
  6. package/dist/components/Alert/index.d.ts.map +1 -0
  7. package/dist/components/Avatar/Avatar.d.ts +1 -1
  8. package/dist/components/Badge/Badge.d.ts +2 -2
  9. package/dist/components/Button/Button.d.ts +3 -3
  10. package/dist/components/Button/Button.d.ts.map +1 -1
  11. package/dist/components/ButtonGroup/ButtonGroup.d.ts +1 -1
  12. package/dist/components/Checkbox/Checkbox.d.ts +1 -1
  13. package/dist/components/Counter/Counter.d.ts +1 -1
  14. package/dist/components/Divider/Divider.d.ts +1 -1
  15. package/dist/components/Dropdown/Dropdown.d.ts +1 -1
  16. package/dist/components/Dropdown/Dropdown.stories.d.ts +60 -60
  17. package/dist/components/IconButton/IconButton.d.ts +22 -0
  18. package/dist/components/IconButton/IconButton.d.ts.map +1 -0
  19. package/dist/components/IconButton/IconButton.stories.d.ts +17 -0
  20. package/dist/components/IconButton/IconButton.stories.d.ts.map +1 -0
  21. package/dist/components/IconButton/index.d.ts +3 -0
  22. package/dist/components/IconButton/index.d.ts.map +1 -0
  23. package/dist/components/Link/Link.d.ts +3 -3
  24. package/dist/components/Link/Link.d.ts.map +1 -1
  25. package/dist/components/Pagination/Pagination.d.ts +1 -1
  26. package/dist/components/Radio/Radio.d.ts +1 -1
  27. package/dist/components/SearchableDropdown/SearchableDropdown.stories.d.ts +74 -74
  28. package/dist/components/Select/Select.d.ts +2 -2
  29. package/dist/components/Switch/Switch.d.ts +1 -1
  30. package/dist/components/TabItem/TabItem.d.ts +1 -1
  31. package/dist/components/Table/tableVariants.d.ts +3 -3
  32. package/dist/components/TextArea/TextArea.d.ts +2 -2
  33. package/dist/components/TextField/TextField.d.ts +2 -2
  34. package/dist/index.css +1 -1
  35. package/dist/index.d.ts +2 -0
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.esm.js +930 -505
  38. package/dist/index.esm.js.map +1 -1
  39. package/dist/index.js +932 -503
  40. package/dist/index.js.map +1 -1
  41. package/dist/lib/icons.d.ts +3 -0
  42. package/dist/lib/icons.d.ts.map +1 -1
  43. package/package.json +1 -1
package/dist/index.esm.js CHANGED
@@ -1,14 +1,120 @@
1
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
3
  import { cva } from 'class-variance-authority';
4
- import { clsx } from 'clsx';
5
- import { twMerge } from 'tailwind-merge';
6
4
  import { Slot } from '@radix-ui/react-slot';
7
5
  import { PulseLoader, ClipLoader } from 'react-spinners';
6
+ import { clsx } from 'clsx';
7
+ import { twMerge } from 'tailwind-merge';
8
8
  import { ExternalLink, Loader2, Search, X, ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react';
9
9
  import { createPortal } from 'react-dom';
10
10
  import { flexRender } from '@tanstack/react-table';
11
11
 
12
+ /**
13
+ * ==============================================
14
+ * HOW TO ADD A NEW ICON:
15
+ * ==============================================
16
+ *
17
+ * 1. Add your SVG file to: src/assets/icons/{iconName}.svg
18
+ *
19
+ * 2. Copy the SVG content from the file
20
+ *
21
+ * 3. Add it to the iconRegistry below:
22
+ * iconName: `<svg>...</svg>`,
23
+ *
24
+ * 4. Use it anywhere in your app:
25
+ * <Icon name="iconName" size={24} />
26
+ *
27
+ * The Icon component will automatically:
28
+ * - Replace hardcoded colors with currentColor
29
+ * - Allow you to control color via className or style
30
+ * - Resize based on the size prop
31
+ * ==============================================
32
+ */
33
+ /**
34
+ * Icon registry - maps icon names to their SVG content
35
+ * Add your icons here by copying the SVG content from your icon files
36
+ */
37
+ const iconRegistry = {
38
+ // Tick/Check icon
39
+ tick: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
40
+ <path d="M10.364 15.1924L19.5564 6L20.9706 7.41421L10.364 18.0208L4 11.6569L5.41422 10.2427L10.364 15.1924Z" fill="#081416"/>
41
+ </svg>`,
42
+ // Alias: check points to the same icon as tick
43
+ check: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
44
+ <path d="M10.364 15.1924L19.5564 6L20.9706 7.41421L10.364 18.0208L4 11.6569L5.41422 10.2427L10.364 15.1924Z" fill="#081416"/>
45
+ </svg>`,
46
+ add: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
47
+ <path d="M12.9 11.0999L21 11.0997V12.8997L12.9 12.8999V21H11.1V12.8999L3.00004 12.9001L3 11.1001L11.1 11.0999L11.0999 3.00001L12.8999 3L12.9 11.0999Z" fill="#081416"/>
48
+ </svg>`,
49
+ // Info icon
50
+ info: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
51
+ <path d="M12 22.5C6.20101 22.5 1.5 17.7989 1.5 12C1.5 6.20101 6.20101 1.5 12 1.5C17.7989 1.5 22.5 6.20101 22.5 12C22.5 17.7989 17.7989 22.5 12 22.5ZM12 20.4C16.6392 20.4 20.4 16.6392 20.4 12C20.4 7.36081 16.6392 3.6 12 3.6C7.36081 3.6 3.6 7.36081 3.6 12C3.6 16.6392 7.36081 20.4 12 20.4ZM10.95 6.75H13.05V8.85H10.95V6.75ZM10.95 10.95H13.05V17.25H10.95V10.95Z" fill="#081416"/>
52
+ </svg>
53
+ `,
54
+ // Exclamation/Warning icon
55
+ exclamation: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
56
+ <path d="M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2ZM13 17H11V15H13V17ZM13 13H11V7H13V13Z" fill="#081416"/>
57
+ </svg>`,
58
+ // Close/X icon
59
+ close: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
60
+ <path d="M18 6L6 18M6 6L18 18" stroke="#081416" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
61
+ </svg>`,
62
+ };
63
+ const Icon = ({ name, size = 24, className = "", style = {}, ...props }) => {
64
+ const svgContent = iconRegistry[name];
65
+ if (!svgContent) {
66
+ console.warn(`Icon "${String(name)}" not found in registry.\n` +
67
+ `Available icons: ${Object.keys(iconRegistry).join(", ")}`);
68
+ return null;
69
+ }
70
+ // Parse the SVG content
71
+ const parser = new DOMParser();
72
+ const svgDoc = parser.parseFromString(svgContent, "image/svg+xml");
73
+ const svgElement = svgDoc.querySelector("svg");
74
+ if (!svgElement) {
75
+ console.error(`Invalid SVG content for icon "${String(name)}"`);
76
+ return null;
77
+ }
78
+ // Get the viewBox for proper scaling
79
+ const viewBox = svgElement.getAttribute("viewBox") || "0 0 24 24";
80
+ // Get all SVG content as string and replace hardcoded colors with currentColor
81
+ let innerSVG = svgElement.innerHTML;
82
+ // Replace common hardcoded colors with currentColor
83
+ // This allows the icon to inherit text color from parent
84
+ innerSVG = innerSVG
85
+ .replace(/fill="[^"]*"/g, 'fill="currentColor"')
86
+ .replace(/stroke="[^"]*"/g, 'stroke="currentColor"');
87
+ return (jsx("svg", { width: size, height: size, viewBox: viewBox, className: className, style: style, xmlns: "http://www.w3.org/2000/svg", ...props, dangerouslySetInnerHTML: { __html: innerSVG } }));
88
+ };
89
+ /**
90
+ * Get all available icon names from the registry
91
+ * @returns Array of registered icon names
92
+ *
93
+ * @example
94
+ * ```tsx
95
+ * const icons = getAvailableIcons();
96
+ * console.log(icons); // ['tick', 'check', ...]
97
+ * ```
98
+ */
99
+ function getAvailableIcons() {
100
+ return Object.keys(iconRegistry);
101
+ }
102
+ /**
103
+ * Check if an icon exists in the registry
104
+ * @param name - Icon name to check
105
+ * @returns true if the icon exists
106
+ *
107
+ * @example
108
+ * ```tsx
109
+ * if (hasIcon('tick')) {
110
+ * // Icon exists
111
+ * }
112
+ * ```
113
+ */
114
+ function hasIcon(name) {
115
+ return name in iconRegistry;
116
+ }
117
+
12
118
  // Define patterns for custom classes that should be preserved
13
119
  // This approach is more scalable than hardcoding individual class names
14
120
  const CUSTOM_CLASS_PATTERNS = [
@@ -48,123 +154,12 @@ function cn(...inputs) {
48
154
  return clsx(mergedStandard, customClasses);
49
155
  }
50
156
 
51
- // Helper function to get the text utility class name
52
- function getTextClassName(variant = "body", size = "medium", weight = "regular", color = "default") {
53
- // Build the base class name
54
- let baseClass = `text-${variant}`;
55
- // Add size
56
- if (size) {
57
- baseClass += `-${size}`;
58
- }
59
- // Add weight
60
- if (weight) {
61
- baseClass += `-${weight}`;
62
- }
63
- // Add color class separately
64
- const colorClass = `text-color-${color}`;
65
- return `${baseClass} ${colorClass}`;
66
- }
67
- const Text = React.forwardRef(({ className, variant = "body", size = "medium", weight = "regular", color = "default", as = "p", children, ...props }, ref) => {
68
- const Component = as;
69
- const textClass = getTextClassName(variant, size, weight, color);
70
- return React.createElement(Component, {
71
- className: cn(textClass, className),
72
- ref,
73
- ...props,
74
- }, children);
75
- });
76
- Text.displayName = "Text";
77
-
78
- const inverseColorClasses = {
79
- a1: "bg-avatar-fill-a1-on-bg text-avatar-fill-a1-bg",
80
- a2: "bg-avatar-fill-a2-on-bg text-avatar-fill-a2-bg",
81
- a3: "bg-avatar-fill-a3-on-bg text-avatar-fill-a3-bg",
82
- a4: "bg-avatar-fill-a4-on-bg text-avatar-fill-a4-bg",
83
- a5: "bg-avatar-fill-a5-on-bg text-avatar-fill-a5-bg",
84
- };
85
- const avatarVariants = cva("inline-flex items-center justify-center font-medium text-center select-none", {
86
- variants: {
87
- color: {
88
- a1: "bg-avatar-fill-a1-bg text-avatar-fill-a1-on-bg",
89
- a2: "bg-avatar-fill-a2-bg text-avatar-fill-a2-on-bg",
90
- a3: "bg-avatar-fill-a3-bg text-avatar-fill-a3-on-bg",
91
- a4: "bg-avatar-fill-a4-bg text-avatar-fill-a4-on-bg",
92
- a5: "bg-avatar-fill-a5-bg text-avatar-fill-a5-on-bg",
93
- },
94
- size: {
95
- small: "h-[24px] w-[24px] text-body-medium-regular rounded-large",
96
- medium: "h-[32px] w-[32px] text-body-medium-regular rounded-xlarge",
97
- xlarge: "h-[36px] w-[36px] text-body-medium-regular rounded-xlarge",
98
- },
99
- },
100
- defaultVariants: {
101
- color: "a1",
102
- size: "medium",
103
- },
104
- });
105
- const statusVariants = cva("absolute flex items-center justify-center rounded-full border-2 border-surface-fill-neutral-intense", {
106
- variants: {
107
- size: {
108
- small: "h-5 w-5 -bottom-0.5 -right-0.5",
109
- medium: "h-6 w-6 -bottom-1 -right-1",
110
- xlarge: "h-6 w-6 -bottom-1 -right-1",
111
- },
112
- statusColor: {
113
- positive: "bg-action-fill-positive-default",
114
- negative: "bg-action-fill-negative-default",
115
- notice: "bg-action-fill-notice-default",
116
- info: "bg-action-fill-info-default",
117
- neutral: "bg-action-fill-neutral-default",
118
- },
119
- },
120
- defaultVariants: {
121
- size: "medium",
122
- statusColor: "notice",
123
- },
124
- });
125
- const Avatar = React.forwardRef(({ className, appearance = "default", color, size, children, src, alt, showStatus = false, statusColor = "notice", statusIcon, label, trailingComponent, containerClassName, ...props }, ref) => {
126
- const [imageError, setImageError] = React.useState(false);
127
- const handleImageError = () => {
128
- setImageError(true);
129
- };
130
- const getStatusIconSize = () => {
131
- switch (size) {
132
- case "small":
133
- return "h-3.5 w-3.5";
134
- case "medium":
135
- return "h-4 w-4";
136
- default:
137
- return "h-4 w-4";
138
- }
139
- };
140
- const getTextSize = () => {
141
- switch (size) {
142
- case "small":
143
- return "small";
144
- case "medium":
145
- return "medium";
146
- default:
147
- return "medium";
148
- }
149
- };
150
- const resolvedColor = color ?? "a1";
151
- const avatarElement = (jsxs("div", { className: "relative inline-block", children: [jsx("div", { className: cn(avatarVariants({ color: resolvedColor, size }), appearance === "inverse"
152
- ? inverseColorClasses[resolvedColor]
153
- : undefined, className), ...props, children: src && !imageError ? (jsx("img", { src: src, alt: alt || "Avatar", className: cn("h-full w-full object-cover", size === "small" ? "rounded-large" : "rounded-xlarge"), onError: handleImageError })) : (children) }), showStatus && (jsx("div", { className: cn(statusVariants({ size, statusColor })), children: statusIcon && (jsx("span", { className: cn("text-action-ink-on-primary-normal", getStatusIconSize()), children: statusIcon })) }))] }));
154
- // If no label or trailing component, return just the avatar
155
- if (!label && !trailingComponent) {
156
- return jsx("div", { ref: ref, children: avatarElement });
157
- }
158
- // Otherwise, return avatar with label and/or trailing component
159
- return (jsxs("div", { ref: ref, className: cn("inline-flex items-center gap-3", containerClassName), children: [avatarElement, label && (jsx(Text, { variant: "body", size: getTextSize(), weight: "medium", color: "default", as: "span", children: label })), trailingComponent && (jsx("span", { className: "ml-auto", children: trailingComponent }))] }));
160
- });
161
- Avatar.displayName = "Avatar";
162
-
163
- const badgeVariants = cva("inline-flex items-center whitespace-nowrap transition-colors", {
157
+ const buttonVariants = cva("items-center gap-3 justify-center whitespace-nowrap ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none", {
164
158
  variants: {
165
159
  variant: {
166
- light: "",
167
- filled: "",
160
+ primary: "bg-action-fill-primary-default text-action-ink-on-primary-normal hover:bg-action-fill-primary-hover",
161
+ secondary: "border",
162
+ tertiary: "hover:bg-accent hover:text-accent-foreground",
168
163
  },
169
164
  color: {
170
165
  primary: "",
@@ -173,171 +168,45 @@ const badgeVariants = cva("inline-flex items-center whitespace-nowrap transition
173
168
  notice: "",
174
169
  info: "",
175
170
  neutral: "",
171
+ white: "",
176
172
  },
177
173
  size: {
178
- small: "px-2 h-[var(--size-20)] gap-2 rounded-large text-body-small-medium",
179
- medium: "px-3 h-[var(--size-24)] gap-3 rounded-large text-body-medium-medium",
180
- large: "px-4 h-[var(--size-28)] gap-3 rounded-xlarge text-body-large-medium",
174
+ xsmall: "md:h-[28px] px-3 rounded-medium text-body-small-medium",
175
+ small: "md:h-[32px] px-4 rounded-medium text-body-small-medium",
176
+ medium: "md:h-[36px] px-5 py-2 rounded-large text-body-medium-medium",
177
+ large: "md:h-[44px] px-6 rounded-large text-body-large-medium",
178
+ },
179
+ isIconOnly: {
180
+ true: "aspect-square p-0",
181
+ false: "",
182
+ },
183
+ isLoading: {
184
+ true: "cursor-not-allowed",
185
+ false: "",
186
+ },
187
+ isDisabled: {
188
+ true: "cursor-not-allowed",
189
+ false: "cursor-pointer",
190
+ },
191
+ isFullWidth: {
192
+ true: "flex w-full",
193
+ false: "flex w-fit",
181
194
  },
182
195
  },
183
196
  compoundVariants: [
184
- // Light variant colors
197
+ // Primary variant colors
185
198
  {
186
- variant: "light",
199
+ variant: "primary",
187
200
  color: "primary",
188
- class: "bg-action-fill-primary-faded text-action-ink-primary-normal",
201
+ class: `bg-action-fill-primary-default text-action-ink-on-primary-normal
202
+ hover:bg-action-fill-primary-hover
203
+ disabled:bg-action-fill-primary-disabled
204
+ disabled:text-action-ink-primary-disabled,
205
+ active:bg-action-fill-primary-activated
206
+ `,
189
207
  },
190
208
  {
191
- variant: "light",
192
- color: "positive",
193
- class: "bg-action-fill-positive-faded text-action-ink-positive-normal",
194
- },
195
- {
196
- variant: "light",
197
- color: "negative",
198
- class: "bg-action-fill-negative-faded text-action-ink-negative-normal",
199
- },
200
- {
201
- variant: "light",
202
- color: "notice",
203
- class: "bg-action-fill-notice-faded text-action-ink-notice-normal",
204
- },
205
- {
206
- variant: "light",
207
- color: "info",
208
- class: "bg-action-fill-info-faded text-action-ink-info-normal",
209
- },
210
- {
211
- variant: "light",
212
- color: "neutral",
213
- class: "bg-action-fill-neutral-faded text-action-ink-neutral-normal",
214
- },
215
- // Filled variant colors
216
- {
217
- variant: "filled",
218
- color: "primary",
219
- class: "bg-action-fill-primary-default text-action-ink-on-primary-normal",
220
- },
221
- {
222
- variant: "filled",
223
- color: "positive",
224
- class: "bg-action-fill-positive-default text-action-ink-on-primary-normal",
225
- },
226
- {
227
- variant: "filled",
228
- color: "negative",
229
- class: "bg-action-fill-negative-default text-action-ink-on-primary-normal",
230
- },
231
- {
232
- variant: "filled",
233
- color: "notice",
234
- class: "bg-action-fill-notice-default text-action-ink-on-primary-normal",
235
- },
236
- {
237
- variant: "filled",
238
- color: "info",
239
- class: "bg-action-fill-info-default text-action-ink-on-primary-normal",
240
- },
241
- {
242
- variant: "filled",
243
- color: "neutral",
244
- class: "bg-action-fill-neutral-default text-action-ink-on-primary-normal",
245
- },
246
- ],
247
- defaultVariants: {
248
- variant: "light",
249
- color: "info",
250
- size: "medium",
251
- },
252
- });
253
- const Badge = React.forwardRef(({ className, variant, size, color, showDot = false, children, ...props }, ref) => {
254
- const getDotColor = () => {
255
- if (variant === "filled") {
256
- return "bg-action-ink-on-primary-normal";
257
- }
258
- // Light variant - use the corresponding action color
259
- switch (color) {
260
- case "primary":
261
- return "bg-action-fill-primary-default";
262
- case "positive":
263
- return "bg-action-fill-positive-default";
264
- case "negative":
265
- return "bg-action-fill-negative-default";
266
- case "notice":
267
- return "bg-action-fill-notice-default";
268
- case "info":
269
- return "bg-action-fill-info-default";
270
- case "neutral":
271
- return "bg-action-fill-neutral-default";
272
- default:
273
- return "bg-action-fill-info-default";
274
- }
275
- };
276
- const getDotSize = () => {
277
- if (size === "small") {
278
- return "h-2 w-2";
279
- }
280
- if (size === "medium") {
281
- return "h-[6px] w-[6px]";
282
- }
283
- return "h-3 w-3";
284
- };
285
- return (jsxs("div", { ref: ref, className: cn(badgeVariants({ variant, size, color }), className), ...props, children: [showDot && (jsx("span", { className: cn("rounded-full", getDotColor(), getDotSize()), "aria-hidden": "true" })), children] }));
286
- });
287
- Badge.displayName = "Badge";
288
-
289
- const buttonVariants = cva("items-center gap-3 justify-center whitespace-nowrap ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none", {
290
- variants: {
291
- variant: {
292
- primary: "bg-action-fill-primary-default text-action-ink-on-primary-normal hover:bg-action-fill-primary-hover",
293
- secondary: "border",
294
- tertiary: "hover:bg-accent hover:text-accent-foreground",
295
- },
296
- color: {
297
- primary: "",
298
- positive: "",
299
- negative: "",
300
- notice: "",
301
- info: "",
302
- neutral: "",
303
- },
304
- size: {
305
- xsmall: "md:h-[28px] px-3 rounded-medium text-body-small-medium",
306
- small: "md:h-[32px] px-4 rounded-medium text-body-small-medium",
307
- medium: "md:h-[36px] px-5 py-2 rounded-large text-body-medium-medium",
308
- large: "md:h-[44px] px-6 rounded-large text-body-large-medium",
309
- },
310
- isIconOnly: {
311
- true: "aspect-square p-0",
312
- false: "",
313
- },
314
- isLoading: {
315
- true: "cursor-not-allowed",
316
- false: "",
317
- },
318
- isDisabled: {
319
- true: "cursor-not-allowed",
320
- false: "cursor-pointer",
321
- },
322
- isFullWidth: {
323
- true: "flex w-full",
324
- false: "flex w-fit",
325
- },
326
- },
327
- compoundVariants: [
328
- // Primary variant colors
329
- {
330
- variant: "primary",
331
- color: "primary",
332
- class: `bg-action-fill-primary-default text-action-ink-on-primary-normal
333
- hover:bg-action-fill-primary-hover
334
- disabled:bg-action-fill-primary-disabled
335
- disabled:text-action-ink-primary-disabled,
336
- active:bg-action-fill-primary-activated
337
- `,
338
- },
339
- {
340
- variant: "primary",
209
+ variant: "primary",
341
210
  color: "positive",
342
211
  class: `bg-action-fill-positive-default text-action-ink-on-primary-normal
343
212
  hover:bg-action-fill-positive-hover
@@ -386,6 +255,16 @@ const buttonVariants = cva("items-center gap-3 justify-center whitespace-nowrap
386
255
  active:bg-action-fill-neutral-activated
387
256
  `,
388
257
  },
258
+ {
259
+ variant: "primary",
260
+ color: "white",
261
+ class: `bg-action-fill-white-default text-action-ink-neutral-subtle
262
+ hover:bg-action-fill-white-hover
263
+ disabled:bg-action-fill-white-disabled
264
+ disabled:text-action-ink-neutral-disabled
265
+ active:bg-action-fill-white-activated
266
+ `,
267
+ },
389
268
  // Secondary variant colors
390
269
  {
391
270
  variant: "secondary",
@@ -474,6 +353,19 @@ const buttonVariants = cva("items-center gap-3 justify-center whitespace-nowrap
474
353
  active:bg-action-fill-neutral-faded-activated
475
354
  `,
476
355
  },
356
+ {
357
+ variant: "secondary",
358
+ color: "white",
359
+ class: `border-action-outline-white-faded
360
+ text-action-ink-on-primary-subtle
361
+ hover:border-action-outline-white-faded-hover
362
+ hover:bg-action-fill-white-faded-hover
363
+ disabled:text-surface-ink-white-disabled
364
+ disabled:border-action-outline-white-disabled
365
+ active:border-action-outline-white-faded-activated
366
+ active:bg-action-fill-white-faded-activated
367
+ `,
368
+ },
477
369
  // Tertiary variant colors
478
370
  {
479
371
  variant: "tertiary",
@@ -529,6 +421,16 @@ const buttonVariants = cva("items-center gap-3 justify-center whitespace-nowrap
529
421
  active:bg-action-fill-neutral-faded-activated
530
422
  `,
531
423
  },
424
+ {
425
+ variant: "tertiary",
426
+ color: "white",
427
+ class: `text-action-ink-white-subtle
428
+ hover:bg-action-white-faded-hover
429
+ disabled:text-action-ink-white-disabled
430
+ disabled:bg-action-fill-white-disabled
431
+ active:bg-action-white-faded-activated
432
+ `,
433
+ },
532
434
  // Icon only sizing
533
435
  {
534
436
  isIconOnly: true,
@@ -563,7 +465,13 @@ const buttonVariants = cva("items-center gap-3 justify-center whitespace-nowrap
563
465
  const Button = React.forwardRef(({ className, variant = "primary", color = "primary", size, isIconOnly, isLoading, asChild = false, leadingIcon, trailingIcon, isFullWidth = false, children, disabled, ...props }, ref) => {
564
466
  const Comp = asChild ? Slot : "button";
565
467
  const isDisabled = disabled || isLoading || false;
566
- const buttonContent = (jsxs(Fragment, { children: [isLoading && !isIconOnly && (jsx(PulseLoader, { color: `var(--color-action-ink-${color}-normal)`, size: 10 })), isLoading && isIconOnly && (jsx(ClipLoader, { color: `var(--color-action-ink-${color}-normal)`, size: 20 })), !isLoading && leadingIcon && (jsx("span", { className: "mr-2", children: leadingIcon })), !isIconOnly && !isLoading && children, isIconOnly && !isLoading && children, !isLoading && trailingIcon && jsx("span", { children: trailingIcon })] }));
468
+ const getLoaderColor = () => {
469
+ if (color === "white") {
470
+ return "var(--color-surface-ink-neutral-normal)";
471
+ }
472
+ return `var(--color-action-ink-${color}-normal)`;
473
+ };
474
+ const buttonContent = (jsxs(Fragment, { children: [isLoading && !isIconOnly && (jsx(PulseLoader, { color: getLoaderColor(), size: 10 })), isLoading && isIconOnly && (jsx(ClipLoader, { color: getLoaderColor(), size: 20 })), !isLoading && leadingIcon && (jsx("span", { className: "mr-2", children: leadingIcon })), !isIconOnly && !isLoading && children, isIconOnly && !isLoading && children, !isLoading && trailingIcon && jsx("span", { children: trailingIcon })] }));
567
475
  return (jsx(Comp, { className: cn(buttonVariants({
568
476
  variant,
569
477
  color,
@@ -574,7 +482,752 @@ const Button = React.forwardRef(({ className, variant = "primary", color = "prim
574
482
  isFullWidth,
575
483
  }), className), ref: ref, disabled: isDisabled, ...props, children: buttonContent }));
576
484
  });
577
- Button.displayName = "Button";
485
+ Button.displayName = "Button";
486
+
487
+ const iconButtonVariants = cva("inline-flex items-center justify-center transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none p-0 border-0 bg-transparent", {
488
+ variants: {
489
+ color: {
490
+ primary: "",
491
+ positive: "",
492
+ negative: "",
493
+ notice: "",
494
+ info: "",
495
+ neutral: "",
496
+ white: "",
497
+ },
498
+ size: {
499
+ xsmall: "",
500
+ small: "",
501
+ medium: "",
502
+ large: "",
503
+ },
504
+ isLoading: {
505
+ true: "cursor-not-allowed",
506
+ false: "",
507
+ },
508
+ isDisabled: {
509
+ true: "cursor-not-allowed",
510
+ false: "cursor-pointer",
511
+ },
512
+ },
513
+ defaultVariants: {
514
+ color: "primary",
515
+ size: "medium",
516
+ isLoading: false,
517
+ },
518
+ });
519
+ const IconButton = React.forwardRef(({ className, color = "primary", size = "medium", isLoading, asChild = false, icon, iconElement, disabled, ...props }, ref) => {
520
+ const Comp = asChild ? Slot : "button";
521
+ const isDisabled = disabled || isLoading || false;
522
+ const getLoaderColor = () => {
523
+ if (color === "white") {
524
+ return "var(--color-surface-ink-neutral-normal)";
525
+ }
526
+ return `var(--color-action-ink-${color}-normal)`;
527
+ };
528
+ const getIconSize = () => {
529
+ switch (size) {
530
+ case "xsmall":
531
+ return 16;
532
+ case "small":
533
+ return 18;
534
+ case "medium":
535
+ return 20;
536
+ case "large":
537
+ return 24;
538
+ default:
539
+ return 20;
540
+ }
541
+ };
542
+ const getIconColorClass = () => {
543
+ // Color classes with normal, hover, focus, and disabled states
544
+ const colorMap = {
545
+ primary: "text-action-ink-primary-normal hover:text-action-ink-primary-subtle focus-visible:text-action-ink-primary-subtle disabled:text-action-ink-primary-muted",
546
+ positive: "text-action-ink-positive-normal hover:text-action-ink-positive-subtle focus-visible:text-action-ink-positive-subtle disabled:text-action-ink-positive-muted",
547
+ negative: "text-action-ink-negative-normal hover:text-action-ink-negative-subtle focus-visible:text-action-ink-negative-subtle disabled:text-action-ink-negative-muted",
548
+ notice: "text-action-ink-notice-normal hover:text-action-ink-notice-subtle focus-visible:text-action-ink-notice-subtle disabled:text-action-ink-notice-muted",
549
+ info: "text-action-ink-info-normal hover:text-action-ink-info-subtle focus-visible:text-action-ink-info-subtle disabled:text-action-ink-info-muted",
550
+ neutral: "text-action-ink-neutral-normal hover:text-action-ink-neutral-subtle focus-visible:text-action-ink-neutral-subtle disabled:text-action-ink-neutral-muted",
551
+ white: "text-surface-ink-white-subtle hover:text-surface-ink-white-normal focus-visible:text-surface-ink-white-normal disabled:text-surface-ink-white-muted",
552
+ };
553
+ return colorMap[color] || colorMap.primary;
554
+ };
555
+ const buttonContent = (jsx(Fragment, { children: isLoading ? (jsx(ClipLoader, { color: getLoaderColor(), size: getIconSize() })) : icon ? (jsx(Icon, { name: icon, size: getIconSize(), className: getIconColorClass() })) : iconElement ? (jsx("span", { className: getIconColorClass(), children: iconElement })) : null }));
556
+ return (jsx(Comp, { className: cn(iconButtonVariants({
557
+ color,
558
+ size,
559
+ isLoading,
560
+ isDisabled,
561
+ }), className), ref: ref, disabled: isDisabled, ...props, children: buttonContent }));
562
+ });
563
+ IconButton.displayName = "IconButton";
564
+
565
+ const linkVariants = cva("inline-flex items-center gap-1 whitespace-nowrap transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none decoration-1 underline-offset-4", {
566
+ variants: {
567
+ type: {
568
+ anchor: "hover:underline",
569
+ action: "no-underline cursor-pointer",
570
+ },
571
+ color: {
572
+ primary: "",
573
+ positive: "",
574
+ negative: "",
575
+ notice: "",
576
+ info: "",
577
+ neutral: "",
578
+ white: "",
579
+ },
580
+ size: {
581
+ xsmall: "text-body-xsmall-medium gap-1",
582
+ small: "text-body-small-medium gap-1",
583
+ medium: "text-body-medium-medium gap-1.5",
584
+ large: "text-body-large-medium gap-1.5",
585
+ },
586
+ isIconOnly: {
587
+ true: "no-underline",
588
+ false: "",
589
+ },
590
+ isDisabled: {
591
+ true: "cursor-not-allowed opacity-50",
592
+ false: "cursor-pointer",
593
+ },
594
+ },
595
+ compoundVariants: [
596
+ // Primary color variants
597
+ {
598
+ color: "primary",
599
+ class: `text-action-ink-primary-normal
600
+ hover:text-action-ink-primary-subtle
601
+ hover:decoration-action-outline-primary-hover
602
+ disabled:text-action-ink-primary-disabled
603
+ focus:text-action-ink-primary-hover
604
+ `,
605
+ },
606
+ // Positive color variants
607
+ {
608
+ color: "positive",
609
+ class: `text-action-ink-positive-normal
610
+ hover:text-action-ink-positive-subtle
611
+ hover:decoration-action-outline-positive-hover
612
+ hover:text-action-ink-positive-hover
613
+ disabled:text-action-ink-positive-disabled
614
+ focus:text-action-ink-positive-hover
615
+ `,
616
+ },
617
+ // Negative color variants
618
+ {
619
+ color: "negative",
620
+ class: `text-action-ink-negative-normal
621
+ hover:text-action-ink-negative-subtle
622
+ hover:decoration-action-outline-negative-hover
623
+ hover:text-action-ink-negative-hover
624
+ disabled:text-action-ink-negative-disabled
625
+ focus:text-action-ink-negative-hover
626
+ `,
627
+ },
628
+ // Notice color variants
629
+ {
630
+ color: "notice",
631
+ class: `text-action-ink-notice-normal
632
+ hover:text-action-ink-notice-subtle
633
+ hover:decoration-action-outline-notice-hover
634
+ hover:text-action-ink-notice-hover
635
+ disabled:text-action-ink-notice-disabled
636
+ focus:text-action-ink-notice-hover
637
+ `,
638
+ },
639
+ // Info color variants
640
+ {
641
+ color: "info",
642
+ class: `text-action-ink-info-normal
643
+ hover:text-action-ink-info-subtle
644
+ hover:decoration-action-outline-info-hover
645
+ hover:text-action-ink-info-hover
646
+ disabled:text-action-ink-info-disabled
647
+ focus:text-action-ink-info-hover
648
+ `,
649
+ },
650
+ // Neutral color variants
651
+ {
652
+ color: "neutral",
653
+ class: `text-action-ink-neutral-normal
654
+ hover:text-action-ink-neutral-subtle
655
+ hover:decoration-action-outline-neutral-hover
656
+ hover:text-action-ink-neutral-hover
657
+ disabled:text-action-ink-neutral-disabled
658
+ focus:text-action-ink-neutral-hover
659
+ `,
660
+ },
661
+ {
662
+ color: "white",
663
+ class: `text-action-ink-white-subtle
664
+ hover:text-action-ink-white-subtle
665
+ hover:decoration-action-outline-white-hover
666
+ hover:text-action-ink-white-hover
667
+ disabled:text-action-ink-white-disabled
668
+ focus:text-action-ink-white-hover
669
+ `,
670
+ },
671
+ ],
672
+ defaultVariants: {
673
+ type: "anchor",
674
+ color: "primary",
675
+ size: "medium",
676
+ isIconOnly: false,
677
+ isDisabled: false,
678
+ },
679
+ });
680
+ const Link = React.forwardRef(({ className, type = "anchor", color = "primary", size = "medium", isIconOnly = false, isDisabled = false, asChild = false, showIcon = false, icon, leadingIcon, trailingIcon, children, onClick, ...props }, ref) => {
681
+ const Comp = asChild ? Slot : "a";
682
+ const handleClick = (e) => {
683
+ if (isDisabled) {
684
+ e.preventDefault();
685
+ return;
686
+ }
687
+ onClick?.(e);
688
+ };
689
+ // Icon size based on link size
690
+ const iconSize = {
691
+ xsmall: 12,
692
+ small: 14,
693
+ medium: 16,
694
+ large: 18,
695
+ }[size];
696
+ // Determine what to show as trailing icon
697
+ // Priority: trailingIcon > (showIcon && icon) > (showIcon && default ExternalLink)
698
+ const finalTrailingIcon = trailingIcon || (showIcon && (icon || jsx(ExternalLink, { size: iconSize })));
699
+ const linkContent = (jsxs(Fragment, { children: [leadingIcon && !isIconOnly && (jsx("span", { className: "inline-flex items-center", children: leadingIcon })), !isIconOnly && children, isIconOnly && children, finalTrailingIcon && !isIconOnly && (jsx("span", { className: "inline-flex items-center", children: finalTrailingIcon })), isIconOnly &&
700
+ (leadingIcon || finalTrailingIcon || (jsx(ExternalLink, { size: iconSize })))] }));
701
+ return (jsx(Comp, { className: cn(linkVariants({
702
+ type,
703
+ color,
704
+ size,
705
+ isIconOnly,
706
+ isDisabled,
707
+ }), className), ref: ref, onClick: handleClick, "aria-disabled": isDisabled, tabIndex: isDisabled ? -1 : undefined, ...props, children: linkContent }));
708
+ });
709
+ Link.displayName = "Link";
710
+
711
+ const alertVariants = cva("relative flex gap-4 p-4 rounded-large transition-colors", {
712
+ variants: {
713
+ emphasis: {
714
+ subtle: "",
715
+ intense: "",
716
+ },
717
+ intent: {
718
+ positive: "",
719
+ negative: "",
720
+ notice: "",
721
+ info: "",
722
+ neutral: "",
723
+ },
724
+ },
725
+ compoundVariants: [
726
+ // Subtle emphasis
727
+ {
728
+ emphasis: "subtle",
729
+ intent: "positive",
730
+ class: "bg-feedback-fill-positive-subtle",
731
+ },
732
+ {
733
+ emphasis: "subtle",
734
+ intent: "negative",
735
+ class: "bg-feedback-fill-negative-subtle",
736
+ },
737
+ {
738
+ emphasis: "subtle",
739
+ intent: "notice",
740
+ class: "bg-feedback-fill-notice-subtle",
741
+ },
742
+ {
743
+ emphasis: "subtle",
744
+ intent: "info",
745
+ class: "bg-feedback-fill-info-subtle",
746
+ },
747
+ {
748
+ emphasis: "subtle",
749
+ intent: "neutral",
750
+ class: "bg-feedback-fill-neutral-subtle",
751
+ },
752
+ // Intense emphasis
753
+ {
754
+ emphasis: "intense",
755
+ intent: "positive",
756
+ class: "bg-feedback-fill-positive-intense",
757
+ },
758
+ {
759
+ emphasis: "intense",
760
+ intent: "negative",
761
+ class: "bg-feedback-fill-negative-intense",
762
+ },
763
+ {
764
+ emphasis: "intense",
765
+ intent: "notice",
766
+ class: "bg-feedback-fill-notice-intense",
767
+ },
768
+ {
769
+ emphasis: "intense",
770
+ intent: "info",
771
+ class: "bg-feedback-fill-info-intense",
772
+ },
773
+ {
774
+ emphasis: "intense",
775
+ intent: "neutral",
776
+ class: "bg-feedback-fill-neutral-intense",
777
+ },
778
+ ],
779
+ defaultVariants: {
780
+ emphasis: "subtle",
781
+ intent: "info",
782
+ },
783
+ });
784
+ const alertIconVariants = cva("flex-shrink-0 rounded-full flex items-center justify-center mt-1", {
785
+ variants: {
786
+ emphasis: {
787
+ subtle: "",
788
+ intense: "text-white",
789
+ },
790
+ intent: {
791
+ positive: "",
792
+ negative: "",
793
+ notice: "",
794
+ info: "",
795
+ neutral: "",
796
+ },
797
+ },
798
+ compoundVariants: [
799
+ // Subtle emphasis - colored backgrounds with white icons
800
+ {
801
+ emphasis: "subtle",
802
+ intent: "positive",
803
+ class: "text-action-ink-positive-normal",
804
+ },
805
+ {
806
+ emphasis: "subtle",
807
+ intent: "negative",
808
+ class: " text-action-ink-negative-normal",
809
+ },
810
+ {
811
+ emphasis: "subtle",
812
+ intent: "notice",
813
+ class: " text-action-ink-notice-normal",
814
+ },
815
+ {
816
+ emphasis: "subtle",
817
+ intent: "info",
818
+ class: " text-action-ink-info-normal",
819
+ },
820
+ {
821
+ emphasis: "subtle",
822
+ intent: "neutral",
823
+ class: " text-action-ink-neutral-normal",
824
+ },
825
+ // Intense emphasis - white backgrounds with colored icons
826
+ {
827
+ emphasis: "intense",
828
+ intent: "positive",
829
+ class: " text-action-ink-on-primary-normal",
830
+ },
831
+ {
832
+ emphasis: "intense",
833
+ intent: "negative",
834
+ class: " text-action-ink-on-primary-normal",
835
+ },
836
+ {
837
+ emphasis: "intense",
838
+ intent: "notice",
839
+ class: " text-action-ink-on-primary-normal",
840
+ },
841
+ {
842
+ emphasis: "intense",
843
+ intent: "info",
844
+ class: " text-action-fill-info-default",
845
+ },
846
+ {
847
+ emphasis: "intense",
848
+ intent: "neutral",
849
+ class: " text-action-fill-neutral-default",
850
+ },
851
+ ],
852
+ defaultVariants: {
853
+ emphasis: "subtle",
854
+ intent: "info",
855
+ },
856
+ });
857
+ const alertTextVariants = cva("", {
858
+ variants: {
859
+ emphasis: {
860
+ subtle: "",
861
+ intense: "",
862
+ },
863
+ intent: {
864
+ positive: "",
865
+ negative: "",
866
+ notice: "",
867
+ info: "",
868
+ neutral: "",
869
+ },
870
+ },
871
+ compoundVariants: [
872
+ // Subtle emphasis - dark text
873
+ {
874
+ emphasis: "subtle",
875
+ intent: "positive",
876
+ class: "text-surface-ink-neutral-normal",
877
+ },
878
+ {
879
+ emphasis: "subtle",
880
+ intent: "negative",
881
+ class: "text-surface-ink-neutral-normal",
882
+ },
883
+ {
884
+ emphasis: "subtle",
885
+ intent: "notice",
886
+ class: "text-surface-ink-neutral-normal",
887
+ },
888
+ {
889
+ emphasis: "subtle",
890
+ intent: "info",
891
+ class: "text-surface-ink-neutral-normal",
892
+ },
893
+ {
894
+ emphasis: "subtle",
895
+ intent: "neutral",
896
+ class: "text-surface-ink-neutral-normal",
897
+ },
898
+ // Intense emphasis - white text
899
+ {
900
+ emphasis: "intense",
901
+ intent: "positive",
902
+ class: "text-action-ink-on-primary-normal",
903
+ },
904
+ {
905
+ emphasis: "intense",
906
+ intent: "negative",
907
+ class: "text-action-ink-on-primary-normal",
908
+ },
909
+ {
910
+ emphasis: "intense",
911
+ intent: "notice",
912
+ class: "text-action-ink-on-primary-normal",
913
+ },
914
+ {
915
+ emphasis: "intense",
916
+ intent: "info",
917
+ class: "text-action-ink-on-primary-normal",
918
+ },
919
+ {
920
+ emphasis: "intense",
921
+ intent: "neutral",
922
+ class: "text-action-ink-on-primary-normal",
923
+ },
924
+ ],
925
+ defaultVariants: {
926
+ emphasis: "subtle",
927
+ intent: "info",
928
+ },
929
+ });
930
+ const getDefaultIcon = (intent, emphasis) => {
931
+ const iconSize = 16;
932
+ switch (emphasis) {
933
+ case "intense":
934
+ return (jsx(Icon, { name: "info", size: iconSize, className: "text-white", "aria-hidden": "true" }));
935
+ default:
936
+ return jsx(Icon, { name: "info", size: iconSize, "aria-hidden": "true" });
937
+ }
938
+ };
939
+ const getButtonColor = (intent) => {
940
+ switch (intent) {
941
+ case "positive":
942
+ return "positive";
943
+ case "negative":
944
+ return "negative";
945
+ case "notice":
946
+ return "notice";
947
+ case "info":
948
+ return "info";
949
+ case "neutral":
950
+ return "neutral";
951
+ default:
952
+ return "neutral";
953
+ }
954
+ };
955
+ const getActionButton = (actionButtonText, onActionButtonClick, emphasis, intent) => {
956
+ if (!actionButtonText)
957
+ return null;
958
+ // For intense emphasis: secondary variant with white border and white text
959
+ // For subtle emphasis: secondary variant with colored border and colored text (default behavior)
960
+ if (emphasis === "intense") {
961
+ return (jsx(Button, { variant: "secondary", color: "white", size: "small", onClick: onActionButtonClick, children: actionButtonText }));
962
+ }
963
+ // Subtle emphasis: use default secondary variant styling
964
+ return (jsx(Button, { variant: "secondary", color: intent, size: "small", onClick: onActionButtonClick, children: actionButtonText }));
965
+ };
966
+ const getActionLink = (actionButtonText, onActionLinkClick, emphasis, intent) => {
967
+ if (!actionButtonText)
968
+ return null;
969
+ // For intense emphasis: secondary variant with white border and white text
970
+ // For subtle emphasis: secondary variant with colored border and colored text (default behavior)
971
+ if (emphasis === "intense") {
972
+ return (jsx(Link, { type: "action", color: "white", size: "small", onClick: onActionLinkClick, children: actionButtonText }));
973
+ }
974
+ // Subtle emphasis: use default secondary variant styling
975
+ return (jsx(Link, { type: "action", color: intent, size: "small", onClick: onActionLinkClick, children: actionButtonText }));
976
+ };
977
+ const Alert = React.forwardRef(({ className, emphasis = "subtle", intent = "info", title, children, isFullWidth = false, onClose, icon, actionButtonText, onActionButtonClick, actionLinkText, onActionLinkClick, ...props }, ref) => {
978
+ const displayIcon = icon ?? getDefaultIcon(intent, emphasis);
979
+ const actionButton = getActionButton(actionButtonText, onActionButtonClick, emphasis, intent);
980
+ const actionLink = getActionLink(actionLinkText, onActionLinkClick, emphasis, intent);
981
+ const hasActions = actionButton || actionLink;
982
+ return (jsxs("div", { ref: ref, className: cn(alertVariants({ emphasis, intent }), className), role: "alert", ...props, children: [jsx("div", { className: cn(alertIconVariants({ emphasis, intent }), "h-5 w-5 shrink-0"), children: displayIcon }), isFullWidth ? (
983
+ /* Full Width Layout: Icon, Content, Actions/Close in one row */
984
+ jsxs("div", { className: "flex-1 min-w-0 flex items-center justify-between gap-4", children: [jsxs("div", { className: "flex-1 min-w-0 flex flex-col gap-1", children: [title && (jsx("div", { className: cn("text-body-medium-semibold", alertTextVariants({ emphasis, intent })), children: title })), jsx("div", { className: cn("text-body-small-regular", alertTextVariants({ emphasis, intent })), children: children })] }), jsxs("div", { className: "flex items-center gap-3 shrink-0", children: [hasActions && (jsxs(Fragment, { children: [actionButton, actionLink] })), onClose && (jsx(IconButton, { icon: "close", color: emphasis === "intense" ? "white" : getButtonColor(intent), size: "xsmall", onClick: onClose, className: cn("shrink-0 h-5 w-5", emphasis === "intense"
985
+ ? "hover:bg-white/20"
986
+ : "hover:bg-black/10"), "aria-label": "Close alert" }))] })] })) : (
987
+ /* Not Full Width Layout: Icon and Content in row, Actions below, Close top-right */
988
+ jsxs("div", { className: "flex-1 min-w-0 relative", children: [onClose && (jsx("div", { className: "absolute top-0 right-0", children: jsx(IconButton, { icon: "close", color: emphasis === "intense" ? "white" : getButtonColor(intent), size: "xsmall", onClick: onClose, className: cn("shrink-0 h-5 w-5", emphasis === "intense"
989
+ ? "hover:bg-white/20"
990
+ : "hover:bg-black/10"), "aria-label": "Close alert" }) })), jsxs("div", { className: "flex flex-col gap-3 pr-8", children: [title && (jsx("div", { className: cn("text-body-medium-semibold", alertTextVariants({ emphasis, intent })), children: title })), jsx("div", { className: cn("text-body-small-regular", alertTextVariants({ emphasis, intent })), children: children }), hasActions && (jsxs("div", { className: "flex items-center gap-3", children: [actionButton, actionLink] }))] })] }))] }));
991
+ });
992
+ Alert.displayName = "Alert";
993
+
994
+ // Helper function to get the text utility class name
995
+ function getTextClassName(variant = "body", size = "medium", weight = "regular", color = "default") {
996
+ // Build the base class name
997
+ let baseClass = `text-${variant}`;
998
+ // Add size
999
+ if (size) {
1000
+ baseClass += `-${size}`;
1001
+ }
1002
+ // Add weight
1003
+ if (weight) {
1004
+ baseClass += `-${weight}`;
1005
+ }
1006
+ // Add color class separately
1007
+ const colorClass = `text-color-${color}`;
1008
+ return `${baseClass} ${colorClass}`;
1009
+ }
1010
+ const Text = React.forwardRef(({ className, variant = "body", size = "medium", weight = "regular", color = "default", as = "p", children, ...props }, ref) => {
1011
+ const Component = as;
1012
+ const textClass = getTextClassName(variant, size, weight, color);
1013
+ return React.createElement(Component, {
1014
+ className: cn(textClass, className),
1015
+ ref,
1016
+ ...props,
1017
+ }, children);
1018
+ });
1019
+ Text.displayName = "Text";
1020
+
1021
+ const inverseColorClasses = {
1022
+ a1: "bg-avatar-fill-a1-on-bg text-avatar-fill-a1-bg",
1023
+ a2: "bg-avatar-fill-a2-on-bg text-avatar-fill-a2-bg",
1024
+ a3: "bg-avatar-fill-a3-on-bg text-avatar-fill-a3-bg",
1025
+ a4: "bg-avatar-fill-a4-on-bg text-avatar-fill-a4-bg",
1026
+ a5: "bg-avatar-fill-a5-on-bg text-avatar-fill-a5-bg",
1027
+ };
1028
+ const avatarVariants = cva("inline-flex items-center justify-center font-medium text-center select-none", {
1029
+ variants: {
1030
+ color: {
1031
+ a1: "bg-avatar-fill-a1-bg text-avatar-fill-a1-on-bg",
1032
+ a2: "bg-avatar-fill-a2-bg text-avatar-fill-a2-on-bg",
1033
+ a3: "bg-avatar-fill-a3-bg text-avatar-fill-a3-on-bg",
1034
+ a4: "bg-avatar-fill-a4-bg text-avatar-fill-a4-on-bg",
1035
+ a5: "bg-avatar-fill-a5-bg text-avatar-fill-a5-on-bg",
1036
+ },
1037
+ size: {
1038
+ small: "h-[24px] w-[24px] text-body-medium-regular rounded-large",
1039
+ medium: "h-[32px] w-[32px] text-body-medium-regular rounded-xlarge",
1040
+ xlarge: "h-[36px] w-[36px] text-body-medium-regular rounded-xlarge",
1041
+ },
1042
+ },
1043
+ defaultVariants: {
1044
+ color: "a1",
1045
+ size: "medium",
1046
+ },
1047
+ });
1048
+ const statusVariants = cva("absolute flex items-center justify-center rounded-full border-2 border-surface-fill-neutral-intense", {
1049
+ variants: {
1050
+ size: {
1051
+ small: "h-5 w-5 -bottom-0.5 -right-0.5",
1052
+ medium: "h-6 w-6 -bottom-1 -right-1",
1053
+ xlarge: "h-6 w-6 -bottom-1 -right-1",
1054
+ },
1055
+ statusColor: {
1056
+ positive: "bg-action-fill-positive-default",
1057
+ negative: "bg-action-fill-negative-default",
1058
+ notice: "bg-action-fill-notice-default",
1059
+ info: "bg-action-fill-info-default",
1060
+ neutral: "bg-action-fill-neutral-default",
1061
+ },
1062
+ },
1063
+ defaultVariants: {
1064
+ size: "medium",
1065
+ statusColor: "notice",
1066
+ },
1067
+ });
1068
+ const Avatar = React.forwardRef(({ className, appearance = "default", color, size, children, src, alt, showStatus = false, statusColor = "notice", statusIcon, label, trailingComponent, containerClassName, ...props }, ref) => {
1069
+ const [imageError, setImageError] = React.useState(false);
1070
+ const handleImageError = () => {
1071
+ setImageError(true);
1072
+ };
1073
+ const getStatusIconSize = () => {
1074
+ switch (size) {
1075
+ case "small":
1076
+ return "h-3.5 w-3.5";
1077
+ case "medium":
1078
+ return "h-4 w-4";
1079
+ default:
1080
+ return "h-4 w-4";
1081
+ }
1082
+ };
1083
+ const getTextSize = () => {
1084
+ switch (size) {
1085
+ case "small":
1086
+ return "small";
1087
+ case "medium":
1088
+ return "medium";
1089
+ default:
1090
+ return "medium";
1091
+ }
1092
+ };
1093
+ const resolvedColor = color ?? "a1";
1094
+ const avatarElement = (jsxs("div", { className: "relative inline-block", children: [jsx("div", { className: cn(avatarVariants({ color: resolvedColor, size }), appearance === "inverse"
1095
+ ? inverseColorClasses[resolvedColor]
1096
+ : undefined, className), ...props, children: src && !imageError ? (jsx("img", { src: src, alt: alt || "Avatar", className: cn("h-full w-full object-cover", size === "small" ? "rounded-large" : "rounded-xlarge"), onError: handleImageError })) : (children) }), showStatus && (jsx("div", { className: cn(statusVariants({ size, statusColor })), children: statusIcon && (jsx("span", { className: cn("text-action-ink-on-primary-normal", getStatusIconSize()), children: statusIcon })) }))] }));
1097
+ // If no label or trailing component, return just the avatar
1098
+ if (!label && !trailingComponent) {
1099
+ return jsx("div", { ref: ref, children: avatarElement });
1100
+ }
1101
+ // Otherwise, return avatar with label and/or trailing component
1102
+ return (jsxs("div", { ref: ref, className: cn("inline-flex items-center gap-3", containerClassName), children: [avatarElement, label && (jsx(Text, { variant: "body", size: getTextSize(), weight: "medium", color: "default", as: "span", children: label })), trailingComponent && (jsx("span", { className: "ml-auto", children: trailingComponent }))] }));
1103
+ });
1104
+ Avatar.displayName = "Avatar";
1105
+
1106
+ const badgeVariants = cva("inline-flex items-center whitespace-nowrap transition-colors", {
1107
+ variants: {
1108
+ variant: {
1109
+ light: "",
1110
+ filled: "",
1111
+ },
1112
+ color: {
1113
+ primary: "",
1114
+ positive: "",
1115
+ negative: "",
1116
+ notice: "",
1117
+ info: "",
1118
+ neutral: "",
1119
+ },
1120
+ size: {
1121
+ small: "px-2 h-[var(--size-20)] gap-2 rounded-large text-body-small-medium",
1122
+ medium: "px-3 h-[var(--size-24)] gap-3 rounded-large text-body-medium-medium",
1123
+ large: "px-4 h-[var(--size-28)] gap-3 rounded-xlarge text-body-large-medium",
1124
+ },
1125
+ },
1126
+ compoundVariants: [
1127
+ // Light variant colors
1128
+ {
1129
+ variant: "light",
1130
+ color: "primary",
1131
+ class: "bg-action-fill-primary-faded text-action-ink-primary-normal",
1132
+ },
1133
+ {
1134
+ variant: "light",
1135
+ color: "positive",
1136
+ class: "bg-action-fill-positive-faded text-action-ink-positive-normal",
1137
+ },
1138
+ {
1139
+ variant: "light",
1140
+ color: "negative",
1141
+ class: "bg-action-fill-negative-faded text-action-ink-negative-normal",
1142
+ },
1143
+ {
1144
+ variant: "light",
1145
+ color: "notice",
1146
+ class: "bg-action-fill-notice-faded text-action-ink-notice-normal",
1147
+ },
1148
+ {
1149
+ variant: "light",
1150
+ color: "info",
1151
+ class: "bg-action-fill-info-faded text-action-ink-info-normal",
1152
+ },
1153
+ {
1154
+ variant: "light",
1155
+ color: "neutral",
1156
+ class: "bg-action-fill-neutral-faded text-action-ink-neutral-normal",
1157
+ },
1158
+ // Filled variant colors
1159
+ {
1160
+ variant: "filled",
1161
+ color: "primary",
1162
+ class: "bg-action-fill-primary-default text-action-ink-on-primary-normal",
1163
+ },
1164
+ {
1165
+ variant: "filled",
1166
+ color: "positive",
1167
+ class: "bg-action-fill-positive-default text-action-ink-on-primary-normal",
1168
+ },
1169
+ {
1170
+ variant: "filled",
1171
+ color: "negative",
1172
+ class: "bg-action-fill-negative-default text-action-ink-on-primary-normal",
1173
+ },
1174
+ {
1175
+ variant: "filled",
1176
+ color: "notice",
1177
+ class: "bg-action-fill-notice-default text-action-ink-on-primary-normal",
1178
+ },
1179
+ {
1180
+ variant: "filled",
1181
+ color: "info",
1182
+ class: "bg-action-fill-info-default text-action-ink-on-primary-normal",
1183
+ },
1184
+ {
1185
+ variant: "filled",
1186
+ color: "neutral",
1187
+ class: "bg-action-fill-neutral-default text-action-ink-on-primary-normal",
1188
+ },
1189
+ ],
1190
+ defaultVariants: {
1191
+ variant: "light",
1192
+ color: "info",
1193
+ size: "medium",
1194
+ },
1195
+ });
1196
+ const Badge = React.forwardRef(({ className, variant, size, color, showDot = false, children, ...props }, ref) => {
1197
+ const getDotColor = () => {
1198
+ if (variant === "filled") {
1199
+ return "bg-action-ink-on-primary-normal";
1200
+ }
1201
+ // Light variant - use the corresponding action color
1202
+ switch (color) {
1203
+ case "primary":
1204
+ return "bg-action-fill-primary-default";
1205
+ case "positive":
1206
+ return "bg-action-fill-positive-default";
1207
+ case "negative":
1208
+ return "bg-action-fill-negative-default";
1209
+ case "notice":
1210
+ return "bg-action-fill-notice-default";
1211
+ case "info":
1212
+ return "bg-action-fill-info-default";
1213
+ case "neutral":
1214
+ return "bg-action-fill-neutral-default";
1215
+ default:
1216
+ return "bg-action-fill-info-default";
1217
+ }
1218
+ };
1219
+ const getDotSize = () => {
1220
+ if (size === "small") {
1221
+ return "h-2 w-2";
1222
+ }
1223
+ if (size === "medium") {
1224
+ return "h-[6px] w-[6px]";
1225
+ }
1226
+ return "h-3 w-3";
1227
+ };
1228
+ return (jsxs("div", { ref: ref, className: cn(badgeVariants({ variant, size, color }), className), ...props, children: [showDot && (jsx("span", { className: cn("rounded-full", getDotColor(), getDotSize()), "aria-hidden": "true" })), children] }));
1229
+ });
1230
+ Badge.displayName = "Badge";
578
1231
 
579
1232
  const buttonGroupVariants = cva("inline-flex", {
580
1233
  variants: {
@@ -756,99 +1409,6 @@ const FormFooter = React.forwardRef(({ helperText, trailingText, validationState
756
1409
  });
757
1410
  FormFooter.displayName = "FormFooter";
758
1411
 
759
- /**
760
- * ==============================================
761
- * HOW TO ADD A NEW ICON:
762
- * ==============================================
763
- *
764
- * 1. Add your SVG file to: src/assets/icons/{iconName}.svg
765
- *
766
- * 2. Copy the SVG content from the file
767
- *
768
- * 3. Add it to the iconRegistry below:
769
- * iconName: `<svg>...</svg>`,
770
- *
771
- * 4. Use it anywhere in your app:
772
- * <Icon name="iconName" size={24} />
773
- *
774
- * The Icon component will automatically:
775
- * - Replace hardcoded colors with currentColor
776
- * - Allow you to control color via className or style
777
- * - Resize based on the size prop
778
- * ==============================================
779
- */
780
- /**
781
- * Icon registry - maps icon names to their SVG content
782
- * Add your icons here by copying the SVG content from your icon files
783
- */
784
- const iconRegistry = {
785
- // Tick/Check icon
786
- tick: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
787
- <path d="M10.364 15.1924L19.5564 6L20.9706 7.41421L10.364 18.0208L4 11.6569L5.41422 10.2427L10.364 15.1924Z" fill="#081416"/>
788
- </svg>`,
789
- // Alias: check points to the same icon as tick
790
- check: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
791
- <path d="M10.364 15.1924L19.5564 6L20.9706 7.41421L10.364 18.0208L4 11.6569L5.41422 10.2427L10.364 15.1924Z" fill="#081416"/>
792
- </svg>`,
793
- add: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
794
- <path d="M12.9 11.0999L21 11.0997V12.8997L12.9 12.8999V21H11.1V12.8999L3.00004 12.9001L3 11.1001L11.1 11.0999L11.0999 3.00001L12.8999 3L12.9 11.0999Z" fill="#081416"/>
795
- </svg>`,
796
- };
797
- const Icon = ({ name, size = 24, className = "", style = {}, ...props }) => {
798
- const svgContent = iconRegistry[name];
799
- if (!svgContent) {
800
- console.warn(`Icon "${String(name)}" not found in registry.\n` +
801
- `Available icons: ${Object.keys(iconRegistry).join(", ")}`);
802
- return null;
803
- }
804
- // Parse the SVG content
805
- const parser = new DOMParser();
806
- const svgDoc = parser.parseFromString(svgContent, "image/svg+xml");
807
- const svgElement = svgDoc.querySelector("svg");
808
- if (!svgElement) {
809
- console.error(`Invalid SVG content for icon "${String(name)}"`);
810
- return null;
811
- }
812
- // Get the viewBox for proper scaling
813
- const viewBox = svgElement.getAttribute("viewBox") || "0 0 24 24";
814
- // Get all SVG content as string and replace hardcoded colors with currentColor
815
- let innerSVG = svgElement.innerHTML;
816
- // Replace common hardcoded colors with currentColor
817
- // This allows the icon to inherit text color from parent
818
- innerSVG = innerSVG
819
- .replace(/fill="[^"]*"/g, 'fill="currentColor"')
820
- .replace(/stroke="[^"]*"/g, 'stroke="currentColor"');
821
- return (jsx("svg", { width: size, height: size, viewBox: viewBox, className: className, style: style, xmlns: "http://www.w3.org/2000/svg", ...props, dangerouslySetInnerHTML: { __html: innerSVG } }));
822
- };
823
- /**
824
- * Get all available icon names from the registry
825
- * @returns Array of registered icon names
826
- *
827
- * @example
828
- * ```tsx
829
- * const icons = getAvailableIcons();
830
- * console.log(icons); // ['tick', 'check', ...]
831
- * ```
832
- */
833
- function getAvailableIcons() {
834
- return Object.keys(iconRegistry);
835
- }
836
- /**
837
- * Check if an icon exists in the registry
838
- * @param name - Icon name to check
839
- * @returns true if the icon exists
840
- *
841
- * @example
842
- * ```tsx
843
- * if (hasIcon('tick')) {
844
- * // Icon exists
845
- * }
846
- * ```
847
- */
848
- function hasIcon(name) {
849
- return name in iconRegistry;
850
- }
851
-
852
1412
  const checkboxVariants = cva("relative inline-flex items-center justify-center shrink-0 transition-all cursor-pointer", {
853
1413
  variants: {
854
1414
  size: {
@@ -1298,141 +1858,6 @@ const ListItem = React.forwardRef(({ className, type = "single", leadingIcon, ti
1298
1858
  });
1299
1859
  ListItem.displayName = "ListItem";
1300
1860
 
1301
- const linkVariants = cva("inline-flex items-center gap-1 whitespace-nowrap transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none decoration-1 underline-offset-4", {
1302
- variants: {
1303
- type: {
1304
- anchor: "hover:underline",
1305
- action: "no-underline cursor-pointer",
1306
- },
1307
- color: {
1308
- primary: "",
1309
- positive: "",
1310
- negative: "",
1311
- notice: "",
1312
- info: "",
1313
- neutral: "",
1314
- },
1315
- size: {
1316
- xsmall: "text-body-xsmall-medium gap-1",
1317
- small: "text-body-small-medium gap-1",
1318
- medium: "text-body-medium-medium gap-1.5",
1319
- large: "text-body-large-medium gap-1.5",
1320
- },
1321
- isIconOnly: {
1322
- true: "no-underline",
1323
- false: "",
1324
- },
1325
- isDisabled: {
1326
- true: "cursor-not-allowed opacity-50",
1327
- false: "cursor-pointer",
1328
- },
1329
- },
1330
- compoundVariants: [
1331
- // Primary color variants
1332
- {
1333
- color: "primary",
1334
- class: `text-action-ink-primary-normal
1335
- hover:text-action-ink-primary-subtle
1336
- hover:decoration-action-outline-primary-hover
1337
- disabled:text-action-ink-primary-disabled
1338
- focus:text-action-ink-primary-hover
1339
- `,
1340
- },
1341
- // Positive color variants
1342
- {
1343
- color: "positive",
1344
- class: `text-action-ink-positive-normal
1345
- hover:text-action-ink-positive-subtle
1346
- hover:decoration-action-outline-positive-hover
1347
- hover:text-action-ink-positive-hover
1348
- disabled:text-action-ink-positive-disabled
1349
- focus:text-action-ink-positive-hover
1350
- `,
1351
- },
1352
- // Negative color variants
1353
- {
1354
- color: "negative",
1355
- class: `text-action-ink-negative-normal
1356
- hover:text-action-ink-negative-subtle
1357
- hover:decoration-action-outline-negative-hover
1358
- hover:text-action-ink-negative-hover
1359
- disabled:text-action-ink-negative-disabled
1360
- focus:text-action-ink-negative-hover
1361
- `,
1362
- },
1363
- // Notice color variants
1364
- {
1365
- color: "notice",
1366
- class: `text-action-ink-notice-normal
1367
- hover:text-action-ink-notice-subtle
1368
- hover:decoration-action-outline-notice-hover
1369
- hover:text-action-ink-notice-hover
1370
- disabled:text-action-ink-notice-disabled
1371
- focus:text-action-ink-notice-hover
1372
- `,
1373
- },
1374
- // Info color variants
1375
- {
1376
- color: "info",
1377
- class: `text-action-ink-info-normal
1378
- hover:text-action-ink-info-subtle
1379
- hover:decoration-action-outline-info-hover
1380
- hover:text-action-ink-info-hover
1381
- disabled:text-action-ink-info-disabled
1382
- focus:text-action-ink-info-hover
1383
- `,
1384
- },
1385
- // Neutral color variants
1386
- {
1387
- color: "neutral",
1388
- class: `text-action-ink-neutral-normal
1389
- hover:text-action-ink-neutral-subtle
1390
- hover:decoration-action-outline-neutral-hover
1391
- hover:text-action-ink-neutral-hover
1392
- disabled:text-action-ink-neutral-disabled
1393
- focus:text-action-ink-neutral-hover
1394
- `,
1395
- },
1396
- ],
1397
- defaultVariants: {
1398
- type: "anchor",
1399
- color: "primary",
1400
- size: "medium",
1401
- isIconOnly: false,
1402
- isDisabled: false,
1403
- },
1404
- });
1405
- const Link = React.forwardRef(({ className, type = "anchor", color = "primary", size = "medium", isIconOnly = false, isDisabled = false, asChild = false, showIcon = false, icon, leadingIcon, trailingIcon, children, onClick, ...props }, ref) => {
1406
- const Comp = asChild ? Slot : "a";
1407
- const handleClick = (e) => {
1408
- if (isDisabled) {
1409
- e.preventDefault();
1410
- return;
1411
- }
1412
- onClick?.(e);
1413
- };
1414
- // Icon size based on link size
1415
- const iconSize = {
1416
- xsmall: 12,
1417
- small: 14,
1418
- medium: 16,
1419
- large: 18,
1420
- }[size];
1421
- // Determine what to show as trailing icon
1422
- // Priority: trailingIcon > (showIcon && icon) > (showIcon && default ExternalLink)
1423
- const finalTrailingIcon = trailingIcon || (showIcon && (icon || jsx(ExternalLink, { size: iconSize })));
1424
- const linkContent = (jsxs(Fragment, { children: [leadingIcon && !isIconOnly && (jsx("span", { className: "inline-flex items-center", children: leadingIcon })), !isIconOnly && children, isIconOnly && children, finalTrailingIcon && !isIconOnly && (jsx("span", { className: "inline-flex items-center", children: finalTrailingIcon })), isIconOnly &&
1425
- (leadingIcon || finalTrailingIcon || (jsx(ExternalLink, { size: iconSize })))] }));
1426
- return (jsx(Comp, { className: cn(linkVariants({
1427
- type,
1428
- color,
1429
- size,
1430
- isIconOnly,
1431
- isDisabled,
1432
- }), className), ref: ref, onClick: handleClick, "aria-disabled": isDisabled, tabIndex: isDisabled ? -1 : undefined, ...props, children: linkContent }));
1433
- });
1434
- Link.displayName = "Link";
1435
-
1436
1861
  const DropdownMenu = React.forwardRef(({ items = [], customContent, sectionHeading, isLoading = false, isEmpty = false, emptyTitle = "No Search Results Found", emptyDescription = "Add description of what the user can search for here.", emptyLinkText = "Link to support site", onEmptyLinkClick, primaryButtonText = "Primary", secondaryButtonText = "Secondary", onPrimaryClick, onSecondaryClick, showChevron = false, emptyIcon, disableFooter = false, showFooter, footerLayout = "horizontal", onClose, focusedIndex = -1, className, width = "auto", }, ref) => {
1437
1862
  const renderContent = () => {
1438
1863
  if (isLoading) {
@@ -3241,5 +3666,5 @@ const TextArea = React.forwardRef(({ label, helperText, errorText, successText,
3241
3666
  });
3242
3667
  TextArea.displayName = "TextArea";
3243
3668
 
3244
- export { Avatar, AvatarCell, Badge, Button, ButtonGroup, Checkbox, Counter, Divider, Dropdown, DropdownMenu, FormFooter, FormHeader, Icon, IconCell, Link, ListItem, Modal, NumberCell, Pagination, Radio, SearchableDropdown, Select, Skeleton, SlotCell, SpacerCell, Switch, TabItem, Table, TableDetailPanel, Tabs, Text, TextArea, TextField, Tooltip, avatarVariants, badgeVariants, buttonGroupVariants, buttonVariants, checkboxVariants, cn, counterVariants, dropdownVariants, getAvailableIcons, hasIcon, iconRegistry, linkVariants, listItemVariants, paginationVariants, radioVariants, selectVariants, switchVariants, tableCellVariants, tableHeaderVariants, tableVariants, textAreaVariants, textFieldVariants, tooltipVariants };
3669
+ export { Alert, Avatar, AvatarCell, Badge, Button, ButtonGroup, Checkbox, Counter, Divider, Dropdown, DropdownMenu, FormFooter, FormHeader, Icon, IconButton, IconCell, Link, ListItem, Modal, NumberCell, Pagination, Radio, SearchableDropdown, Select, Skeleton, SlotCell, SpacerCell, Switch, TabItem, Table, TableDetailPanel, Tabs, Text, TextArea, TextField, Tooltip, alertVariants, avatarVariants, badgeVariants, buttonGroupVariants, buttonVariants, checkboxVariants, cn, counterVariants, dropdownVariants, getAvailableIcons, hasIcon, iconButtonVariants, iconRegistry, linkVariants, listItemVariants, paginationVariants, radioVariants, selectVariants, switchVariants, tableCellVariants, tableHeaderVariants, tableVariants, textAreaVariants, textFieldVariants, tooltipVariants };
3245
3670
  //# sourceMappingURL=index.esm.js.map