infinity-ui-elements 1.3.1 → 1.4.1-beta.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 (80) hide show
  1. package/README.md +62 -1
  2. package/dist/components/Badge/Badge.d.ts +28 -0
  3. package/dist/components/Badge/Badge.d.ts.map +1 -0
  4. package/dist/components/Badge/Badge.stories.d.ts +16 -0
  5. package/dist/components/Badge/Badge.stories.d.ts.map +1 -0
  6. package/dist/components/Badge/index.d.ts +3 -0
  7. package/dist/components/Badge/index.d.ts.map +1 -0
  8. package/dist/components/Button/Button.d.ts +1 -1
  9. package/dist/components/Button/Button.d.ts.map +1 -1
  10. package/dist/components/Checkbox/Checkbox.d.ts +49 -0
  11. package/dist/components/Checkbox/Checkbox.d.ts.map +1 -0
  12. package/dist/components/Checkbox/Checkbox.stories.d.ts +23 -0
  13. package/dist/components/Checkbox/Checkbox.stories.d.ts.map +1 -0
  14. package/dist/components/Checkbox/index.d.ts +3 -0
  15. package/dist/components/Checkbox/index.d.ts.map +1 -0
  16. package/dist/components/Counter/Counter.d.ts +24 -0
  17. package/dist/components/Counter/Counter.d.ts.map +1 -0
  18. package/dist/components/Counter/Counter.stories.d.ts +14 -0
  19. package/dist/components/Counter/Counter.stories.d.ts.map +1 -0
  20. package/dist/components/Counter/index.d.ts +3 -0
  21. package/dist/components/Counter/index.d.ts.map +1 -0
  22. package/dist/components/Divider/Divider.d.ts +37 -0
  23. package/dist/components/Divider/Divider.d.ts.map +1 -0
  24. package/dist/components/Divider/Divider.stories.d.ts +12 -0
  25. package/dist/components/Divider/Divider.stories.d.ts.map +1 -0
  26. package/dist/components/Divider/index.d.ts +2 -0
  27. package/dist/components/Divider/index.d.ts.map +1 -0
  28. package/dist/components/FormFooter/FormFooter.d.ts +38 -0
  29. package/dist/components/FormFooter/FormFooter.d.ts.map +1 -0
  30. package/dist/components/FormFooter/FormFooter.stories.d.ts +50 -0
  31. package/dist/components/FormFooter/FormFooter.stories.d.ts.map +1 -0
  32. package/dist/components/FormFooter/index.d.ts +3 -0
  33. package/dist/components/FormFooter/index.d.ts.map +1 -0
  34. package/dist/components/FormHeader/FormHeader.d.ts +58 -0
  35. package/dist/components/FormHeader/FormHeader.d.ts.map +1 -0
  36. package/dist/components/FormHeader/FormHeader.stories.d.ts +65 -0
  37. package/dist/components/FormHeader/FormHeader.stories.d.ts.map +1 -0
  38. package/dist/components/FormHeader/index.d.ts +3 -0
  39. package/dist/components/FormHeader/index.d.ts.map +1 -0
  40. package/dist/components/ListItem/ListItem.d.ts +63 -0
  41. package/dist/components/ListItem/ListItem.d.ts.map +1 -0
  42. package/dist/components/ListItem/ListItem.stories.d.ts +66 -0
  43. package/dist/components/ListItem/ListItem.stories.d.ts.map +1 -0
  44. package/dist/components/ListItem/index.d.ts +3 -0
  45. package/dist/components/ListItem/index.d.ts.map +1 -0
  46. package/dist/components/Radio/Radio.d.ts +45 -0
  47. package/dist/components/Radio/Radio.d.ts.map +1 -0
  48. package/dist/components/Radio/Radio.stories.d.ts +23 -0
  49. package/dist/components/Radio/Radio.stories.d.ts.map +1 -0
  50. package/dist/components/Radio/index.d.ts +3 -0
  51. package/dist/components/Radio/index.d.ts.map +1 -0
  52. package/dist/components/Switch/Switch.d.ts +39 -0
  53. package/dist/components/Switch/Switch.d.ts.map +1 -0
  54. package/dist/components/Switch/Switch.stories.d.ts +37 -0
  55. package/dist/components/Switch/Switch.stories.d.ts.map +1 -0
  56. package/dist/components/Switch/index.d.ts +3 -0
  57. package/dist/components/Switch/index.d.ts.map +1 -0
  58. package/dist/components/Text/Text.d.ts +2 -9
  59. package/dist/components/Text/Text.d.ts.map +1 -1
  60. package/dist/components/Text/index.d.ts +1 -1
  61. package/dist/components/Text/index.d.ts.map +1 -1
  62. package/dist/components/TextArea/TextArea.d.ts +31 -0
  63. package/dist/components/TextArea/TextArea.d.ts.map +1 -0
  64. package/dist/components/TextArea/TextArea.stories.d.ts +27 -0
  65. package/dist/components/TextArea/TextArea.stories.d.ts.map +1 -0
  66. package/dist/components/TextArea/index.d.ts +3 -0
  67. package/dist/components/TextArea/index.d.ts.map +1 -0
  68. package/dist/components/TextField/TextField.d.ts +4 -0
  69. package/dist/components/TextField/TextField.d.ts.map +1 -1
  70. package/dist/index.css +1 -1
  71. package/dist/index.d.ts +11 -0
  72. package/dist/index.d.ts.map +1 -1
  73. package/dist/index.esm.js +1208 -145
  74. package/dist/index.esm.js.map +1 -1
  75. package/dist/index.js +1227 -144
  76. package/dist/index.js.map +1 -1
  77. package/dist/lib/icons.d.ts +96 -0
  78. package/dist/lib/icons.d.ts.map +1 -0
  79. package/dist/lib/utils.d.ts.map +1 -1
  80. package/package.json +6 -2
package/dist/index.esm.js CHANGED
@@ -1,16 +1,28 @@
1
- import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
1
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
- import { Slot } from '@radix-ui/react-slot';
4
3
  import { cva } from 'class-variance-authority';
5
- import { PulseLoader, ClipLoader } from 'react-spinners';
6
4
  import { clsx } from 'clsx';
7
5
  import { twMerge } from 'tailwind-merge';
6
+ import { Slot } from '@radix-ui/react-slot';
7
+ import { PulseLoader, ClipLoader } from 'react-spinners';
8
8
 
9
9
  // Define patterns for custom classes that should be preserved
10
10
  // This approach is more scalable than hardcoding individual class names
11
11
  const CUSTOM_CLASS_PATTERNS = [
12
12
  // Custom font classes
13
13
  /^font-(functional|display)$/,
14
+ // Custom font-size classes
15
+ /^font-size-/,
16
+ // Custom leading (line-height) classes
17
+ /^leading-(00|25|50|75|100|200|300|400|500|600|700|800|900|1000|1100)$/,
18
+ // Custom text utility classes (text-display, text-heading, text-body, text-caption variants)
19
+ /^text-(display|heading|body|caption)(-\w+)?(-\w+)?$/,
20
+ // Text weight classes
21
+ /^text-weight-/,
22
+ // Text color classes
23
+ /^text-color-/,
24
+ /^outline-width-/,
25
+ /^border-width-/,
14
26
  // Custom spacing classes (example)
15
27
  // /^spacing-(xs|sm|md|lg|xl)$/,
16
28
  // Custom color classes (example)
@@ -33,7 +45,133 @@ function cn(...inputs) {
33
45
  return clsx(mergedStandard, customClasses);
34
46
  }
35
47
 
36
- const buttonVariants = cva("items-center gap-3 justify-center whitespace-nowrap text-sm font-medium 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", {
48
+ const badgeVariants = cva("inline-flex items-center whitespace-nowrap transition-colors", {
49
+ variants: {
50
+ variant: {
51
+ light: "",
52
+ filled: "",
53
+ },
54
+ color: {
55
+ primary: "",
56
+ positive: "",
57
+ negative: "",
58
+ notice: "",
59
+ info: "",
60
+ neutral: "",
61
+ },
62
+ size: {
63
+ small: "px-2 h-[var(--size-20)] gap-2 rounded-large text-body-small-medium",
64
+ medium: "px-3 h-[var(--size-24)] gap-3 rounded-large text-body-medium-medium",
65
+ large: "px-4 h-[var(--size-28)] gap-3 rounded-xlarge text-body-large-medium",
66
+ },
67
+ },
68
+ compoundVariants: [
69
+ // Light variant colors
70
+ {
71
+ variant: "light",
72
+ color: "primary",
73
+ class: "bg-action-fill-primary-faded text-action-ink-primary-normal",
74
+ },
75
+ {
76
+ variant: "light",
77
+ color: "positive",
78
+ class: "bg-action-fill-positive-faded text-action-ink-positive-normal",
79
+ },
80
+ {
81
+ variant: "light",
82
+ color: "negative",
83
+ class: "bg-action-fill-negative-faded text-action-ink-negative-normal",
84
+ },
85
+ {
86
+ variant: "light",
87
+ color: "notice",
88
+ class: "bg-action-fill-notice-faded text-action-ink-notice-normal",
89
+ },
90
+ {
91
+ variant: "light",
92
+ color: "info",
93
+ class: "bg-action-fill-info-faded text-action-ink-info-normal",
94
+ },
95
+ {
96
+ variant: "light",
97
+ color: "neutral",
98
+ class: "bg-action-fill-neutral-faded text-action-ink-neutral-normal",
99
+ },
100
+ // Filled variant colors
101
+ {
102
+ variant: "filled",
103
+ color: "primary",
104
+ class: "bg-action-fill-primary-default text-action-ink-on-primary-normal",
105
+ },
106
+ {
107
+ variant: "filled",
108
+ color: "positive",
109
+ class: "bg-action-fill-positive-default text-action-ink-on-primary-normal",
110
+ },
111
+ {
112
+ variant: "filled",
113
+ color: "negative",
114
+ class: "bg-action-fill-negative-default text-action-ink-on-primary-normal",
115
+ },
116
+ {
117
+ variant: "filled",
118
+ color: "notice",
119
+ class: "bg-action-fill-notice-default text-action-ink-on-primary-normal",
120
+ },
121
+ {
122
+ variant: "filled",
123
+ color: "info",
124
+ class: "bg-action-fill-info-default text-action-ink-on-primary-normal",
125
+ },
126
+ {
127
+ variant: "filled",
128
+ color: "neutral",
129
+ class: "bg-action-fill-neutral-default text-action-ink-on-primary-normal",
130
+ },
131
+ ],
132
+ defaultVariants: {
133
+ variant: "light",
134
+ color: "info",
135
+ size: "medium",
136
+ },
137
+ });
138
+ const Badge = React.forwardRef(({ className, variant, size, color, showDot = false, children, ...props }, ref) => {
139
+ const getDotColor = () => {
140
+ if (variant === "filled") {
141
+ return "bg-action-ink-on-primary-normal";
142
+ }
143
+ // Light variant - use the corresponding action color
144
+ switch (color) {
145
+ case "primary":
146
+ return "bg-action-fill-primary-default";
147
+ case "positive":
148
+ return "bg-action-fill-positive-default";
149
+ case "negative":
150
+ return "bg-action-fill-negative-default";
151
+ case "notice":
152
+ return "bg-action-fill-notice-default";
153
+ case "info":
154
+ return "bg-action-fill-info-default";
155
+ case "neutral":
156
+ return "bg-action-fill-neutral-default";
157
+ default:
158
+ return "bg-action-fill-info-default";
159
+ }
160
+ };
161
+ const getDotSize = () => {
162
+ if (size === "small") {
163
+ return "h-2 w-2";
164
+ }
165
+ if (size === "medium") {
166
+ return "h-[6px] w-[6px]";
167
+ }
168
+ return "h-3 w-3";
169
+ };
170
+ 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] }));
171
+ });
172
+ Badge.displayName = "Badge";
173
+
174
+ 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", {
37
175
  variants: {
38
176
  variant: {
39
177
  primary: "bg-action-fill-primary-default text-action-ink-on-primary-normal hover:bg-action-fill-primary-hover",
@@ -49,10 +187,10 @@ const buttonVariants = cva("items-center gap-3 justify-center whitespace-nowrap
49
187
  neutral: "",
50
188
  },
51
189
  size: {
52
- xsmall: "md:h-[28px] px-3 text-xs rounded-medium",
53
- small: "md:h-[32px] px-4 text-xs rounded-medium",
54
- medium: "md:h-[36px] px-6 py-2 rounded-medium",
55
- large: "md:h-[44px] px-6 text-base rounded-xlarge",
190
+ xsmall: "md:h-[28px] px-3 rounded-medium text-body-small-medium",
191
+ small: "md:h-[32px] px-4 rounded-medium text-body-small-medium",
192
+ medium: "md:h-[36px] px-6 py-2 rounded-medium text-body-medium-medium",
193
+ large: "md:h-[44px] px-6 rounded-xlarge text-body-large-medium",
56
194
  },
57
195
  isIconOnly: {
58
196
  true: "aspect-square p-0",
@@ -79,7 +217,8 @@ const buttonVariants = cva("items-center gap-3 justify-center whitespace-nowrap
79
217
  class: `bg-action-fill-primary-default text-action-ink-on-primary-normal
80
218
  hover:bg-action-fill-primary-hover
81
219
  disabled:bg-action-fill-primary-disabled
82
- disabled:text-action-ink-primary-disabled
220
+ disabled:text-action-ink-primary-disabled,
221
+ active:bg-action-fill-primary-activated
83
222
  `,
84
223
  },
85
224
  {
@@ -89,6 +228,7 @@ const buttonVariants = cva("items-center gap-3 justify-center whitespace-nowrap
89
228
  hover:bg-action-fill-positive-hover
90
229
  disabled:bg-action-fill-primary-disabled
91
230
  disabled:text-action-ink-primary-disabled
231
+ active:bg-action-fill-positive-activated
92
232
  `,
93
233
  },
94
234
  {
@@ -98,6 +238,7 @@ const buttonVariants = cva("items-center gap-3 justify-center whitespace-nowrap
98
238
  hover:bg-action-fill-negative-hover
99
239
  disabled:bg-action-fill-negative-disabled
100
240
  disabled:text-action-ink-negative-disabled
241
+ active:bg-action-fill-negative-activated
101
242
  `,
102
243
  },
103
244
  {
@@ -107,6 +248,7 @@ const buttonVariants = cva("items-center gap-3 justify-center whitespace-nowrap
107
248
  hover:bg-action-fill-notice-hover
108
249
  disabled:bg-action-fill-notice-disabled
109
250
  disabled:text-action-ink-notice-disabled
251
+ active:bg-action-fill-notice-activated
110
252
  `,
111
253
  },
112
254
  {
@@ -116,6 +258,7 @@ const buttonVariants = cva("items-center gap-3 justify-center whitespace-nowrap
116
258
  hover:bg-action-fill-info-hover
117
259
  disabled:bg-action-fill-info-disabled
118
260
  disabled:text-action-ink-info-disabled
261
+ active:bg-action-fill-info-activated
119
262
  `,
120
263
  },
121
264
  {
@@ -125,42 +268,96 @@ const buttonVariants = cva("items-center gap-3 justify-center whitespace-nowrap
125
268
  hover:bg-action-fill-neutral-hover
126
269
  disabled:bg-action-fill-neutral-disabled
127
270
  disabled:text-action-ink-neutral-disabled
271
+ active:bg-action-fill-neutral-activated
128
272
  `,
129
273
  },
130
274
  // Secondary variant colors
131
275
  {
132
276
  variant: "secondary",
133
277
  color: "primary",
134
- class: `border-action-outline-info-faded text-action-ink-primary-normal hover:border-action-outline-info-faded-hover
278
+ class: `
279
+ border-action-outline-info-faded
280
+ text-action-ink-primary-normal
281
+ hover:border-action-outline-primary-faded-hover
282
+ hover:bg-action-fill-primary-faded-hover
135
283
  disabled:bg-action-outline-info-disabled
136
284
  disabled:text-action-ink-primary-disabled
137
285
  disabled:border-action-outline-primary-disabled
286
+ active:border-action-outline-primary-faded-activated
287
+ active:bg-action-fill-primary-faded-activated
138
288
  `,
139
289
  },
140
290
  {
141
291
  variant: "secondary",
142
292
  color: "positive",
143
- class: "border-action-outline-positive-faded text-action-ink-positive-normal hover:border-action-outline-positive-faded-hover disabled:bg-action-outline-positive-disabled",
293
+ class: `
294
+ border-action-outline-positive-faded
295
+ text-action-ink-positive-normal
296
+ hover:border-action-outline-positive-faded-hover
297
+ hover:bg-action-fill-positive-faded-hover
298
+ disabled:bg-action-outline-positive-disabled
299
+ disabled:text-action-ink-positive-disabled
300
+ disabled:border-action-outline-positive-disabled
301
+ active:border-action-outline-positive-faded-activated
302
+ active:bg-action-fill-positive-faded-activated
303
+ `,
144
304
  },
145
305
  {
146
306
  variant: "secondary",
147
307
  color: "negative",
148
- class: "border-action-outline-negative-faded text-action-ink-negative-normal hover:border-action-outline-negative-faded-hover disabled:bg-action-outline-negative-disabled",
308
+ class: `
309
+ border-action-outline-negative-faded
310
+ text-action-ink-negative-normal
311
+ hover:border-action-outline-negative-faded-hover
312
+ hover:bg-action-fill-negative-faded-hover
313
+ disabled:bg-action-outline-negative-disabled
314
+ disabled:text-action-ink-negative-disabled
315
+ disabled:border-action-outline-negative-disabled
316
+ active:border-action-outline-negative-faded-activated
317
+ active:bg-action-fill-negative-faded-activated
318
+ `,
149
319
  },
150
320
  {
151
321
  variant: "secondary",
152
322
  color: "notice",
153
- class: "border-action-outline-notice-faded text-action-ink-notice-normal hover:border-action-outline-notice-faded-hover disabled:bg-action-outline-notice-disabled",
323
+ class: `
324
+ border-action-outline-notice-faded
325
+ text-action-ink-notice-normal
326
+ hover:border-action-outline-notice-faded-hover
327
+ hover:bg-action-fill-notice-faded-hover
328
+ disabled:bg-action-outline-notice-disabled
329
+ disabled:text-action-ink-notice-disabled
330
+ disabled:border-action-outline-notice-disabled
331
+ active:border-action-outline-notice-faded-activated
332
+ active:bg-action-fill-notice-faded-activated
333
+ `,
154
334
  },
155
335
  {
156
336
  variant: "secondary",
157
337
  color: "info",
158
- class: "border-action-outline-info-faded text-action-ink-info-normal hover:border-action-outline-info-faded-hover disabled:bg-action-outline-info-disabled",
338
+ class: `border-action-outline-info-faded
339
+ text-action-ink-info-normal
340
+ hover:border-action-outline-info-faded-hover
341
+ hover:bg-action-fill-info-faded-hover
342
+ disabled:bg-action-outline-info-disabled
343
+ disabled:text-action-ink-info-disabled
344
+ disabled:border-action-outline-info-disabled
345
+ active:border-action-outline-info-faded-activated
346
+ active:bg-action-fill-info-faded-activated
347
+ `,
159
348
  },
160
349
  {
161
350
  variant: "secondary",
162
351
  color: "neutral",
163
- class: "border-action-outline-neutral-faded text-action-ink-neutral-normal hover:bg-action-outline-neutral-faded-hover",
352
+ class: `border-action-outline-neutral-faded
353
+ text-action-ink-neutral-normal
354
+ hover:bg-action-outline-neutral-faded-hover
355
+ hover:bg-action-fill-neutral-faded-hover
356
+ disabled:text-action-ink-neutral-disabled
357
+ disabled:border-action-outline-neutral-disabled
358
+ active:border-action-outline-neutral-faded-activated
359
+ active:bg-action-fill-neutral-faded-activated
360
+ `,
164
361
  },
165
362
  // Tertiary variant colors
166
363
  {
@@ -169,6 +366,7 @@ const buttonVariants = cva("items-center gap-3 justify-center whitespace-nowrap
169
366
  class: `text-action-ink-primary-normal
170
367
  hover:bg-action-fill-primary-faded
171
368
  disabled:text-action-ink-on-primary-muted
369
+ active:bg-action-fill-primary-faded-activated
172
370
  `,
173
371
  },
174
372
  {
@@ -177,6 +375,7 @@ const buttonVariants = cva("items-center gap-3 justify-center whitespace-nowrap
177
375
  class: `text-action-ink-positive-normal
178
376
  hover:bg-action-fill-positive-faded
179
377
  disabled:text-action-ink-on-positive-muted
378
+ active:bg-action-fill-positive-faded-activated
180
379
  `,
181
380
  },
182
381
  {
@@ -185,6 +384,7 @@ const buttonVariants = cva("items-center gap-3 justify-center whitespace-nowrap
185
384
  class: `text-action-ink-negative-normal
186
385
  hover:bg-action-fill-negative-faded
187
386
  disabled:text-action-ink-on-negative-muted
387
+ active:bg-action-fill-negative-faded-activated
188
388
  `,
189
389
  },
190
390
  {
@@ -193,6 +393,7 @@ const buttonVariants = cva("items-center gap-3 justify-center whitespace-nowrap
193
393
  class: `text-action-ink-notice-normal
194
394
  hover:bg-action-fill-notice-faded
195
395
  disabled:text-action-ink-on-notice-muted
396
+ active:bg-action-fill-notice-faded-activated
196
397
  `,
197
398
  },
198
399
  {
@@ -201,6 +402,7 @@ const buttonVariants = cva("items-center gap-3 justify-center whitespace-nowrap
201
402
  class: `text-action-ink-info-normal
202
403
  hover:bg-action-fill-info-faded
203
404
  disabled:text-action-ink-on-notice-muted
405
+ active:bg-action-fill-info-faded-activated
204
406
  `,
205
407
  },
206
408
  {
@@ -209,6 +411,7 @@ const buttonVariants = cva("items-center gap-3 justify-center whitespace-nowrap
209
411
  class: `text-action-ink-neutral-normal
210
412
  hover:bg-action-fill-neutral-faded
211
413
  disabled:text-action-ink-on-notice-muted
414
+ active:bg-action-fill-neutral-faded-activated
212
415
  `,
213
416
  },
214
417
  // Icon only sizing
@@ -258,174 +461,533 @@ const Button = React.forwardRef(({ className, variant = "primary", color = "prim
258
461
  });
259
462
  Button.displayName = "Button";
260
463
 
261
- const textVariants = cva("", {
464
+ // Helper function to get the text utility class name
465
+ function getTextClassName(variant = "body", size = "medium", weight = "regular", color = "default") {
466
+ // Build the base class name
467
+ let baseClass = `text-${variant}`;
468
+ // Add size
469
+ if (size) {
470
+ baseClass += `-${size}`;
471
+ }
472
+ // Add weight
473
+ if (weight) {
474
+ baseClass += `-${weight}`;
475
+ }
476
+ // Add color class separately
477
+ const colorClass = `text-color-${color}`;
478
+ return `${baseClass} ${colorClass}`;
479
+ }
480
+ const Text = React.forwardRef(({ className, variant = "body", size = "medium", weight = "regular", color = "default", as = "p", children, ...props }, ref) => {
481
+ const Component = as;
482
+ const textClass = getTextClassName(variant, size, weight, color);
483
+ return React.createElement(Component, {
484
+ className: cn(textClass, className),
485
+ ref,
486
+ ...props,
487
+ }, children);
488
+ });
489
+ Text.displayName = "Text";
490
+
491
+ const FormFooter = React.forwardRef(({ helperText, trailingText, validationState = "default", size = "medium", isDisabled = false, className, helperTextClassName, trailingTextClassName, }, ref) => {
492
+ // Size-based configurations
493
+ const sizeConfig = {
494
+ small: {
495
+ textSize: "xsmall",
496
+ iconSize: 12,
497
+ gap: "gap-1",
498
+ },
499
+ medium: {
500
+ textSize: "small",
501
+ iconSize: 14,
502
+ gap: "gap-1",
503
+ },
504
+ large: {
505
+ textSize: "medium",
506
+ iconSize: 16,
507
+ gap: "gap-1.5",
508
+ },
509
+ };
510
+ const config = sizeConfig[size];
511
+ // Determine text color based on validation state and disabled state
512
+ const getTextColor = () => {
513
+ if (isDisabled)
514
+ return "disabled";
515
+ if (validationState === "positive")
516
+ return "positive";
517
+ if (validationState === "negative")
518
+ return "negative";
519
+ if (validationState === "default")
520
+ return "default";
521
+ return "default";
522
+ };
523
+ // Don't render anything if there's no content
524
+ if (!helperText && !trailingText) {
525
+ return null;
526
+ }
527
+ return (jsxs("div", { ref: ref, className: cn("flex items-center justify-between px-1", config.gap, className), children: [helperText && (jsxs("div", { className: cn("flex items-center", config.gap), children: [validationState === "positive" && (jsx("svg", { width: config.iconSize, height: config.iconSize, viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: "text-feedback-ink-positive-intense shrink-0", children: jsx("path", { d: "M3 7L6 10L11 4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })), validationState === "negative" && (jsxs("svg", { width: config.iconSize, height: config.iconSize, viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: "text-feedback-ink-negative-subtle shrink-0", children: [jsx("circle", { cx: "7", cy: "7", r: "6", stroke: "currentColor", strokeWidth: "1" }), jsx("path", { d: "M7 4V7.5M7 10V9.5", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round" })] })), jsx(Text, { as: "span", variant: "body", size: config.textSize, weight: "regular", color: getTextColor(), className: cn("italic font-size-100 leading-100", helperTextClassName), children: helperText })] })), trailingText && (jsx(Text, { as: "span", variant: "body", size: config.textSize, weight: "regular", color: isDisabled ? "disabled" : "muted", className: cn("font-size-100 leading-100 shrink-0", trailingTextClassName), children: trailingText }))] }));
528
+ });
529
+ FormFooter.displayName = "FormFooter";
530
+
531
+ /**
532
+ * ==============================================
533
+ * HOW TO ADD A NEW ICON:
534
+ * ==============================================
535
+ *
536
+ * 1. Add your SVG file to: src/assets/icons/{iconName}.svg
537
+ *
538
+ * 2. Copy the SVG content from the file
539
+ *
540
+ * 3. Add it to the iconRegistry below:
541
+ * iconName: `<svg>...</svg>`,
542
+ *
543
+ * 4. Use it anywhere in your app:
544
+ * <Icon name="iconName" size={24} />
545
+ *
546
+ * The Icon component will automatically:
547
+ * - Replace hardcoded colors with currentColor
548
+ * - Allow you to control color via className or style
549
+ * - Resize based on the size prop
550
+ * ==============================================
551
+ */
552
+ /**
553
+ * Icon registry - maps icon names to their SVG content
554
+ * Add your icons here by copying the SVG content from your icon files
555
+ */
556
+ const iconRegistry = {
557
+ // Tick/Check icon
558
+ tick: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
559
+ <path d="M10.364 15.1924L19.5564 6L20.9706 7.41421L10.364 18.0208L4 11.6569L5.41422 10.2427L10.364 15.1924Z" fill="#081416"/>
560
+ </svg>`,
561
+ // Alias: check points to the same icon as tick
562
+ check: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
563
+ <path d="M10.364 15.1924L19.5564 6L20.9706 7.41421L10.364 18.0208L4 11.6569L5.41422 10.2427L10.364 15.1924Z" fill="#081416"/>
564
+ </svg>`,
565
+ };
566
+ const Icon = ({ name, size = 24, className = "", style = {}, ...props }) => {
567
+ const svgContent = iconRegistry[name];
568
+ if (!svgContent) {
569
+ console.warn(`Icon "${String(name)}" not found in registry.\n` +
570
+ `Available icons: ${Object.keys(iconRegistry).join(", ")}`);
571
+ return null;
572
+ }
573
+ // Parse the SVG content
574
+ const parser = new DOMParser();
575
+ const svgDoc = parser.parseFromString(svgContent, "image/svg+xml");
576
+ const svgElement = svgDoc.querySelector("svg");
577
+ if (!svgElement) {
578
+ console.error(`Invalid SVG content for icon "${String(name)}"`);
579
+ return null;
580
+ }
581
+ // Extract viewBox attribute
582
+ const viewBox = svgElement.getAttribute("viewBox") || "0 0 24 24";
583
+ let innerHTML = svgElement.innerHTML;
584
+ // Replace hardcoded fill and stroke colors with currentColor
585
+ // This allows the icon color to be controlled via CSS color property
586
+ innerHTML = innerHTML
587
+ .replace(/fill="[^"]*"/g, 'fill="currentColor"')
588
+ .replace(/stroke="[^"]*"/g, 'stroke="currentColor"');
589
+ return (jsx("svg", { width: size, height: size, viewBox: viewBox, fill: "none", xmlns: "http://www.w3.org/2000/svg", className: className, style: style, ...props, dangerouslySetInnerHTML: { __html: innerHTML } }));
590
+ };
591
+ Icon.displayName = "Icon";
592
+ /**
593
+ * Get all available icon names from the registry
594
+ * @returns Array of registered icon names
595
+ *
596
+ * @example
597
+ * ```tsx
598
+ * const icons = getAvailableIcons();
599
+ * console.log(icons); // ['tick', 'check', ...]
600
+ * ```
601
+ */
602
+ function getAvailableIcons() {
603
+ return Object.keys(iconRegistry);
604
+ }
605
+ /**
606
+ * Check if an icon exists in the registry
607
+ * @param name - Icon name to check
608
+ * @returns true if the icon exists
609
+ *
610
+ * @example
611
+ * ```tsx
612
+ * if (hasIcon('tick')) {
613
+ * // Icon exists
614
+ * }
615
+ * ```
616
+ */
617
+ function hasIcon(name) {
618
+ return name in iconRegistry;
619
+ }
620
+
621
+ const checkboxVariants = cva("relative inline-flex items-center justify-center shrink-0 transition-all cursor-pointer", {
262
622
  variants: {
263
- variant: {
264
- display: "font-functional font-medium",
265
- heading: "font-display",
266
- body: "font-display",
267
- caption: "font-display font-semibold",
623
+ size: {
624
+ small: "w-[14px] h-[14px] rounded-small border-[1.5px]",
625
+ medium: "w-[16px] h-[16px] rounded-small border-[1.5px]",
626
+ large: "w-[20px] h-[20px] rounded-medium border-[2px]",
627
+ },
628
+ validationState: {
629
+ none: "",
630
+ error: "border-action-outline-negative-default hover:border-action-outline-negative-hover focus:ring-2 ring-action-outline-negative-faded-hover",
631
+ },
632
+ isChecked: {
633
+ true: "",
634
+ false: "",
635
+ },
636
+ isIndeterminate: {
637
+ true: "",
638
+ false: "",
639
+ },
640
+ isDisabled: {
641
+ true: "cursor-not-allowed opacity-60 border-action-outline-neutral-disabled bg-surface-fill-neutral-subtle",
642
+ false: "",
643
+ },
644
+ },
645
+ compoundVariants: [
646
+ // Unchecked state - none validation
647
+ {
648
+ isChecked: false,
649
+ validationState: "none",
650
+ isDisabled: false,
651
+ class: "border-action-outline-neutral-faded hover:bg-action-fill-neutral-faded hover:border-action-outline-neutral-faded",
652
+ },
653
+ // Checked state - none validation
654
+ {
655
+ isChecked: true,
656
+ validationState: "none",
657
+ isDisabled: false,
658
+ class: "bg-action-fill-primary-default hover:bg-action-fill-primary-hover hover:border-action-fill-primary-hover border-action-fill-primary-default",
659
+ },
660
+ // Checked or Indeterminate state - error validation
661
+ {
662
+ isChecked: true,
663
+ validationState: "error",
664
+ isDisabled: false,
665
+ class: "bg-action-fill-negative-default border-action-fill-negative-default hover:bg-action-fill-negative-hover hover:border-action-fill-negative-hover",
268
666
  },
667
+ // Indeterminate state - none validation
668
+ {
669
+ isIndeterminate: true,
670
+ validationState: "none",
671
+ isDisabled: false,
672
+ class: "bg-action-fill-primary-default border-action-fill-primary-default hover:bg-action-fill-primary-hover hover:border-action-fill-primary-hover",
673
+ },
674
+ // Indeterminate state - error validation (same as checked error)
675
+ {
676
+ isIndeterminate: true,
677
+ validationState: "error",
678
+ isDisabled: false,
679
+ class: "bg-action-fill-negative-default border-action-fill-negative-default hover:bg-action-fill-negative-hover hover:border-action-fill-negative-hover",
680
+ },
681
+ ],
682
+ defaultVariants: {
683
+ size: "medium",
684
+ validationState: "none",
685
+ isChecked: false,
686
+ isIndeterminate: false,
687
+ isDisabled: false,
688
+ },
689
+ });
690
+ const Checkbox = React.forwardRef(({ label, errorText, size = "medium", validationState = "none", isDisabled = false, isIndeterminate = false, showErrorText = true, containerClassName, labelClassName, className, checked, onChange, ...props }, ref) => {
691
+ const [internalChecked, setInternalChecked] = React.useState(false);
692
+ const [showRipple, setShowRipple] = React.useState(false);
693
+ const inputRef = React.useRef(null);
694
+ const rippleTimeoutRef = React.useRef(null);
695
+ // Use forwarded ref or internal ref
696
+ React.useImperativeHandle(ref, () => inputRef.current);
697
+ const isChecked = checked !== undefined ? checked : internalChecked;
698
+ // Set indeterminate property on the input element
699
+ React.useEffect(() => {
700
+ if (inputRef.current) {
701
+ inputRef.current.indeterminate = isIndeterminate;
702
+ }
703
+ }, [isIndeterminate]);
704
+ // Cleanup timeout on unmount
705
+ React.useEffect(() => {
706
+ return () => {
707
+ if (rippleTimeoutRef.current) {
708
+ clearTimeout(rippleTimeoutRef.current);
709
+ }
710
+ };
711
+ }, []);
712
+ const handleChange = (e) => {
713
+ if (onChange) {
714
+ onChange(e);
715
+ }
716
+ else {
717
+ setInternalChecked(e.target.checked);
718
+ }
719
+ };
720
+ const triggerRipple = () => {
721
+ if (!isDisabled && !showRipple) {
722
+ // Clear any existing timeout
723
+ if (rippleTimeoutRef.current) {
724
+ clearTimeout(rippleTimeoutRef.current);
725
+ }
726
+ setShowRipple(true);
727
+ rippleTimeoutRef.current = setTimeout(() => {
728
+ setShowRipple(false);
729
+ rippleTimeoutRef.current = null;
730
+ }, 400); // Match animation duration (0.4s)
731
+ }
732
+ };
733
+ const handleContainerClick = () => {
734
+ if (!isDisabled && inputRef.current) {
735
+ triggerRipple();
736
+ inputRef.current.click();
737
+ }
738
+ };
739
+ const handleKeyDown = (e) => {
740
+ if ((e.key === " " || e.key === "Enter") && !isDisabled) {
741
+ e.preventDefault();
742
+ triggerRipple();
743
+ inputRef.current?.click();
744
+ }
745
+ };
746
+ // Size-based configurations
747
+ const sizeConfig = {
748
+ small: {
749
+ gap: "gap-2",
750
+ labelSize: "text-body-small-regular",
751
+ iconSize: 10,
752
+ },
753
+ medium: {
754
+ gap: "gap-2.5",
755
+ labelSize: "text-body-medium-regular",
756
+ iconSize: 12,
757
+ },
758
+ large: {
759
+ gap: "gap-3",
760
+ labelSize: "text-body-large-regular",
761
+ iconSize: 14,
762
+ },
763
+ };
764
+ const config = sizeConfig[size];
765
+ // Determine if we should show the error text
766
+ const shouldShowError = errorText && showErrorText;
767
+ return (jsxs("div", { className: cn("inline-flex flex-col", containerClassName), children: [jsxs("div", { className: cn("inline-flex items-center", config.gap, isDisabled ? "cursor-not-allowed" : "cursor-pointer"), onClick: handleContainerClick, onKeyDown: handleKeyDown, role: "checkbox", "aria-checked": isIndeterminate ? "mixed" : isChecked, "aria-disabled": isDisabled, tabIndex: isDisabled ? -1 : 0, children: [jsx("input", { ref: inputRef, type: "checkbox", className: "sr-only", checked: isChecked, onChange: handleChange, disabled: isDisabled, ...props }), jsxs("div", { className: "relative inline-flex shrink-0", children: [showRipple && (jsx("div", { className: cn("absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full pointer-events-none w-full h-full", validationState === "error"
768
+ ? "bg-action-outline-negative-faded"
769
+ : "bg-action-outline-primary-faded"), style: {
770
+ animation: "var(--animate-checkbox-ripple)",
771
+ } })), jsxs("div", { className: cn(checkboxVariants({
772
+ size,
773
+ validationState,
774
+ isChecked: isChecked && !isIndeterminate,
775
+ isIndeterminate,
776
+ isDisabled,
777
+ }), className), children: [isChecked && !isIndeterminate && (jsx(Icon, { name: "tick", size: config.iconSize, className: "text-action-ink-on-primary-normal" })), isIndeterminate && (jsx("svg", { width: config.iconSize, height: config.iconSize, viewBox: "0 0 12 12", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: "text-action-ink-on-primary-normal", children: jsx("path", { d: "M3 6H9", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }))] })] }), label && (jsx("label", { className: cn(config.labelSize, "select-none inline-flex items-center", isDisabled
778
+ ? "text-surface-ink-neutral-disabled"
779
+ : "text-surface-ink-neutral-normal", labelClassName), children: label }))] }), shouldShowError && (jsx(FormFooter, { helperText: errorText, validationState: "negative", size: size, isDisabled: isDisabled }))] }));
780
+ });
781
+ Checkbox.displayName = "Checkbox";
782
+
783
+ const counterVariants = cva("inline-flex items-center justify-center transition-colors", {
784
+ variants: {
269
785
  size: {
270
- "2xlarge": "font-size-700 leading-700 tracking-[0%] mb-0",
271
- xlarge: "font-size-1100 leading-1100 tracking-[0%] mb-0",
272
- large: "font-size-1000 leading-1000 tracking-[0%] mb-0",
273
- medium: "font-size-900 leading-900 tracking-[0%] mb-0",
274
- small: "font-size-800 leading-800 tracking-[0%] mb-0",
275
- xsmall: "font-size-25 leading-25 tracking-[0%] mb-[var(--paragraph-spacing-100)]",
276
- },
277
- weight: {
278
- regular: "font-normal",
279
- medium: "font-medium",
280
- semibold: "font-semibold",
786
+ small: "text-body-xsmall-medium rounded-small px-1 py-0.5 min-w-[20px] h-[16px]",
787
+ medium: "text-body-small-medium rounded-medium px-2 py-1 min-w-[24px] h-[20px]",
788
+ large: "text-body-medium-medium rounded-medium px-2 py-1 min-w-[28px] h-[24px]",
281
789
  },
282
790
  color: {
283
- default: "text-surface-ink-neutral-normal",
284
- subtle: "text-surface-ink-neutral-subtle",
285
- muted: "text-surface-ink-neutral-muted",
286
- disabled: "text-surface-ink-neutral-disabled",
287
- primary: "text-surface-ink-primary-normal",
288
- onPrimary: "text-action-ink-on-primary-normal",
289
- },
290
- as: {
291
- h1: "",
292
- h2: "",
293
- h3: "",
294
- h4: "",
295
- h5: "",
296
- h6: "",
297
- p: "",
298
- span: "",
299
- div: "",
300
- label: "",
791
+ positive: "",
792
+ negative: "",
793
+ notice: "",
794
+ information: "",
795
+ neutral: "",
796
+ primary: "",
797
+ },
798
+ emphasis: {
799
+ subtle: "",
800
+ intense: "",
301
801
  },
302
802
  },
303
803
  compoundVariants: [
304
- // Display variants use Clash Grotesk with medium weight
804
+ // Positive - Subtle
305
805
  {
306
- variant: "display",
307
- weight: "regular",
308
- class: "font-medium",
806
+ color: "positive",
807
+ emphasis: "subtle",
808
+ class: "bg-feedback-fill-positive-subtle text-feedback-ink-positive-intense",
309
809
  },
810
+ // Positive - Intense
310
811
  {
311
- variant: "display",
312
- weight: "medium",
313
- class: "font-medium",
812
+ color: "positive",
813
+ emphasis: "intense",
814
+ class: "bg-feedback-fill-positive-intense text-action-ink-on-primary-normal",
314
815
  },
816
+ // Negative - Subtle
315
817
  {
316
- variant: "display",
317
- weight: "semibold",
318
- class: "font-medium",
818
+ color: "negative",
819
+ emphasis: "subtle",
820
+ class: "bg-feedback-fill-negative-subtle text-feedback-ink-negative-subtle",
319
821
  },
320
- // Caption is always semibold
822
+ // Negative - Intense
321
823
  {
322
- variant: "caption",
323
- weight: "regular",
324
- class: "font-semibold",
824
+ color: "negative",
825
+ emphasis: "intense",
826
+ class: "bg-feedback-fill-negative-intense text-action-ink-on-primary-normal",
325
827
  },
828
+ // Notice - Subtle
326
829
  {
327
- variant: "caption",
328
- weight: "medium",
329
- class: "font-semibold",
830
+ color: "notice",
831
+ emphasis: "subtle",
832
+ class: "bg-feedback-fill-notice-subtle text-feedback-ink-notice-subtle",
330
833
  },
834
+ // Notice - Intense
331
835
  {
332
- variant: "caption",
333
- weight: "semibold",
334
- class: "font-semibold",
836
+ color: "notice",
837
+ emphasis: "intense",
838
+ class: "bg-feedback-fill-notice-intense text-action-ink-on-primary-normal",
335
839
  },
336
- // Size-specific styling based on variant
337
- // Display sizes
840
+ // Information - Subtle
338
841
  {
339
- variant: "display",
340
- size: "xlarge",
341
- class: "font-size-1100 leading-1100 tracking-[0%] mb-0",
842
+ color: "information",
843
+ emphasis: "subtle",
844
+ class: "bg-feedback-fill-info-subtle text-feedback-ink-info-subtle",
342
845
  },
846
+ // Information - Intense
343
847
  {
344
- variant: "display",
345
- size: "large",
346
- class: "font-size-1000 leading-1000 tracking-[0%] mb-0",
848
+ color: "information",
849
+ emphasis: "intense",
850
+ class: "bg-feedback-fill-info-intense text-action-ink-on-primary-normal",
347
851
  },
852
+ // Neutral - Subtle
348
853
  {
349
- variant: "display",
350
- size: "medium",
351
- class: "font-size-900 leading-900 tracking-[0%] mb-0",
854
+ color: "neutral",
855
+ emphasis: "subtle",
856
+ class: "bg-surface-fill-neutral-subtle text-surface-ink-neutral-normal",
352
857
  },
858
+ // Neutral - Intense
353
859
  {
354
- variant: "display",
355
- size: "small",
356
- class: "font-size-800 leading-800 tracking-[0%] mb-0",
860
+ color: "neutral",
861
+ emphasis: "intense",
862
+ class: "bg-feedback-fill-neutral-intense text-action-ink-on-primary-normal",
863
+ },
864
+ // Primary - Subtle
865
+ {
866
+ color: "primary",
867
+ emphasis: "subtle",
868
+ class: "bg-surface-fill-primary-moderate text-surface-ink-primary-normal",
869
+ },
870
+ // Primary - Intense
871
+ {
872
+ color: "primary",
873
+ emphasis: "intense",
874
+ class: "bg-surface-fill-primary-intense text-action-ink-on-primary-normal",
875
+ },
876
+ ],
877
+ defaultVariants: {
878
+ size: "medium",
879
+ color: "neutral",
880
+ emphasis: "subtle",
881
+ },
882
+ });
883
+ const Counter = React.forwardRef(({ value, max, size = "medium", color = "neutral", emphasis = "subtle", className, ...props }, ref) => {
884
+ // Handle max value logic
885
+ const displayValue = React.useMemo(() => {
886
+ if (max !== undefined && typeof value === "number" && value > max) {
887
+ return `${max}+`;
888
+ }
889
+ return value;
890
+ }, [value, max]);
891
+ return (jsx("span", { ref: ref, className: cn(counterVariants({
892
+ size,
893
+ color,
894
+ emphasis,
895
+ }), className), ...props, children: displayValue }));
896
+ });
897
+ Counter.displayName = "Counter";
898
+
899
+ const dividerVariants = cva("", {
900
+ variants: {
901
+ orientation: {
902
+ horizontal: "w-full",
903
+ vertical: "h-full",
357
904
  },
358
- // Heading sizes
905
+ thickness: {
906
+ thinner: "",
907
+ thin: "",
908
+ thick: "",
909
+ thicker: "",
910
+ },
911
+ lineStyle: {
912
+ solid: "border-solid",
913
+ dashed: "border-dashed",
914
+ },
915
+ variant: {
916
+ normal: "",
917
+ subtle: "",
918
+ muted: "",
919
+ },
920
+ },
921
+ compoundVariants: [
922
+ // Horizontal orientation with thickness
359
923
  {
360
- variant: "heading",
361
- size: "2xlarge",
362
- class: "font-size-700 leading-700 tracking-[0%] mb-0",
924
+ orientation: "horizontal",
925
+ thickness: "thinner",
926
+ class: "border-t-[0.5px]",
363
927
  },
364
928
  {
365
- variant: "heading",
366
- size: "xlarge",
367
- class: "font-size-600 leading-600 tracking-[0%] mb-0",
929
+ orientation: "horizontal",
930
+ thickness: "thin",
931
+ class: "border-t-[1px]",
368
932
  },
369
933
  {
370
- variant: "heading",
371
- size: "large",
372
- class: "font-size-500 leading-500 tracking-[0%] mb-0",
934
+ orientation: "horizontal",
935
+ thickness: "thick",
936
+ class: "border-t-[2px]",
373
937
  },
374
938
  {
375
- variant: "heading",
376
- size: "medium",
377
- class: "font-size-400 leading-400 tracking-[0%] mb-0",
939
+ orientation: "horizontal",
940
+ thickness: "thicker",
941
+ class: "border-t-[3px]",
378
942
  },
943
+ // Vertical orientation with thickness
379
944
  {
380
- variant: "heading",
381
- size: "small",
382
- class: "font-size-300 leading-300 tracking-[0%] mb-0",
945
+ orientation: "vertical",
946
+ thickness: "thinner",
947
+ class: "border-l-[0.5px]",
383
948
  },
384
- // Body sizes
385
949
  {
386
- variant: "body",
387
- size: "large",
388
- class: "font-size-200 leading-200 tracking-[0%]",
950
+ orientation: "vertical",
951
+ thickness: "thin",
952
+ class: "border-l-[1px]",
389
953
  },
390
954
  {
391
- variant: "body",
392
- size: "medium",
393
- class: "font-size-100 leading-100 tracking-[0%]",
955
+ orientation: "vertical",
956
+ thickness: "thick",
957
+ class: "border-l-[2px]",
394
958
  },
395
959
  {
396
- variant: "body",
397
- size: "small",
398
- class: "font-size-75 leading-75 tracking-[0%]",
960
+ orientation: "vertical",
961
+ thickness: "thicker",
962
+ class: "border-l-[3px]",
399
963
  },
964
+ // Normal variant colors
400
965
  {
401
- variant: "body",
402
- size: "xsmall",
403
- class: "font-size-25 leading-25 tracking-[0%]",
966
+ variant: "normal",
967
+ class: "border-surface-outline-neutral-normal",
404
968
  },
405
- // Caption sizes
969
+ // Subtle variant colors
406
970
  {
407
- variant: "caption",
408
- size: "medium",
409
- class: "text-50 leading-50 tracking-[0%] mb-0",
971
+ variant: "subtle",
972
+ class: "border-surface-outline-neutral-subtle",
973
+ },
974
+ // Muted variant colors
975
+ {
976
+ variant: "muted",
977
+ class: "border-surface-outline-neutral-muted",
410
978
  },
411
979
  ],
412
980
  defaultVariants: {
413
- variant: "body",
414
- size: "medium",
415
- weight: "regular",
416
- color: "default",
417
- as: "p",
981
+ orientation: "horizontal",
982
+ thickness: "thin",
983
+ lineStyle: "solid",
984
+ variant: "normal",
418
985
  },
419
986
  });
420
- const Text = React.forwardRef(({ className, variant, size, weight, color, as, children, ...props }, ref) => {
421
- const Component = as || "p";
422
- return React.createElement(Component, {
423
- className: cn(textVariants({ variant, size, weight, color, as, className })),
424
- ref,
425
- ...props,
426
- }, children);
987
+ const Divider = React.forwardRef(({ className, orientation = "horizontal", thickness = "thin", lineStyle = "solid", variant = "normal", ...props }, ref) => {
988
+ return (jsx("div", { ref: ref, role: "separator", "aria-orientation": orientation, className: cn(dividerVariants({ orientation, thickness, lineStyle, variant }), className), ...props }));
427
989
  });
428
- Text.displayName = "Text";
990
+ Divider.displayName = "Divider";
429
991
 
430
992
  const tooltipVariants = cva("fixed z-50 bg-popup-fill-intense text-action-ink-on-primary-normal rounded-medium border border-popup-outline-subtle flex flex-col p-4 rounded-xlarge min-w-[200px] max-w-[300px] transition-opacity duration-200", {
431
993
  variants: {
@@ -657,6 +1219,504 @@ const Tooltip = React.forwardRef(({ children, heading, description, placement =
657
1219
  });
658
1220
  Tooltip.displayName = "Tooltip";
659
1221
 
1222
+ const FormHeader = React.forwardRef(({ label, size = "medium", isOptional = false, isRequired = false, infoHeading, infoDescription, linkText, linkHref, onLinkClick, htmlFor, className, labelClassName, linkClassName, }, ref) => {
1223
+ // Size-based configurations
1224
+ const sizeConfig = {
1225
+ small: {
1226
+ textSize: "xsmall",
1227
+ iconSize: 12,
1228
+ gap: "gap-1.5",
1229
+ },
1230
+ medium: {
1231
+ textSize: "small",
1232
+ iconSize: 14,
1233
+ gap: "gap-2",
1234
+ },
1235
+ large: {
1236
+ textSize: "medium",
1237
+ iconSize: 16,
1238
+ gap: "gap-2.5",
1239
+ },
1240
+ };
1241
+ const config = sizeConfig[size];
1242
+ return (jsxs("div", { ref: ref, className: cn("flex items-center justify-between px-1", config.gap, className), children: [jsxs("div", { className: cn("flex items-center", config.gap), children: [jsxs("label", { htmlFor: htmlFor, className: cn("flex items-center", labelClassName), children: [jsx(Text, { as: "span", variant: "body", size: config.textSize, weight: "semibold", color: "subtle", children: label }), isRequired && (jsx(Text, { as: "span", variant: "body", size: config.textSize, weight: "semibold", className: "text-feedback-ink-negative-subtle ml-0.5", children: "*" })), isOptional && (jsx(Text, { as: "span", variant: "body", size: config.textSize, weight: "regular", className: "text-surface-ink-neutral-muted ml-1", children: "(optional)" }))] }), infoDescription && (jsx(Tooltip, { description: infoDescription, heading: infoHeading, children: jsxs("svg", { width: config.iconSize, height: config.iconSize, viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: "text-surface-ink-neutral-muted cursor-help", children: [jsx("circle", { cx: "7", cy: "7", r: "6", stroke: "currentColor", strokeWidth: "1" }), jsx("path", { d: "M7 6V10M7 4.5V4", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round" })] }) }))] }), linkText && (jsx("a", { href: linkHref, onClick: onLinkClick, className: cn("text-surface-ink-primary-normal hover:text-surface-ink-primary-hover transition-colors cursor-pointer font-display font-semibold leading-tight shrink-0", size === "small" && "text-xs", size === "medium" && "text-xs", size === "large" && "text-sm", linkClassName), children: linkText }))] }));
1243
+ });
1244
+ FormHeader.displayName = "FormHeader";
1245
+
1246
+ const listItemVariants = cva("flex items-start gap-3 p-3 rounded-medium transition-colors cursor-pointer", {
1247
+ variants: {
1248
+ variant: {
1249
+ default: `hover:bg-action-fill-neutral-faded
1250
+ focus:bg-action-fill-neutral-faded
1251
+ focus:ring-2
1252
+ ring-action-outline-primary-faded-hover
1253
+ border border-transparent
1254
+ `,
1255
+ bordered: "border border-action-outline-primary-faded hover:bg-surface-fill-primary-subtle",
1256
+ primary: `hover:bg-action-fill-neutral-faded
1257
+ focus:bg-action-fill-neutral-faded
1258
+ focus:ring-2
1259
+ ring-action-outline-primary-faded-hover
1260
+ border border-transparent
1261
+ `,
1262
+ negative: `hover:bg-action-fill-negative-faded
1263
+ focus:bg-action-fill-negative-faded
1264
+ focus:ring-2 ring-action-outline-negative-faded-hover
1265
+ border border-transparent
1266
+ `,
1267
+ },
1268
+ isDisabled: {
1269
+ true: "cursor-not-allowed opacity-60",
1270
+ false: "",
1271
+ },
1272
+ isSelected: {
1273
+ true: "bg-action-fill-primary-faded border-action-outline-primary-faded",
1274
+ false: "",
1275
+ },
1276
+ },
1277
+ defaultVariants: {
1278
+ variant: "default",
1279
+ isDisabled: false,
1280
+ isSelected: false,
1281
+ },
1282
+ });
1283
+ const ChevronRightIcon = ({ className }) => (jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: className, children: jsx("path", { d: "M7.5 15L12.5 10L7.5 5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
1284
+ const ListItem = React.forwardRef(({ className, type = "single", leadingIcon, title, description, trailingIcon, showChevron = true, variant = "default", isDisabled = false, isSelected = false, onSelectionChange, checkboxSize = "small", containerClassName, contentClassName, onClick, ...props }, ref) => {
1285
+ const [internalSelected, setInternalSelected] = React.useState(isSelected);
1286
+ // Sync internal state with prop
1287
+ React.useEffect(() => {
1288
+ setInternalSelected(isSelected);
1289
+ }, [isSelected]);
1290
+ const handleClick = (e) => {
1291
+ if (isDisabled)
1292
+ return;
1293
+ if (type === "multiple") {
1294
+ const newSelected = !internalSelected;
1295
+ setInternalSelected(newSelected);
1296
+ onSelectionChange?.(newSelected);
1297
+ }
1298
+ onClick?.(e);
1299
+ };
1300
+ const handleCheckboxChange = (e) => {
1301
+ e.stopPropagation();
1302
+ if (isDisabled)
1303
+ return;
1304
+ const newSelected = e.target.checked;
1305
+ setInternalSelected(newSelected);
1306
+ onSelectionChange?.(newSelected);
1307
+ };
1308
+ return (jsxs("div", { ref: ref, className: cn(listItemVariants({
1309
+ variant,
1310
+ isDisabled,
1311
+ isSelected: type === "multiple" ? internalSelected : false,
1312
+ }), containerClassName), onClick: handleClick, role: type === "multiple" ? "checkbox" : "button", "aria-checked": type === "multiple" ? internalSelected : undefined, "aria-disabled": isDisabled, tabIndex: isDisabled ? -1 : 0, ...props, children: [type === "multiple" && (jsx(Checkbox, { checked: internalSelected, onChange: handleCheckboxChange, isDisabled: isDisabled, size: checkboxSize, className: "shrink-0 mt-0.5" })), leadingIcon && (jsx("div", { className: cn(`shrink-0 flex items-center justify-center mt-0.5`, variant === "primary"
1313
+ ? "text-action-ink-primary-normal"
1314
+ : variant === "negative"
1315
+ ? "text-action-ink-negative-normal"
1316
+ : "text-action-ink-neutral-subtle", isDisabled && "text-surface-ink-neutral-disabled"), children: leadingIcon })), jsxs("div", { className: cn("flex-1 min-w-0 flex flex-col justify-center", contentClassName), children: [jsx("div", { className: cn("text-body-medium-regular truncate", variant === "primary"
1317
+ ? "text-action-ink-primary-normal"
1318
+ : variant === "negative"
1319
+ ? "text-action-ink-negative-normal"
1320
+ : "text-action-ink-neutral-normal", isDisabled && "text-surface-ink-neutral-disabled"), children: title }), description && (jsx("div", { className: cn("text-body-small-regular text-surface-ink-neutral-muted mt-0.5 line-clamp-2", isDisabled && "text-surface-ink-neutral-disabled"), children: description }))] }), (trailingIcon || showChevron) && (jsx("div", { className: "shrink-0 self-center text-action-ink-neutral-subtle", children: trailingIcon || jsx(ChevronRightIcon, {}) }))] }));
1321
+ });
1322
+ ListItem.displayName = "ListItem";
1323
+
1324
+ const radioVariants = cva("relative inline-flex items-center justify-center shrink-0 border transition-all cursor-pointer rounded-full", {
1325
+ variants: {
1326
+ size: {
1327
+ small: "w-[14px] h-[14px] border-[1.5px]",
1328
+ medium: "w-[16px] h-[16px] border-[1.5px]",
1329
+ large: "w-[20px] h-[20px] border-[2px]",
1330
+ },
1331
+ validationState: {
1332
+ none: "",
1333
+ error: "border-action-outline-negative-default hover:border-action-outline-negative-hover",
1334
+ },
1335
+ isChecked: {
1336
+ true: "",
1337
+ false: "",
1338
+ },
1339
+ isDisabled: {
1340
+ true: "cursor-not-allowed opacity-60 border-action-outline-neutral-disabled bg-surface-fill-neutral-subtle",
1341
+ false: "",
1342
+ },
1343
+ isFocused: {
1344
+ true: "",
1345
+ false: "",
1346
+ },
1347
+ },
1348
+ compoundVariants: [
1349
+ // Unchecked state - none validation
1350
+ {
1351
+ isChecked: false,
1352
+ validationState: "none",
1353
+ isDisabled: false,
1354
+ class: `border-action-outline-neutral-faded
1355
+ hover:bg-action-fill-neutral-faded
1356
+ hover:border-action-outline-neutral-faded
1357
+ `,
1358
+ },
1359
+ // Checked state - none validation
1360
+ {
1361
+ isChecked: true,
1362
+ validationState: "none",
1363
+ isDisabled: false,
1364
+ class: "bg-action-fill-primary-default hover:bg-action-fill-primary-hover border-action-fill-primary-default hover:border-action-fill-primary-hover",
1365
+ },
1366
+ // Checked state - error validation
1367
+ {
1368
+ isChecked: true,
1369
+ validationState: "error",
1370
+ isDisabled: false,
1371
+ class: "bg-action-fill-negative-default hover:bg-action-fill-negative-hover border-action-fill-negative-default hover:border-action-fill-negative-hover",
1372
+ },
1373
+ // Focused state - none validation
1374
+ {
1375
+ isFocused: true,
1376
+ validationState: "none",
1377
+ isDisabled: false,
1378
+ class: "ring-2 ring-action-outline-primary-faded",
1379
+ },
1380
+ // Focused state - error validation
1381
+ {
1382
+ isFocused: true,
1383
+ validationState: "error",
1384
+ isDisabled: false,
1385
+ class: "ring-2 ring-action-outline-negative-faded",
1386
+ },
1387
+ ],
1388
+ defaultVariants: {
1389
+ size: "medium",
1390
+ validationState: "none",
1391
+ isChecked: false,
1392
+ isDisabled: false,
1393
+ isFocused: false,
1394
+ },
1395
+ });
1396
+ const Radio = React.forwardRef(({ label, errorText, size = "medium", validationState = "none", isDisabled = false, showErrorText = true, containerClassName, labelClassName, className, checked, onChange, ...props }, ref) => {
1397
+ const [internalChecked, setInternalChecked] = React.useState(false);
1398
+ const [showRipple, setShowRipple] = React.useState(false);
1399
+ const [isFocused, setIsFocused] = React.useState(false);
1400
+ const inputRef = React.useRef(null);
1401
+ // Use forwarded ref or internal ref
1402
+ React.useImperativeHandle(ref, () => inputRef.current);
1403
+ const isChecked = checked !== undefined ? checked : internalChecked;
1404
+ const handleChange = (e) => {
1405
+ if (onChange) {
1406
+ onChange(e);
1407
+ }
1408
+ else {
1409
+ setInternalChecked(e.target.checked);
1410
+ }
1411
+ };
1412
+ const triggerRipple = () => {
1413
+ if (!isDisabled) {
1414
+ setShowRipple(true);
1415
+ setTimeout(() => {
1416
+ setShowRipple(false);
1417
+ }, 360); // Match animation duration (0.36s)
1418
+ }
1419
+ };
1420
+ const handleContainerClick = () => {
1421
+ if (!isDisabled && inputRef.current) {
1422
+ // Only show ripple when checking (not unchecking)
1423
+ const willBeChecked = !isChecked;
1424
+ if (willBeChecked) {
1425
+ triggerRipple();
1426
+ }
1427
+ inputRef.current.click();
1428
+ }
1429
+ };
1430
+ const handleKeyDown = (e) => {
1431
+ if ((e.key === " " || e.key === "Enter") && !isDisabled) {
1432
+ e.preventDefault();
1433
+ // Only show ripple when checking (not unchecking)
1434
+ const willBeChecked = !isChecked;
1435
+ if (willBeChecked) {
1436
+ triggerRipple();
1437
+ }
1438
+ inputRef.current?.click();
1439
+ }
1440
+ };
1441
+ const handleFocus = () => {
1442
+ if (!isDisabled) {
1443
+ setIsFocused(true);
1444
+ }
1445
+ };
1446
+ const handleBlur = () => {
1447
+ setIsFocused(false);
1448
+ };
1449
+ // Size-based configurations
1450
+ const sizeConfig = {
1451
+ small: {
1452
+ gap: "gap-2",
1453
+ labelSize: "text-body-small-regular",
1454
+ innerCircleSize: 6,
1455
+ },
1456
+ medium: {
1457
+ gap: "gap-2.5",
1458
+ labelSize: "text-body-medium-regular",
1459
+ innerCircleSize: 7,
1460
+ },
1461
+ large: {
1462
+ gap: "gap-3",
1463
+ labelSize: "text-body-large-regular",
1464
+ innerCircleSize: 8,
1465
+ },
1466
+ };
1467
+ const config = sizeConfig[size];
1468
+ // Determine if we should show the error text
1469
+ const shouldShowError = errorText && showErrorText;
1470
+ return (jsxs("div", { className: cn("inline-flex flex-col", containerClassName), children: [jsxs("div", { className: cn("inline-flex items-center", config.gap, isDisabled ? "cursor-not-allowed" : "cursor-pointer"), onClick: handleContainerClick, onKeyDown: handleKeyDown, onFocus: handleFocus, onBlur: handleBlur, role: "radio", "aria-checked": isChecked, "aria-disabled": isDisabled, tabIndex: isDisabled ? -1 : 0, children: [jsx("input", { ref: inputRef, type: "radio", className: "sr-only", checked: isChecked, onChange: handleChange, disabled: isDisabled, ...props }), jsxs("div", { className: "relative inline-flex shrink-0", children: [showRipple && (jsx("div", { className: cn("absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full pointer-events-none w-full h-full", validationState === "error"
1471
+ ? "bg-action-outline-negative-faded"
1472
+ : "bg-action-outline-primary-faded"), style: {
1473
+ animation: "var(--animate-checkbox-ripple)",
1474
+ } })), jsx("div", { className: cn(radioVariants({
1475
+ size,
1476
+ validationState,
1477
+ isChecked,
1478
+ isDisabled,
1479
+ isFocused,
1480
+ }), className), children: isChecked && (jsx("div", { className: "rounded-full bg-white transition-all", style: {
1481
+ width: `${config.innerCircleSize}px`,
1482
+ height: `${config.innerCircleSize}px`,
1483
+ } })) })] }), label && (jsx("label", { className: cn(config.labelSize, "select-none inline-flex items-center", isDisabled
1484
+ ? "text-surface-ink-neutral-disabled"
1485
+ : "text-surface-ink-neutral-normal", labelClassName), children: label }))] }), shouldShowError && (jsx(FormFooter, { helperText: errorText, validationState: "negative", size: size, isDisabled: isDisabled }))] }));
1486
+ });
1487
+ Radio.displayName = "Radio";
1488
+
1489
+ const switchVariants = cva("relative inline-flex items-center shrink-0 cursor-pointer rounded-full transition-all duration-200", {
1490
+ variants: {
1491
+ size: {
1492
+ small: "w-[20px] h-[12px]",
1493
+ medium: "w-[28px] h-[16px]",
1494
+ large: "w-[36px] h-[20px]",
1495
+ },
1496
+ isChecked: {
1497
+ true: "bg-action-fill-primary-default ",
1498
+ false: "bg-surface-fill-neutral-subtle",
1499
+ },
1500
+ isDisabled: {
1501
+ true: "cursor-not-allowed opacity-60",
1502
+ false: "",
1503
+ },
1504
+ },
1505
+ compoundVariants: [
1506
+ {
1507
+ isChecked: true,
1508
+ isDisabled: false,
1509
+ class: "hover:bg-action-fill-primary-hover ",
1510
+ },
1511
+ {
1512
+ isChecked: false,
1513
+ isDisabled: false,
1514
+ class: "hover:bg-action-fill-neutral-faded",
1515
+ },
1516
+ ],
1517
+ defaultVariants: {
1518
+ size: "medium",
1519
+ isChecked: false,
1520
+ isDisabled: false,
1521
+ },
1522
+ });
1523
+ const switchThumbVariants = cva("inline-block rounded-full bg-neutral-00 shadow-sm transition-transform duration-200", {
1524
+ variants: {
1525
+ size: {
1526
+ small: "h-[8px] w-[8px]",
1527
+ medium: "h-[12px] w-[12px]",
1528
+ large: "h-[16px] w-[16px]",
1529
+ },
1530
+ isChecked: {
1531
+ true: "",
1532
+ false: "",
1533
+ },
1534
+ },
1535
+ compoundVariants: [
1536
+ // Small size translations
1537
+ {
1538
+ size: "small",
1539
+ isChecked: false,
1540
+ class: "translate-x-[2px]",
1541
+ },
1542
+ {
1543
+ size: "small",
1544
+ isChecked: true,
1545
+ class: "translate-x-[10px]",
1546
+ },
1547
+ // Medium size translations
1548
+ {
1549
+ size: "medium",
1550
+ isChecked: false,
1551
+ class: "translate-x-[2px]",
1552
+ },
1553
+ {
1554
+ size: "medium",
1555
+ isChecked: true,
1556
+ class: "translate-x-[14px]",
1557
+ },
1558
+ // Large size translations
1559
+ {
1560
+ size: "large",
1561
+ isChecked: false,
1562
+ class: "translate-x-[2px]",
1563
+ },
1564
+ {
1565
+ size: "large",
1566
+ isChecked: true,
1567
+ class: "translate-x-[18px]",
1568
+ },
1569
+ ],
1570
+ defaultVariants: {
1571
+ size: "medium",
1572
+ isChecked: false,
1573
+ },
1574
+ });
1575
+ const Switch = React.forwardRef(({ label, size = "medium", isDisabled = false, containerClassName, labelClassName, trackClassName, thumbClassName, className, checked, onChange, ...props }, ref) => {
1576
+ const [internalChecked, setInternalChecked] = React.useState(false);
1577
+ const inputRef = React.useRef(null);
1578
+ // Use forwarded ref or internal ref
1579
+ React.useImperativeHandle(ref, () => inputRef.current);
1580
+ const isChecked = checked !== undefined ? checked : internalChecked;
1581
+ const handleChange = (e) => {
1582
+ if (onChange) {
1583
+ onChange(e);
1584
+ }
1585
+ else {
1586
+ setInternalChecked(e.target.checked);
1587
+ }
1588
+ };
1589
+ const handleContainerClick = () => {
1590
+ if (!isDisabled && inputRef.current) {
1591
+ inputRef.current.click();
1592
+ }
1593
+ };
1594
+ const handleKeyDown = (e) => {
1595
+ if ((e.key === " " || e.key === "Enter") && !isDisabled) {
1596
+ e.preventDefault();
1597
+ inputRef.current?.click();
1598
+ }
1599
+ };
1600
+ // Size-based configurations
1601
+ const sizeConfig = {
1602
+ small: {
1603
+ gap: "gap-2",
1604
+ labelSize: "text-body-small-regular",
1605
+ },
1606
+ medium: {
1607
+ gap: "gap-2.5",
1608
+ labelSize: "text-body-medium-regular",
1609
+ },
1610
+ large: {
1611
+ gap: "gap-3",
1612
+ labelSize: "text-body-large-regular",
1613
+ },
1614
+ };
1615
+ const config = sizeConfig[size];
1616
+ return (jsx("div", { className: cn("inline-flex flex-col", containerClassName), children: jsxs("div", { className: cn("inline-flex items-center", config.gap, isDisabled ? "cursor-not-allowed" : "cursor-pointer"), onClick: handleContainerClick, onKeyDown: handleKeyDown, role: "switch", "aria-checked": isChecked, "aria-disabled": isDisabled, tabIndex: isDisabled ? -1 : 0, children: [jsx("input", { ref: inputRef, type: "checkbox", role: "switch", className: "sr-only", checked: isChecked, onChange: handleChange, disabled: isDisabled, ...props }), jsx("div", { className: "relative inline-flex shrink-0", children: jsx("div", { className: cn(switchVariants({
1617
+ size,
1618
+ isChecked,
1619
+ isDisabled,
1620
+ }), trackClassName, className, "focus-visible:ring-2 focus-visible:ring-action-outline-primary-faded focus-visible:ring-offset-2"), children: jsx("span", { className: cn(switchThumbVariants({
1621
+ size,
1622
+ isChecked,
1623
+ }), thumbClassName) }) }) }), label && (jsx("label", { className: cn(config.labelSize, "select-none inline-flex items-center", isDisabled
1624
+ ? "text-surface-ink-neutral-disabled"
1625
+ : "text-surface-ink-neutral-normal", labelClassName), children: label }))] }) }));
1626
+ });
1627
+ Switch.displayName = "Switch";
1628
+
1629
+ const textAreaVariants = cva("relative flex flex-col border rounded-medium transition-all font-display font-size-100 leading-100", {
1630
+ variants: {
1631
+ size: {
1632
+ small: "p-3 min-h-[56px] text-xs gap-2",
1633
+ medium: "p-4 min-h-[56px] text-sm gap-2",
1634
+ large: "p-5 min-h-[64px]text-base gap-3",
1635
+ },
1636
+ validationState: {
1637
+ none: `
1638
+ border-action-outline-neutral-faded
1639
+ hover:border-action-outline-primary-hover
1640
+ focus-within:border-action-outline-primary-hover
1641
+ focus-within:ring-2
1642
+ ring-action-outline-primary-faded-hover`,
1643
+ positive: `
1644
+ border-action-outline-positive-default
1645
+ focus-within:border-action-outline-positive-hover
1646
+ focus-within:ring-2
1647
+ ring-action-outline-positive-faded-hover`,
1648
+ negative: `border-action-outline-negative-default
1649
+ focus-within:border-action-outline-negative-hover
1650
+ focus-within:ring-2
1651
+ ring-action-outline-negative-faded-hover`,
1652
+ },
1653
+ isDisabled: {
1654
+ true: `
1655
+ border-[var(--border-width-thinner)]
1656
+ hover:border-action-outline-neutral-disabled
1657
+ border-action-outline-neutral-disabled
1658
+ bg-surface-fill-neutral-intense cursor-not-allowed opacity-60`,
1659
+ false: "bg-surface-fill-neutral-intense",
1660
+ },
1661
+ },
1662
+ defaultVariants: {
1663
+ size: "medium",
1664
+ validationState: "none",
1665
+ isDisabled: false,
1666
+ },
1667
+ });
1668
+ const TextArea = React.forwardRef(({ label, helperText, errorText, successText, size = "medium", validationState = "none", isDisabled = false, isRequired = false, isOptional = false, maxChar, showCharCount = true, infoDescription, infoHeading, linkText, linkHref, onLinkClick, containerClassName, labelClassName, textAreaClassName, className, value, onChange, rows = 4, ...props }, ref) => {
1669
+ const [internalValue, setInternalValue] = React.useState("");
1670
+ const textAreaValue = value !== undefined ? value : internalValue;
1671
+ const currentLength = String(textAreaValue).length;
1672
+ const handleChange = (e) => {
1673
+ const newValue = e.target.value;
1674
+ // Prevent exceeding maxChar if specified
1675
+ if (maxChar && newValue.length > maxChar) {
1676
+ return;
1677
+ }
1678
+ if (onChange) {
1679
+ onChange(e);
1680
+ }
1681
+ else {
1682
+ setInternalValue(newValue);
1683
+ }
1684
+ };
1685
+ // Determine which helper text to show
1686
+ const displayHelperText = errorText || successText || helperText;
1687
+ const currentValidationState = errorText
1688
+ ? "negative"
1689
+ : successText
1690
+ ? "positive"
1691
+ : validationState;
1692
+ // Check if we're approaching or at the limit
1693
+ const isNearLimit = maxChar && currentLength >= maxChar * 0.9;
1694
+ const isAtLimit = maxChar && currentLength >= maxChar;
1695
+ const sizeConfig = {
1696
+ small: {
1697
+ gap: "gap-2",
1698
+ },
1699
+ medium: {
1700
+ gap: "gap-2",
1701
+ },
1702
+ large: {
1703
+ gap: "gap-3",
1704
+ },
1705
+ };
1706
+ return (jsxs("div", { className: cn("w-full flex flex-col", sizeConfig[size].gap, containerClassName), children: [label && (jsx(FormHeader, { label: label, size: size, isRequired: isRequired, isOptional: isOptional, infoHeading: infoHeading, infoDescription: infoDescription, linkText: linkText, linkHref: linkHref, onLinkClick: onLinkClick, htmlFor: props.id, className: "mb-2", labelClassName: labelClassName })), jsx("div", { className: cn(textAreaVariants({
1707
+ size,
1708
+ validationState: currentValidationState,
1709
+ isDisabled,
1710
+ }), className), children: jsx("textarea", { ref: ref, value: textAreaValue, onChange: handleChange, disabled: isDisabled, required: isRequired, rows: rows, className: cn("flex-1 bg-transparent border-none outline-none text-surface-ink-neutral-normal placeholder:text-surface-ink-neutral-muted disabled:cursor-not-allowed disabled:text-surface-ink-neutral-disabled font-display resize-none", size === "small" && "text-xs", size === "medium" && "text-sm", size === "large" && "text-base", textAreaClassName), ...props }) }), jsx(FormFooter, { helperText: displayHelperText, trailingText: maxChar && showCharCount ? `${currentLength}/${maxChar}` : undefined, validationState: currentValidationState === "none"
1711
+ ? "default"
1712
+ : currentValidationState, size: size, isDisabled: isDisabled, className: "mt-1", trailingTextClassName: cn("transition-colors", isAtLimit
1713
+ ? "text-feedback-ink-negative-subtle"
1714
+ : isNearLimit
1715
+ ? "text-feedback-ink-warning-subtle"
1716
+ : "") })] }));
1717
+ });
1718
+ TextArea.displayName = "TextArea";
1719
+
660
1720
  const textFieldVariants = cva("relative flex items-center gap-2 border rounded-medium transition-all font-display font-size-100 leading-100", {
661
1721
  variants: {
662
1722
  size: {
@@ -666,19 +1726,17 @@ const textFieldVariants = cva("relative flex items-center gap-2 border rounded-m
666
1726
  },
667
1727
  validationState: {
668
1728
  none: `
669
- border-action-outline-neutral-default
1729
+ border-action-outline-neutral-faded
670
1730
  hover:border-action-outline-primary-hover
671
1731
  focus-within:border-action-outline-primary-hover
672
1732
  focus-within:ring-2
673
1733
  ring-action-outline-primary-faded-hover`,
674
1734
  positive: `
675
1735
  border-action-outline-positive-default
676
- hover:border-action-outline-positive-hover
677
1736
  focus-within:border-action-outline-positive-hover
678
1737
  focus-within:ring-2
679
1738
  ring-action-outline-positive-faded-hover`,
680
1739
  negative: `border-action-outline-negative-default
681
- hover:border-action-outline-negative-hover
682
1740
  focus-within:border-action-outline-negative-hover
683
1741
  focus-within:ring-2
684
1742
  ring-action-outline-negative-faded-hover`,
@@ -698,7 +1756,7 @@ const textFieldVariants = cva("relative flex items-center gap-2 border rounded-m
698
1756
  isDisabled: false,
699
1757
  },
700
1758
  });
701
- const TextField = React.forwardRef(({ label, helperText, errorText, successText, size = "medium", validationState = "none", isDisabled = false, isRequired = false, prefix, suffix, showClearButton = false, infoDescription, infoHeading, onClear, containerClassName, labelClassName, inputClassName, className, value, onChange, ...props }, ref) => {
1759
+ const TextField = React.forwardRef(({ label, helperText, errorText, successText, size = "medium", validationState = "none", isDisabled = false, isRequired = false, isOptional = false, prefix, suffix, showClearButton = false, infoDescription, infoHeading, linkText, linkHref, onLinkClick, onClear, containerClassName, labelClassName, inputClassName, className, value, onChange, ...props }, ref) => {
702
1760
  const [internalValue, setInternalValue] = React.useState("");
703
1761
  const inputValue = value !== undefined ? value : internalValue;
704
1762
  const hasValue = inputValue && String(inputValue).length > 0;
@@ -730,7 +1788,18 @@ const TextField = React.forwardRef(({ label, helperText, errorText, successText,
730
1788
  : successText
731
1789
  ? "positive"
732
1790
  : validationState;
733
- return (jsxs("div", { className: cn("w-full flex flex-col gap-3", containerClassName), children: [label && (jsxs("div", { className: "flex items-center gap-2 mb-2", children: [jsxs("label", { htmlFor: props.id, className: cn("flex items-center", labelClassName), children: [jsx(Text, { as: "span", variant: "body", size: "small", weight: "semibold", color: isDisabled ? "disabled" : "default", children: label }), isRequired && (jsx(Text, { as: "span", variant: "body", size: "small", weight: "semibold", className: "text-feedback-ink-negative-subtle ml-0.5", children: "*" }))] }), infoDescription && (jsx(Tooltip, { description: infoDescription, heading: infoHeading, children: jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: "text-surface-ink-neutral-muted", children: [jsx("circle", { cx: "7", cy: "7", r: "6", stroke: "currentColor", strokeWidth: "1" }), jsx("path", { d: "M7 6V10M7 4.5V4", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round" })] }) }))] })), jsxs("div", { className: cn(textFieldVariants({
1791
+ const sizeConfig = {
1792
+ small: {
1793
+ gap: "gap-2",
1794
+ },
1795
+ medium: {
1796
+ gap: "gap-2",
1797
+ },
1798
+ large: {
1799
+ gap: "gap-3",
1800
+ },
1801
+ };
1802
+ return (jsxs("div", { className: cn("w-full flex flex-col", sizeConfig[size].gap, containerClassName), children: [label && (jsx(FormHeader, { label: label, size: size, isRequired: isRequired, isOptional: isOptional, infoHeading: infoHeading, infoDescription: infoDescription, linkText: linkText, linkHref: linkHref, onLinkClick: onLinkClick, htmlFor: props.id, className: "mb-2", labelClassName: labelClassName })), jsxs("div", { className: cn(textFieldVariants({
734
1803
  size,
735
1804
  validationState: currentValidationState,
736
1805
  isDisabled,
@@ -746,17 +1815,11 @@ const TextField = React.forwardRef(({ label, helperText, errorText, successText,
746
1815
  ? "text-feedback-ink-positive-intense"
747
1816
  : currentValidationState === "negative"
748
1817
  ? "text-feedback-ink-negative-subtle"
749
- : "text-surface-ink-neutral-muted"), children: suffix }))] }), displayHelperText && (jsxs("div", { className: "flex items-center gap-1 mt-1", children: [currentValidationState === "positive" && (jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: "text-feedback-ink-positive-intense shrink-0", children: jsx("path", { d: "M3 7L6 10L11 4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })), currentValidationState === "negative" && (jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: "text-feedback-ink-negative-subtle shrink-0", children: [jsx("circle", { cx: "7", cy: "7", r: "6", stroke: "currentColor", strokeWidth: "1" }), jsx("path", { d: "M7 4V7.5M7 10V9.5", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round" })] })), jsx(Text, { as: "span", variant: "body", size: "small", color: currentValidationState === "positive"
750
- ? "default"
751
- : currentValidationState === "negative"
752
- ? "default"
753
- : isDisabled
754
- ? "disabled"
755
- : "subtle", className: cn("italic font-size-100 leading-100", currentValidationState === "positive" &&
756
- "text-feedback-ink-positive-intense", currentValidationState === "negative" &&
757
- "text-feedback-ink-negative-subtle"), children: displayHelperText })] }))] }));
1818
+ : "text-surface-ink-neutral-muted"), children: suffix }))] }), jsx(FormFooter, { helperText: displayHelperText, validationState: currentValidationState === "none"
1819
+ ? "default"
1820
+ : currentValidationState, size: size, isDisabled: isDisabled, className: "mt-1" })] }));
758
1821
  });
759
1822
  TextField.displayName = "TextField";
760
1823
 
761
- export { Button, Text, TextField, Tooltip, buttonVariants, cn, textFieldVariants, textVariants, tooltipVariants };
1824
+ export { Badge, Button, Checkbox, Counter, Divider, FormFooter, FormHeader, Icon, ListItem, Radio, Switch, Text, TextArea, TextField, Tooltip, badgeVariants, buttonVariants, checkboxVariants, cn, counterVariants, getAvailableIcons, hasIcon, iconRegistry, listItemVariants, radioVariants, switchVariants, textAreaVariants, textFieldVariants, tooltipVariants };
762
1825
  //# sourceMappingURL=index.esm.js.map