@pk-design/react-tailwind 1.5.4 → 1.6.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 (65) hide show
  1. package/dist/react-tailwind.cjs.js +1429 -289
  2. package/dist/react-tailwind.cjs.js.map +1 -1
  3. package/dist/react-tailwind.es.js +1420 -292
  4. package/dist/react-tailwind.es.js.map +1 -1
  5. package/dist/types/components/BodyText/BodyText.d.ts +1 -1
  6. package/dist/types/components/BodyText/BodyText.types.d.ts +47 -8
  7. package/dist/types/components/Breadcrumb/Breadcrumb.d.ts +2 -3
  8. package/dist/types/components/Breadcrumb/Breadcrumb.types.d.ts +43 -10
  9. package/dist/types/components/Button/Button.types.d.ts +17 -63
  10. package/dist/types/components/ButtonGroup/ButtonGroup.types.d.ts +16 -0
  11. package/dist/types/components/Card/Card.d.ts +7 -1
  12. package/dist/types/components/Card/Card.types.d.ts +48 -42
  13. package/dist/types/components/Checkbox/Checkbox.types.d.ts +28 -1
  14. package/dist/types/components/CheckboxGroup/CheckboxGroup.types.d.ts +53 -1
  15. package/dist/types/components/DetailedInformation/DetailedInformation.d.ts +1 -1
  16. package/dist/types/components/DetailedInformation/DetailedInformation.types.d.ts +47 -3
  17. package/dist/types/components/Drawer/Drawer.types.d.ts +37 -13
  18. package/dist/types/components/Dropdown/Dropdown.types.d.ts +17 -1
  19. package/dist/types/components/HeadingText/HeadingText.d.ts +7 -7
  20. package/dist/types/components/HeadingText/HeadingText.test.d.ts +1 -0
  21. package/dist/types/components/HeadingText/HeadingText.types.d.ts +56 -3
  22. package/dist/types/components/Input/Input.d.ts +4 -2
  23. package/dist/types/components/Input/Input.types.d.ts +36 -3
  24. package/dist/types/components/Loader/index.d.ts +1 -1
  25. package/dist/types/components/PasswordInput/PasswordInput.d.ts +8 -4
  26. package/dist/types/components/PasswordInput/PasswordInput.types.d.ts +8 -0
  27. package/dist/types/components/QuantityStepper/QuantityStepper.d.ts +3 -0
  28. package/dist/types/components/QuantityStepper/QuantityStepper.test.d.ts +1 -0
  29. package/dist/types/components/QuantityStepper/QuantityStepper.types.d.ts +49 -0
  30. package/dist/types/components/QuantityStepper/index.d.ts +2 -0
  31. package/dist/types/components/Radio/Radio.d.ts +1 -1
  32. package/dist/types/components/Radio/Radio.types.d.ts +12 -2
  33. package/dist/types/components/RadioGroup/RadioGroup.d.ts +1 -1
  34. package/dist/types/components/RadioGroup/RadioGroup.types.d.ts +41 -13
  35. package/dist/types/components/RadioSwitch/RadioSwitch.d.ts +0 -4
  36. package/dist/types/components/RadioSwitch/RadioSwitch.types.d.ts +6 -31
  37. package/dist/types/components/RangeInput/RangeInput.d.ts +1 -1
  38. package/dist/types/components/RangeInput/RangeInput.types.d.ts +14 -1
  39. package/dist/types/components/RangeSlider/RangeSlider.d.ts +1 -1
  40. package/dist/types/components/RangeSlider/RangeSlider.types.d.ts +28 -13
  41. package/dist/types/components/SegmentedControl/SegmentedControl.d.ts +3 -0
  42. package/dist/types/components/SegmentedControl/SegmentedControl.test.d.ts +1 -0
  43. package/dist/types/components/SegmentedControl/SegmentedControl.types.d.ts +53 -0
  44. package/dist/types/components/SegmentedControl/index.d.ts +2 -0
  45. package/dist/types/components/SelectBox/SelectBox.types.d.ts +23 -14
  46. package/dist/types/components/Skeleton/Skeleton.d.ts +2 -0
  47. package/dist/types/components/Skeleton/Skeleton.test.d.ts +1 -0
  48. package/dist/types/components/Skeleton/Skeleton.types.d.ts +38 -0
  49. package/dist/types/components/Skeleton/index.d.ts +2 -0
  50. package/dist/types/components/StatusPill/StatusPill.d.ts +2 -0
  51. package/dist/types/components/StatusPill/StatusPill.test.d.ts +1 -0
  52. package/dist/types/components/StatusPill/StatusPill.types.d.ts +29 -0
  53. package/dist/types/components/StatusPill/index.d.ts +2 -0
  54. package/dist/types/components/StickyActionBar/StickyActionBar.d.ts +2 -0
  55. package/dist/types/components/StickyActionBar/StickyActionBar.test.d.ts +1 -0
  56. package/dist/types/components/StickyActionBar/StickyActionBar.types.d.ts +30 -0
  57. package/dist/types/components/StickyActionBar/index.d.ts +2 -0
  58. package/dist/types/components/TextContent/TextContent.d.ts +2 -2
  59. package/dist/types/components/TextContent/TextContent.types.d.ts +47 -7
  60. package/dist/types/components/Textarea/Textarea.types.d.ts +37 -3
  61. package/dist/types/components/Toast/Toast.d.ts +1 -1
  62. package/dist/types/components/Toast/Toast.types.d.ts +117 -43
  63. package/dist/types/components/Toast/ToastProvider.d.ts +3 -6
  64. package/dist/types/index.d.ts +5 -0
  65. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
- import React__default, { useId, useMemo, useState, forwardRef, Children, isValidElement, cloneElement, useCallback, useRef, useEffect, Fragment as Fragment$1, useImperativeHandle, createContext, useContext } from 'react';
3
+ import React__default, { useId, useMemo, useState, forwardRef, Children, isValidElement, cloneElement, useRef, useImperativeHandle, useEffect, useCallback, Fragment as Fragment$1, useLayoutEffect, createContext, useContext } from 'react';
4
4
  import { twMerge } from 'tailwind-merge';
5
- import { Dialog, DialogBackdrop, DialogPanel, DialogTitle, Menu, MenuButton, MenuItems, MenuItem, RadioGroup as RadioGroup$1, Radio as Radio$1, Combobox, ComboboxInput, ComboboxButton, ComboboxOptions, ComboboxOption, TabGroup, TabList, Tab, TabPanels, TabPanel, Transition } from '@headlessui/react';
5
+ import { Dialog, DialogBackdrop, DialogPanel, DialogTitle, Menu, MenuButton, MenuItems, MenuItem, Combobox, ComboboxInput, ComboboxButton, ComboboxOptions, ComboboxOption, TabGroup, TabList, Tab, TabPanels, TabPanel, Transition } from '@headlessui/react';
6
6
  import { FixedSizeList } from 'react-window';
7
7
 
8
8
  const buildClassName = (...classes) => twMerge(classes.filter(Boolean));
@@ -194,15 +194,46 @@ const loaderSizeMap = {
194
194
  lg: 'size-10',
195
195
  };
196
196
  const Loader = ({ className = '', size = 'md', ...props }) => {
197
- return (jsx("svg", { className: buildClassName('text-blue-600 dark:text-white', loaderSizeMap[size], className, 'spinner'), viewBox: "0 0 50 50", ...props, children: jsx("circle", { className: "path", cx: "25", cy: "25", r: "20", fill: "none", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "5" }) }));
197
+ const hasColorClass = /\btext-/.test(className);
198
+ return (jsx("svg", { className: buildClassName(!hasColorClass && 'text-blue-600 dark:text-white', loaderSizeMap[size], className, 'spinner'), viewBox: "0 0 50 50", ...props, children: jsx("circle", { className: "path", cx: "25", cy: "25", r: "20", fill: "none", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "5" }) }));
198
199
  };
199
200
 
201
+ const sizeClassMap$2 = {
202
+ xs: 'text-xs/5',
203
+ sm: 'text-sm/6',
204
+ md: 'text-base/7',
205
+ lg: 'text-lg/8',
206
+ xl: 'text-xl/9',
207
+ };
208
+ const weightClassMap$2 = {
209
+ light: 'font-light',
210
+ normal: 'font-normal',
211
+ medium: 'font-medium',
212
+ semibold: 'font-semibold',
213
+ bold: 'font-bold',
214
+ };
200
215
  const TextContent = forwardRef((props, ref) => {
201
- const { xsmall = false, small = false, strong = false, muted = false, error = false, className, ...rest } = props;
202
- return React__default.createElement('span', {
203
- ...rest,
216
+ const { as = 'span', size, weight, xsmall = false, small = false, strong = false, muted = false, error = false, success = false, warning = false, info = false, invert = false, monospace = false, truncate = false, className, children, ...rest } = props;
217
+ const textColorClass = error
218
+ ? 'text-[var(--ui-text-danger)]'
219
+ : success
220
+ ? 'text-[var(--ui-success)]'
221
+ : warning
222
+ ? 'text-[var(--ui-warning)]'
223
+ : info
224
+ ? 'text-[var(--ui-info)]'
225
+ : muted
226
+ ? invert
227
+ ? 'text-gray-300 dark:text-gray-600'
228
+ : 'text-[var(--ui-text-muted)]'
229
+ : invert
230
+ ? 'text-white dark:text-black'
231
+ : 'text-[var(--ui-text)]';
232
+ return React__default.createElement(as, {
204
233
  ref,
205
- className: buildClassName('text-[var(--ui-text)]', xsmall && 'text-xs/5', small && 'text-sm/6', strong && 'font-semibold', muted && 'text-[var(--ui-text-muted)]', error && 'text-[var(--ui-text-danger)]', className),
234
+ className: buildClassName(textColorClass, size ? sizeClassMap$2[size] : xsmall ? 'text-xs/5' : small ? 'text-sm/6' : undefined, weight ? weightClassMap$2[weight] : strong ? 'font-semibold' : undefined, monospace && 'font-mono', truncate && 'truncate inline-block max-w-full', className),
235
+ ...rest,
236
+ children,
206
237
  });
207
238
  });
208
239
  TextContent.displayName = 'TextContent';
@@ -212,19 +243,29 @@ TextContent.displayName = 'TextContent';
212
243
  */
213
244
  const THEMED_BTN_CLASS = {
214
245
  primary: {
215
- default: 'text-white bg-[var(--ui-primary)] hover:bg-[var(--ui-primary-hover)] focus:outline-[var(--ui-primary-ring)] disabled:bg-[var(--ui-primary-disabled)]',
216
- outlined: 'text-[var(--ui-primary-text)] hover:text-white border border-[var(--ui-primary)] hover:bg-[var(--ui-primary)] focus:outline-[var(--ui-primary-ring)] disabled:text-[var(--ui-primary-disabled)] disabled:border-[var(--ui-primary-disabled)]',
217
- plain: 'text-[var(--ui-primary-text)] hover:bg-black/5 dark:hover:bg-gray-700 focus:outline-[var(--ui-primary-ring)] disabled:text-[var(--ui-primary-disabled)]',
246
+ default: 'text-white bg-[var(--ui-primary)] hover:bg-[var(--ui-primary-hover)] focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-primary-ring)] disabled:bg-[var(--ui-primary-disabled)]',
247
+ outlined: 'text-[var(--ui-primary-text)] hover:text-white border border-[var(--ui-primary)] hover:bg-[var(--ui-primary)] focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-primary-ring)] disabled:text-[var(--ui-primary-disabled)] disabled:border-[var(--ui-primary-disabled)]',
248
+ plain: 'text-[var(--ui-primary-text)] hover:bg-black/5 dark:hover:bg-gray-700 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-primary-ring)] disabled:text-[var(--ui-primary-disabled)]',
218
249
  },
219
250
  danger: {
220
- default: 'text-white bg-[var(--ui-danger)] hover:bg-[var(--ui-danger-hover)] focus:outline-[var(--ui-danger-ring)] disabled:bg-[var(--ui-danger-disabled)]',
221
- outlined: 'text-[var(--ui-danger-text)] hover:text-white border border-[var(--ui-danger)] hover:bg-[var(--ui-danger)] focus:outline-[var(--ui-danger-ring)] disabled:text-[var(--ui-danger-disabled)] disabled:border-[var(--ui-danger-disabled)]',
222
- plain: 'text-[var(--ui-danger-text)] hover:bg-black/5 dark:hover:bg-gray-700 focus:outline-[var(--ui-danger-ring)] disabled:text-[var(--ui-danger-disabled)]',
251
+ default: 'text-white bg-[var(--ui-danger)] hover:bg-[var(--ui-danger-hover)] focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-danger-ring)] disabled:bg-[var(--ui-danger-disabled)]',
252
+ outlined: 'text-[var(--ui-danger-text)] hover:text-white border border-[var(--ui-danger)] hover:bg-[var(--ui-danger)] focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-danger-ring)] disabled:text-[var(--ui-danger-disabled)] disabled:border-[var(--ui-danger-disabled)]',
253
+ plain: 'text-[var(--ui-danger-text)] hover:bg-black/5 dark:hover:bg-gray-700 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-danger-ring)] disabled:text-[var(--ui-danger-disabled)]',
223
254
  },
224
255
  secondary: {
225
- default: 'text-gray-700 bg-white border border-gray-300 hover:bg-gray-100 dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700 dark:hover:border-gray-600 focus:outline-[var(--ui-focus-ring)] disabled:bg-gray-200 dark:disabled:bg-gray-700',
226
- outlined: 'text-gray-700 hover:text-white border border-gray-500 hover:bg-gray-700 dark:border-gray-500 dark:text-gray-200 dark:hover:text-white dark:hover:bg-gray-600 focus:outline-[var(--ui-focus-ring)] disabled:text-gray-400 disabled:border-gray-400',
227
- plain: 'text-gray-700 hover:bg-black/5 dark:text-gray-200 dark:hover:bg-gray-700 focus:outline-[var(--ui-focus-ring)] disabled:text-gray-400',
256
+ default: 'text-gray-700 bg-white border border-gray-300 hover:bg-gray-100 dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700 dark:hover:border-gray-600 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-focus-ring)] disabled:bg-gray-200 dark:disabled:bg-gray-700',
257
+ outlined: 'text-gray-700 hover:text-white border border-gray-500 hover:bg-gray-700 dark:border-gray-500 dark:text-gray-200 dark:hover:text-white dark:hover:bg-gray-600 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-focus-ring)] disabled:text-gray-400 disabled:border-gray-400',
258
+ plain: 'text-gray-700 hover:bg-black/5 dark:text-gray-200 dark:hover:bg-gray-700 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-focus-ring)] disabled:text-gray-400',
259
+ },
260
+ success: {
261
+ default: 'text-white bg-emerald-600 hover:bg-emerald-700 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-emerald-500 disabled:bg-emerald-300 dark:disabled:bg-emerald-900',
262
+ outlined: 'text-emerald-600 hover:text-white border border-emerald-600 hover:bg-emerald-600 dark:text-emerald-400 dark:border-emerald-500 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-emerald-500 disabled:opacity-50',
263
+ plain: 'text-emerald-600 hover:bg-emerald-50 dark:text-emerald-400 dark:hover:bg-emerald-950/40 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-emerald-500 disabled:opacity-50',
264
+ },
265
+ warning: {
266
+ default: 'text-white bg-amber-500 hover:bg-amber-600 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-amber-500 disabled:bg-amber-300 dark:disabled:bg-amber-900',
267
+ outlined: 'text-amber-600 hover:text-white border border-amber-500 hover:bg-amber-500 dark:text-amber-400 dark:border-amber-500 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-amber-500 disabled:opacity-50',
268
+ plain: 'text-amber-600 hover:bg-amber-50 dark:text-amber-400 dark:hover:bg-amber-950/40 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-amber-500 disabled:opacity-50',
228
269
  },
229
270
  };
230
271
  const LOADER_TEXT_CLASS = {
@@ -243,6 +284,16 @@ const LOADER_TEXT_CLASS = {
243
284
  outlined: 'text-gray-700 dark:text-gray-200',
244
285
  plain: 'text-gray-700 dark:text-gray-200',
245
286
  },
287
+ success: {
288
+ default: 'text-white',
289
+ outlined: 'text-emerald-600 dark:text-emerald-400',
290
+ plain: 'text-emerald-600 dark:text-emerald-400',
291
+ },
292
+ warning: {
293
+ default: 'text-white',
294
+ outlined: 'text-amber-600 dark:text-amber-400',
295
+ plain: 'text-amber-600 dark:text-amber-400',
296
+ },
246
297
  };
247
298
  /**
248
299
  * Generate size classes.
@@ -250,13 +301,15 @@ const LOADER_TEXT_CLASS = {
250
301
  const getBtnSizeClasses = (size, iconOnly = false) => {
251
302
  switch (size) {
252
303
  case 'xs':
253
- return iconOnly ? 'p-1 rounded-sm text-xs' : 'px-2 py-1 rounded-sm gap-1 text-xs';
304
+ return iconOnly ? 'h-7 w-7 p-1 rounded-sm text-xs' : 'h-7 px-2.5 rounded-sm gap-1 text-xs';
254
305
  case 'sm':
255
- return iconOnly ? 'p-1.5 rounded text-sm' : 'px-3 py-1.5 rounded gap-1.5 text-sm';
306
+ return iconOnly ? 'h-8 w-8 p-1.5 rounded text-sm' : 'h-8 px-3 rounded gap-1.5 text-sm';
256
307
  case 'md':
257
- return iconOnly ? 'p-2 rounded-md text-sm' : 'px-4 py-2 rounded-md gap-2 text-sm';
308
+ return iconOnly ? 'h-9 w-9 p-2 rounded-md text-sm' : 'h-9 px-4 rounded-md gap-2 text-sm';
258
309
  case 'lg':
259
- return iconOnly ? 'p-2.5 rounded-lg text-base' : 'px-5 py-2.5 rounded-lg gap-2 text-base';
310
+ return iconOnly
311
+ ? 'h-10 w-10 p-2.5 rounded-lg text-base'
312
+ : 'h-10 px-5 rounded-lg gap-2 text-base';
260
313
  }
261
314
  };
262
315
  /**
@@ -264,15 +317,14 @@ const getBtnSizeClasses = (size, iconOnly = false) => {
264
317
  */
265
318
  const BASE_BTN_CLASS = 'font-medium flex items-center justify-center shrink-0 cursor-pointer ' +
266
319
  'disabled:cursor-not-allowed disabled:pointer-events-none disabled:opacity-75 ' +
267
- 'transition duration-200 ease-in-out';
320
+ 'active:scale-[0.98] transition-all duration-150 ease-out focus:outline-none';
268
321
  const Button = forwardRef(({ as, type, ...props }, ref) => {
269
- const { className = '', children, theme = 'primary', variant = 'default', size = 'md', loading = false, loadingText = '', rounded = false, disabled, iconOnly = false, noOutlineOnFocus = false, ...restProps } = props;
322
+ const { className = '', children, theme = 'primary', variant = 'default', size = 'md', leftIcon, rightIcon, fullWidth = false, loading = false, loadingText = '', rounded = false, disabled, iconOnly = false, noOutlineOnFocus = false, ...restProps } = props;
270
323
  const Component = as || 'button';
271
324
  // Accessibility guard for icon-only buttons
272
325
  if (iconOnly && !restProps['aria-label']) {
273
326
  console.warn('[ui] <Button iconOnly> requires aria-label for accessibility.');
274
327
  }
275
- // Disable interaction when loading
276
328
  const resolvedDisabled = disabled || loading;
277
329
  // Clean invalid props when not rendering a real button
278
330
  const { type: _ignoreType, disabled: _ignoreDisabled, ...cleanRest } = restProps;
@@ -282,18 +334,15 @@ const Button = forwardRef(({ as, type, ...props }, ref) => {
282
334
  disabled: resolvedDisabled,
283
335
  }
284
336
  : {};
337
+ const classes = useMemo(() => buildClassName(BASE_BTN_CLASS, THEMED_BTN_CLASS[theme][variant], getBtnSizeClasses(size, iconOnly), !noOutlineOnFocus && 'focus:outline focus:outline-2 focus:outline-offset-2', rounded && 'rounded-full', fullWidth && 'w-full', variant === 'default' && 'shadow-xs hover:shadow-none disabled:shadow-none', className), [theme, variant, size, iconOnly, rounded, fullWidth, noOutlineOnFocus, className]);
285
338
  /**
286
- * Memoized className for perf & stability.
287
- */
288
- const classes = useMemo(() => buildClassName(BASE_BTN_CLASS, THEMED_BTN_CLASS[theme][variant], getBtnSizeClasses(size, iconOnly), !noOutlineOnFocus && 'focus:outline focus:outline-2 focus:outline-offset-2', rounded && 'rounded-full', variant === 'default' && 'shadow-xs hover:shadow-none disabled:shadow-none', className), [theme, variant, size, iconOnly, rounded, noOutlineOnFocus, className]);
289
- /**
290
- * SPECIAL CASE:
291
- * iconOnly + loading → show spinner only
339
+ * SPECIAL CASE: iconOnly + loading → show spinner only
292
340
  */
293
341
  if (loading && iconOnly) {
294
- return (jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: jsx(Loader, { "data-testid": "btn-loader-icon", size: "xs" }) }));
342
+ return (jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: jsx(Loader, { "data-testid": "btn-loader-icon", size: "xs", className: buildClassName(LOADER_TEXT_CLASS[theme][variant]) }) }));
295
343
  }
296
- return (jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: loading ? (jsxs("span", { className: "flex items-center gap-2", "aria-live": "polite", children: [jsx(Loader, { "data-testid": "btn-loader-icon", size: "xs", className: buildClassName(LOADER_TEXT_CLASS[theme][variant]) }), loadingText && (jsx(TextContent, { className: buildClassName(LOADER_TEXT_CLASS[theme][variant]), children: loadingText }))] })) : (children) }));
344
+ const loaderIcon = (jsx(Loader, { "data-testid": "btn-loader-icon", size: "xs", className: buildClassName(LOADER_TEXT_CLASS[theme][variant]) }));
345
+ return (jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: loading && !loadingText ? (jsxs("span", { className: "flex items-center gap-2", "aria-live": "polite", children: [loaderIcon, children] })) : loading && loadingText ? (jsxs("span", { className: "flex items-center gap-2", "aria-live": "polite", children: [loaderIcon, jsx(TextContent, { className: buildClassName(LOADER_TEXT_CLASS[theme][variant]), children: loadingText })] })) : (jsxs(Fragment, { children: [leftIcon && jsx("span", { className: "shrink-0 flex items-center", children: leftIcon }), children, rightIcon && jsx("span", { className: "shrink-0 flex items-center", children: rightIcon })] })) }));
297
346
  });
298
347
  Button.displayName = 'Button';
299
348
 
@@ -324,13 +373,13 @@ const ThemeClassMap = {
324
373
  info: 'bg-[var(--ui-info-light)] text-[var(--ui-info-text)] ring-[var(--ui-info-ring-subtle)]',
325
374
  secondary: 'bg-gray-50 dark:bg-gray-700 text-gray-700 dark:text-gray-200 ring-[var(--ui-secondary-ring-subtle)]',
326
375
  };
327
- const SIZE_CLASS = {
376
+ const SIZE_CLASS$1 = {
328
377
  sm: 'text-xs py-0.5 px-2',
329
378
  md: 'text-xs py-1 px-3',
330
379
  };
331
380
  const Badge = forwardRef((props, ref) => {
332
381
  const { theme = 'info', className, children, removable = false, rounded = false, onRemove, size = 'md', icon, ...restProps } = props;
333
- return (jsxs(TextContent, { ref: ref, className: buildClassName('inline-flex items-center gap-1 font-medium ring-1 ring-inset', SIZE_CLASS[size], ThemeClassMap[theme], rounded ? 'rounded-full' : 'rounded-md', icon ? 'pl-2' : '', removable ? 'pr-1' : '', className), "data-testid": `badge-${theme}`, ...restProps, children: [icon && jsx("span", { className: "shrink-0 inline-flex", children: icon }), children, removable && (jsx(SvgX, { className: "size-4 shrink-0 hover:bg-black/10 dark:hover:bg-white/10 rounded-sm cursor-pointer", role: "button", tabIndex: 0, "aria-label": "Remove", onClick: () => onRemove === null || onRemove === void 0 ? void 0 : onRemove(), onKeyDown: (e) => {
382
+ return (jsxs(TextContent, { ref: ref, className: buildClassName('inline-flex items-center gap-1 font-medium ring-1 ring-inset', SIZE_CLASS$1[size], ThemeClassMap[theme], rounded ? 'rounded-full' : 'rounded-md', icon ? 'pl-2' : '', removable ? 'pr-1' : '', className), "data-testid": `badge-${theme}`, ...restProps, children: [icon && jsx("span", { className: "shrink-0 inline-flex", children: icon }), children, removable && (jsx(SvgX, { className: "size-4 shrink-0 hover:bg-black/10 dark:hover:bg-white/10 rounded-sm cursor-pointer", role: "button", tabIndex: 0, "aria-label": "Remove", onClick: () => onRemove === null || onRemove === void 0 ? void 0 : onRemove(), onKeyDown: (e) => {
334
383
  if (e.key === 'Enter' || e.key === ' ') {
335
384
  e.preventDefault();
336
385
  onRemove === null || onRemove === void 0 ? void 0 : onRemove();
@@ -408,21 +457,54 @@ const Banner = forwardRef((props, ref) => {
408
457
  });
409
458
  Banner.displayName = 'Banner';
410
459
 
460
+ const sizeClassMap$1 = {
461
+ xs: 'text-xs/5',
462
+ sm: 'text-sm/6',
463
+ md: 'text-base/7',
464
+ lg: 'text-lg/8',
465
+ xl: 'text-xl/9',
466
+ };
467
+ const weightClassMap$1 = {
468
+ light: 'font-light',
469
+ normal: 'font-normal',
470
+ medium: 'font-medium',
471
+ semibold: 'font-semibold',
472
+ bold: 'font-bold',
473
+ };
474
+ const alignClassMap$1 = {
475
+ left: 'text-left',
476
+ center: 'text-center',
477
+ right: 'text-right',
478
+ justify: 'text-justify',
479
+ };
480
+ const clampClassMap = {
481
+ 1: 'line-clamp-1',
482
+ 2: 'line-clamp-2',
483
+ 3: 'line-clamp-3',
484
+ 4: 'line-clamp-4',
485
+ };
411
486
  const BodyText = forwardRef((props, ref) => {
412
- const { as = 'p', small = false, strong = false, muted = false, error = false, inline = false, className, invert, ...rest } = props;
487
+ const { as = 'p', size, weight, small = false, strong = false, muted = false, error = false, success = false, warning = false, info = false, inline = false, invert = false, align, truncate = false, clamp, className, children, ...rest } = props;
413
488
  const textColorClass = error
414
489
  ? 'text-[var(--ui-text-danger)]'
415
- : muted
416
- ? invert
417
- ? 'text-gray-300 dark:text-gray-600'
418
- : 'text-[var(--ui-text-muted)]'
419
- : invert
420
- ? 'text-white dark:text-black'
421
- : 'text-[var(--ui-text)]';
490
+ : success
491
+ ? 'text-[var(--ui-success)]'
492
+ : warning
493
+ ? 'text-[var(--ui-warning)]'
494
+ : info
495
+ ? 'text-[var(--ui-info)]'
496
+ : muted
497
+ ? invert
498
+ ? 'text-gray-300 dark:text-gray-600'
499
+ : 'text-[var(--ui-text-muted)]'
500
+ : invert
501
+ ? 'text-white dark:text-black'
502
+ : 'text-[var(--ui-text)]';
422
503
  return React__default.createElement(as, {
423
- ref: ref,
424
- className: buildClassName(textColorClass, small && 'text-sm/6', strong && 'font-semibold', inline && 'inline-flex items-center gap-2', className),
504
+ ref,
505
+ className: buildClassName(textColorClass, size ? sizeClassMap$1[size] : small && 'text-sm/6', weight ? weightClassMap$1[weight] : strong && 'font-semibold', align && alignClassMap$1[align], inline && 'inline-flex items-center gap-2', truncate && 'truncate', clamp && clampClassMap[clamp], className),
425
506
  ...rest,
507
+ children,
426
508
  });
427
509
  });
428
510
  BodyText.displayName = 'BodyText';
@@ -446,24 +528,60 @@ var SvgSlash = function SvgSlash(props) {
446
528
  })));
447
529
  };
448
530
 
449
- const Breadcrumb = ({ items, render }) => {
450
- const count = items.length;
451
- const isLast = (index) => index === count - 1;
452
- return (jsx("nav", { "aria-label": "Breadcrumb", className: "flex flex-wrap items-center gap-2", "data-testid": "breadcrumb-container", children: items.map((item, index) => (jsxs("span", { className: "inline-flex gap-2 items-center dark:text-white", "data-testid": `breadcrumb-item-${item.key}`, children: [jsx("span", { className: "font-medium", ...(isLast(index) ? { 'aria-current': 'page' } : {}), children: isLast(index) ? item.text : render(item) }), !isLast(index) && (jsx(SvgSlash, { "data-testid": `breadcrumb-divider-${index}`, className: "size-4" }))] }, item.key))) }));
531
+ const SIZE_CONTAINER_CLASS$2 = {
532
+ sm: 'text-xs gap-1.5',
533
+ md: 'text-sm gap-2',
534
+ lg: 'text-base gap-2.5',
535
+ };
536
+ const SIZE_ICON_CLASS$1 = {
537
+ sm: 'size-3',
538
+ md: 'size-3.5',
539
+ lg: 'size-4',
453
540
  };
541
+ function RenderSeparator({ separator, size, index, className, }) {
542
+ const testId = `breadcrumb-divider-${index}`;
543
+ if (separator === 'slash' || !separator) {
544
+ return (jsx(SvgSlash, { "data-testid": testId, className: buildClassName(SIZE_ICON_CLASS$1[size] || 'size-3.5', 'text-gray-400 dark:text-gray-500 shrink-0 select-none', className) }));
545
+ }
546
+ if (separator === 'chevron') {
547
+ return (jsx("svg", { "data-testid": testId, className: buildClassName(SIZE_ICON_CLASS$1[size] || 'size-3.5', 'text-gray-400 dark:text-gray-500 shrink-0 select-none', className), fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) }));
548
+ }
549
+ if (separator === 'dot') {
550
+ return (jsx("span", { "data-testid": testId, className: buildClassName('size-1 rounded-full bg-gray-400 dark:bg-gray-500 shrink-0 select-none mx-0.5', className) }));
551
+ }
552
+ return (jsx("span", { "data-testid": testId, className: buildClassName('shrink-0 select-none', className), children: separator }));
553
+ }
554
+ const Breadcrumb = forwardRef((props, ref) => {
555
+ const { items = [], render, separator = 'slash', size = 'md', maxItems, className, itemClass, activeItemClass, separatorClass, ...navProps } = props;
556
+ const [expanded, setExpanded] = useState(false);
557
+ const count = items.length;
558
+ const shouldTruncate = maxItems !== undefined && maxItems > 1 && count > maxItems && !expanded;
559
+ const getVisibleItems = () => {
560
+ if (!shouldTruncate)
561
+ return items;
562
+ // Always keep 1st item and last 1-2 items
563
+ const firstItem = items[0];
564
+ const lastItems = items.slice(count - (maxItems - 1));
565
+ return [firstItem, ...lastItems];
566
+ };
567
+ const hiddenItems = shouldTruncate ? items.slice(1, count - (maxItems - 1)) : [];
568
+ getVisibleItems();
569
+ return (jsx("nav", { ref: ref, "aria-label": "Breadcrumb", "data-testid": "breadcrumb-container", className: buildClassName('flex flex-wrap items-center text-gray-600 dark:text-gray-300 font-medium', SIZE_CONTAINER_CLASS$2[size] || SIZE_CONTAINER_CLASS$2.md, className), ...navProps, children: items.map((item, index) => {
570
+ const isLast = index === count - 1;
571
+ const isCollapsedMiddle = shouldTruncate && index > 0 && index < count - (maxItems - 1);
572
+ if (isCollapsedMiddle) {
573
+ if (index === 1) {
574
+ // Render the single '...' collapse pill
575
+ return (jsxs("span", { className: "inline-flex items-center gap-2 relative group/ellipsis", children: [jsx("button", { type: "button", onClick: () => setExpanded(true), className: "px-1.5 py-0.5 rounded-md text-xs font-semibold bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-300 transition-colors cursor-pointer", "aria-label": `Expand ${hiddenItems.length} hidden breadcrumbs`, title: `Show ${hiddenItems.length} hidden levels`, children: "\u2022\u2022\u2022" }), jsx(RenderSeparator, { separator: separator, size: size, index: index, className: separatorClass })] }, "breadcrumb-collapsed"));
576
+ }
577
+ return null;
578
+ }
579
+ const defaultContent = isLast ? (jsx("span", { className: buildClassName('text-gray-900 dark:text-white font-semibold', activeItemClass, item.className), children: item.text })) : item.to ? (jsx("a", { href: item.to, className: buildClassName('hover:text-blue-600 dark:hover:text-blue-400 transition-colors', itemClass, item.className), children: item.text })) : (jsx("span", { className: buildClassName(itemClass, item.className), children: item.text }));
580
+ return (jsxs("span", { className: "inline-flex items-center gap-2", "data-testid": `breadcrumb-item-${item.key}`, children: [jsxs("span", { className: "inline-flex items-center gap-1.5", ...(isLast ? { 'aria-current': 'page' } : {}), children: [item.icon && jsx("span", { className: "shrink-0 text-gray-400", children: item.icon }), isLast ? (defaultContent) : render ? (jsx("span", { className: buildClassName(itemClass, item.className), children: render(item) })) : (defaultContent)] }), !isLast && (jsx(RenderSeparator, { separator: separator, size: size, index: index, className: separatorClass }))] }, item.key));
581
+ }) }));
582
+ });
454
583
  Breadcrumb.displayName = 'Breadcrumb';
455
584
 
456
- /**
457
- * Corner-rounding classes for the group's first/last button, keyed by
458
- * orientation + size, matching Button's own size -> radius mapping
459
- * (xs: rounded-sm, sm: rounded, md: rounded-md, lg: rounded-lg).
460
- *
461
- * These must stay fully static string literals (no template-literal
462
- * interpolation) — Tailwind's JIT scanner only picks up class names that
463
- * appear verbatim in source, and the docs site scans the library's
464
- * *compiled* output, where interpolated fragments never appear as a
465
- * complete class name.
466
- */
467
585
  const EDGE_ROUNDING = {
468
586
  horizontal: {
469
587
  xs: { first: 'rounded-l-sm rounded-r-none', last: 'rounded-r-sm rounded-l-none' },
@@ -482,11 +600,6 @@ const PILL_EDGE_ROUNDING = {
482
600
  horizontal: { first: 'rounded-l-full rounded-r-none', last: 'rounded-r-full rounded-l-none' },
483
601
  vertical: { first: 'rounded-t-full rounded-b-none', last: 'rounded-b-full rounded-t-none' },
484
602
  };
485
- /**
486
- * Resolves the corner-rounding classes for a single button at `index`
487
- * out of `count` inside the group, so only the group's outer edges stay
488
- * rounded and interior seams go square.
489
- */
490
603
  const getEdgeRoundingClass = ({ index, count, size, orientation, pill, }) => {
491
604
  if (count <= 1)
492
605
  return '';
@@ -497,18 +610,46 @@ const getEdgeRoundingClass = ({ index, count, size, orientation, pill, }) => {
497
610
  ? PILL_EDGE_ROUNDING[orientation][position]
498
611
  : EDGE_ROUNDING[orientation][size][position];
499
612
  };
500
- const ButtonGroup = ({ children, orientation = 'horizontal', theme = 'secondary', variant = 'default', size = 'md', rounded = false, fullWidth = false, label, className = '', }) => {
613
+ const ButtonGroup = ({ children, orientation = 'horizontal', theme = 'secondary', variant = 'default', size = 'md', value, onChange, disabled: groupDisabled = false, rounded = false, fullWidth = false, label, className = '', }) => {
501
614
  const isVertical = orientation === 'vertical';
502
615
  const items = useMemo(() => Children.toArray(children).filter(isValidElement), [children]);
503
616
  const count = items.length;
504
- return (jsx("div", { role: "group", "aria-label": label, className: buildClassName('inline-flex', isVertical ? 'flex-col' : 'flex-row', fullWidth && 'flex w-full', className), children: items.map((child, index) => {
505
- var _a, _b, _c;
506
- const childSize = (_a = child.props.size) !== null && _a !== void 0 ? _a : size;
617
+ const isToggleMode = value !== undefined;
618
+ return (jsx("div", { role: "group", "aria-label": label, className: buildClassName('inline-flex items-stretch', isVertical ? 'flex-col' : 'flex-row', fullWidth && 'flex w-full', className), children: items.map((child, index) => {
619
+ var _a, _b, _c, _d, _e, _f, _g, _h;
620
+ const childProps = child.props;
621
+ const childSize = (_a = childProps.size) !== null && _a !== void 0 ? _a : size;
622
+ const childDisabled = (_b = childProps.disabled) !== null && _b !== void 0 ? _b : groupDisabled;
623
+ // Resolve item value for toggle mode
624
+ const itemVal = (_d = (_c = childProps.value) !== null && _c !== void 0 ? _c : childProps.id) !== null && _d !== void 0 ? _d : (typeof childProps.children === 'string' || typeof childProps.children === 'number'
625
+ ? childProps.children
626
+ : index);
627
+ const isSelected = isToggleMode
628
+ ? Array.isArray(value)
629
+ ? value.includes(itemVal)
630
+ : value === itemVal
631
+ : false;
632
+ const resolvedTheme = isToggleMode
633
+ ? isSelected
634
+ ? ((_e = childProps.theme) !== null && _e !== void 0 ? _e : 'primary')
635
+ : ((_f = childProps.theme) !== null && _f !== void 0 ? _f : 'secondary')
636
+ : ((_g = childProps.theme) !== null && _g !== void 0 ? _g : theme);
637
+ const resolvedVariant = (_h = childProps.variant) !== null && _h !== void 0 ? _h : variant;
638
+ const handleClick = (e) => {
639
+ var _a;
640
+ (_a = childProps.onClick) === null || _a === void 0 ? void 0 : _a.call(childProps, e);
641
+ if (isToggleMode && !childDisabled) {
642
+ onChange === null || onChange === void 0 ? void 0 : onChange(itemVal);
643
+ }
644
+ };
507
645
  return cloneElement(child, {
508
- theme: (_b = child.props.theme) !== null && _b !== void 0 ? _b : theme,
509
- variant: (_c = child.props.variant) !== null && _c !== void 0 ? _c : variant,
646
+ theme: resolvedTheme,
647
+ variant: resolvedVariant,
510
648
  size: childSize,
511
- className: buildClassName(child.props.className, getEdgeRoundingClass({
649
+ disabled: childDisabled,
650
+ ...(isToggleMode ? { 'aria-pressed': isSelected } : {}),
651
+ onClick: handleClick,
652
+ className: buildClassName(childProps.className, getEdgeRoundingClass({
512
653
  index,
513
654
  count,
514
655
  size: childSize,
@@ -516,23 +657,66 @@ const ButtonGroup = ({ children, orientation = 'horizontal', theme = 'secondary'
516
657
  pill: rounded,
517
658
  }), count > 1 && [
518
659
  'relative shadow-none hover:z-10 focus:z-10 focus-visible:z-10',
660
+ isSelected && 'z-10',
519
661
  index > 0 && (isVertical ? '-mt-px' : '-ml-px'),
662
+ index > 0 &&
663
+ resolvedVariant === 'default' &&
664
+ (isVertical
665
+ ? resolvedTheme === 'primary' || resolvedTheme === 'danger'
666
+ ? 'border-t border-white/25 dark:border-white/20'
667
+ : 'border-t border-gray-300/70 dark:border-gray-700/70'
668
+ : resolvedTheme === 'primary' || resolvedTheme === 'danger'
669
+ ? 'border-l border-white/25 dark:border-white/20'
670
+ : 'border-l border-gray-300/70 dark:border-gray-700/70'),
520
671
  ], fullWidth && 'flex-1'),
521
672
  });
522
673
  }) }));
523
674
  };
524
675
  ButtonGroup.displayName = 'ButtonGroup';
525
676
 
526
- const BASE_CARD_CLASS = 'bg-white rounded-md dark:bg-gray-900 block';
677
+ const RADIUS_CLASS = {
678
+ none: 'rounded-none',
679
+ sm: 'rounded-sm',
680
+ md: 'rounded-md',
681
+ lg: 'rounded-lg',
682
+ xl: 'rounded-xl',
683
+ '2xl': 'rounded-2xl',
684
+ };
685
+ const ASPECT_RATIO_CLASS = {
686
+ auto: 'aspect-auto',
687
+ square: 'aspect-square',
688
+ video: 'aspect-video',
689
+ wide: 'aspect-[21/9]',
690
+ };
527
691
  const Card = forwardRef(({ as, ...props }, ref) => {
528
- // Remove Card-only props from rest (avoid leaking to DOM)
529
- const { hoverable = false, bordered = true, compact = false, zeroPadding = false, ...rest } = props;
692
+ const { variant = 'outlined', radius = 'xl', hoverable = false, clickable = false, selected = false, bordered = true, compact = false, zeroPadding = false, ...rest } = props;
530
693
  const Component = as || 'div';
531
- // Extract className safely from rest
532
694
  const { className, ...cleanRest } = rest;
533
- const classes = buildClassName(BASE_CARD_CLASS, zeroPadding ? '' : compact ? 'p-2' : 'p-6', hoverable && 'transition-background duration-200 hover:bg-gray-100 dark:hover:bg-gray-800', bordered && 'border border-[var(--ui-border)]', className);
695
+ const isOutlined = variant === 'outlined' && bordered;
696
+ const isElevated = variant === 'elevated';
697
+ const isFilled = variant === 'filled';
698
+ const isGhost = variant === 'ghost';
699
+ const classes = buildClassName('block transition-all duration-200', RADIUS_CLASS[radius] || RADIUS_CLASS.xl, zeroPadding ? '' : compact ? 'p-3 sm:p-4' : 'p-5 sm:p-6',
700
+ // Surface variants
701
+ isOutlined && 'bg-white dark:bg-gray-900 border border-[var(--ui-border)]', isElevated &&
702
+ 'bg-white dark:bg-gray-900 border border-gray-100 dark:border-gray-800 shadow-md shadow-gray-200/50 dark:shadow-none', isFilled && 'bg-gray-50/80 dark:bg-gray-800/60 border border-transparent', isGhost && 'bg-transparent border border-transparent',
703
+ // Hoverable & Clickable states
704
+ hoverable && 'hover:bg-gray-100/80 dark:hover:bg-gray-800/80', clickable && 'cursor-pointer hover:-translate-y-0.5 hover:shadow-lg', selected && 'ring-2 ring-[var(--ui-primary)] border-[var(--ui-primary)]', className);
534
705
  return jsx(Component, { ref: ref, className: classes, ...cleanRest });
535
706
  });
707
+ const CardHeader = ({ className, bordered = false, children, ...props }) => (jsx("div", { className: buildClassName('flex items-center justify-between gap-4 mb-4', bordered && 'pb-4 border-b border-gray-200 dark:border-gray-800', className), ...props, children: children }));
708
+ const CardTitle = ({ className, as: Component = 'h3', children, ...props }) => (jsx(Component, { className: buildClassName('text-lg font-semibold text-gray-900 dark:text-white tracking-tight', className), ...props, children: children }));
709
+ const CardDescription = ({ className, children, ...props }) => (jsx("p", { className: buildClassName('text-sm text-gray-500 dark:text-gray-400 font-normal mt-1', className), ...props, children: children }));
710
+ const CardContent = ({ className, children, ...props }) => (jsx("div", { className: buildClassName('space-y-3', className), ...props, children: children }));
711
+ const CardFooter = ({ className, bordered = false, children, ...props }) => (jsx("div", { className: buildClassName('flex items-center justify-end gap-3 mt-4', bordered && 'pt-4 border-t border-gray-200 dark:border-gray-800', className), ...props, children: children }));
712
+ const CardMedia = ({ className, aspectRatio = 'video', alt = '', ...props }) => (jsx("div", { className: buildClassName('overflow-hidden -mx-5 -mt-5 mb-4 sm:-mx-6 sm:-mt-6', className), children: jsx("img", { alt: alt, className: buildClassName('w-full object-cover', ASPECT_RATIO_CLASS[aspectRatio] || ASPECT_RATIO_CLASS.video), ...props }) }));
713
+ Card.Header = CardHeader;
714
+ Card.Title = CardTitle;
715
+ Card.Description = CardDescription;
716
+ Card.Content = CardContent;
717
+ Card.Body = CardContent;
718
+ Card.Footer = CardFooter;
719
+ Card.Media = CardMedia;
536
720
  Card.displayName = 'Card';
537
721
 
538
722
  const Label = forwardRef((props, ref) => {
@@ -541,49 +725,127 @@ const Label = forwardRef((props, ref) => {
541
725
  });
542
726
  Label.displayName = 'Label';
543
727
 
728
+ const sizeStyles = {
729
+ sm: {
730
+ box: 'size-3.5',
731
+ svg: 'h-2.5 w-2.5',
732
+ text: 'text-xs',
733
+ subtext: 'text-[11px]',
734
+ },
735
+ md: {
736
+ box: 'size-4.5',
737
+ svg: 'h-3.25 w-3.25',
738
+ text: 'text-sm',
739
+ subtext: 'text-xs',
740
+ },
741
+ lg: {
742
+ box: 'size-5.5',
743
+ svg: 'h-4 w-4',
744
+ text: 'text-base',
745
+ subtext: 'text-sm',
746
+ },
747
+ };
544
748
  const Checkbox = forwardRef((props, ref) => {
545
749
  var _a;
546
- const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '',
547
- // Checkbox-only props removed from rest
548
- ...rest } = props;
750
+ const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, helperText, disabled = false, indeterminate = false, size = 'md', variant = 'default', description, className = '', checked, onChange, ...rest } = props;
549
751
  const generatedId = useId();
550
752
  const checkboxId = (_a = id !== null && id !== void 0 ? id : name) !== null && _a !== void 0 ? _a : generatedId;
551
- return (jsxs("div", { className: buildClassName(wrapperClass), children: [jsxs("div", { className: buildClassName('flex items-center', containerClass), children: [jsxs("label", { htmlFor: checkboxId, className: buildClassName('inline-flex items-center relative select-none', disabled ? 'cursor-not-allowed opacity-70' : 'cursor-pointer'), children: [jsx("input", { ref: ref, ...rest, id: checkboxId, name: name, type: "checkbox", disabled: disabled, "aria-checked": rest.checked, className: buildClassName('peer size-4.5 cursor-pointer transition-all appearance-none rounded', 'border focus:ring-2 focus:ring-offset-2', 'checked:bg-[var(--ui-primary)] checked:border-[var(--ui-primary)]', 'focus:ring-[var(--ui-focus-ring)] dark:focus:ring-offset-gray-900', disabled
552
- ? 'border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-gray-800 pointer-events-none'
553
- : 'bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600 hover:border-gray-400 dark:hover:border-gray-500 hover:shadow-md', className) }), jsx("span", { className: "absolute text-white opacity-0 peer-checked:opacity-100 \n top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2", children: jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-3.25 w-3.25", viewBox: "0 0 20 20", fill: "currentColor", stroke: "currentColor", strokeWidth: "1", children: jsx("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }) })] }), label && (jsx(Label, { htmlFor: checkboxId, className: buildClassName(disabled
554
- ? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
555
- : 'cursor-pointer text-gray-600 dark:text-gray-300', 'ml-2 font-normal', labelClass), children: label }))] }), error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
753
+ const innerRef = useRef(null);
754
+ useImperativeHandle(ref, () => innerRef.current);
755
+ useEffect(() => {
756
+ if (innerRef.current) {
757
+ innerRef.current.indeterminate = Boolean(indeterminate);
758
+ }
759
+ }, [indeterminate]);
760
+ const currentSize = sizeStyles[size] || sizeStyles.md;
761
+ const isCard = variant === 'card';
762
+ return (jsxs("div", { className: buildClassName(wrapperClass), children: [jsx("div", { className: buildClassName(isCard
763
+ ? 'rounded-xl border p-3.5 transition-all duration-150 relative'
764
+ : 'flex items-start', isCard &&
765
+ (disabled
766
+ ? 'border-gray-200 dark:border-gray-800 bg-gray-50/50 dark:bg-gray-900/40 opacity-70'
767
+ : checked || indeterminate
768
+ ? 'border-blue-500/80 bg-blue-50/30 dark:bg-blue-950/20 dark:border-blue-500/60 shadow-xs'
769
+ : 'border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 hover:border-gray-300 dark:hover:border-gray-700 shadow-2xs'), containerClass), children: jsxs("label", { htmlFor: checkboxId, className: buildClassName('inline-flex items-start relative select-none w-full', disabled ? 'cursor-not-allowed opacity-70' : 'cursor-pointer'), children: [jsxs("div", { className: "relative inline-flex items-center shrink-0 pt-0.5", children: [jsx("input", { ref: innerRef, ...rest, id: checkboxId, name: name, type: "checkbox", disabled: disabled, checked: checked, onChange: onChange, "aria-checked": indeterminate ? 'mixed' : checked, className: buildClassName('peer cursor-pointer transition-all appearance-none rounded', currentSize.box, 'border focus:ring-2 focus:ring-offset-2', 'checked:bg-blue-600 checked:border-blue-600 dark:checked:bg-blue-600 dark:checked:border-blue-600', 'focus:ring-[var(--ui-focus-ring)] dark:focus:ring-offset-gray-900', disabled
770
+ ? 'border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-gray-800 pointer-events-none'
771
+ : 'bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600 hover:border-gray-400 dark:hover:border-gray-500 hover:shadow-xs', indeterminate &&
772
+ '!bg-blue-600 !border-blue-600 dark:!bg-blue-600 dark:!border-blue-600', className) }), !indeterminate && (jsx("span", { className: buildClassName('absolute text-white pointer-events-none transition-opacity top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2', checked !== undefined
773
+ ? checked
774
+ ? 'opacity-100'
775
+ : 'opacity-0'
776
+ : 'opacity-0 peer-checked:opacity-100'), children: jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", stroke: "currentColor", strokeWidth: "1", children: jsx("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }) })), indeterminate && (jsx("span", { className: "absolute text-white pointer-events-none top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 opacity-100 z-10", children: jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", stroke: "currentColor", strokeWidth: "2", children: jsx("path", { fillRule: "evenodd", d: "M4 10a1 1 0 011-1h10a1 1 0 110 2H5a1 1 0 01-1-1z", clipRule: "evenodd" }) }) }))] }), (label || description) && (jsxs("div", { className: "ml-2.5 flex flex-col justify-center min-w-0 flex-1", children: [label && (jsx(Label, { htmlFor: checkboxId, className: buildClassName(disabled
777
+ ? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
778
+ : 'cursor-pointer text-gray-800 dark:text-gray-200 font-medium', currentSize.text, labelClass), children: label })), description && (jsx("span", { className: buildClassName('mt-0.5 text-gray-500 dark:text-gray-400 font-normal leading-normal', currentSize.subtext), children: description }))] }))] }) }), (error || helperText) && (jsx("div", { className: "mt-1.5 ml-0.5", children: error ? (jsx(TextContent, { error: true, small: true, children: error })) : (jsx(TextContent, { muted: true, small: true, children: helperText })) }))] }));
556
779
  });
557
780
  Checkbox.displayName = 'Checkbox';
558
781
 
782
+ const columnClasses = {
783
+ 1: 'grid grid-cols-1 gap-3',
784
+ 2: 'grid grid-cols-1 sm:grid-cols-2 gap-3',
785
+ 3: 'grid grid-cols-1 sm:grid-cols-3 gap-3',
786
+ 4: 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3',
787
+ };
559
788
  const CheckboxGroup = (props) => {
560
- const { name, options, label, labelClass = '', labelWrapperClass = '', labelHint, containerClass = '', value = [], onChange, inline = false, } = props;
789
+ const { name, options, label, labelClass = '', labelWrapperClass = '', labelHint, containerClass = '', value: controlledValue, defaultValue, onChange, inline = false, columns, size = 'md', variant = 'default', showSelectAll = false, selectAllLabel = 'Select All', error, helperText, disabled = false, } = props;
561
790
  const groupId = useId();
791
+ const [internalValue, setInternalValue] = useState(defaultValue || []);
792
+ const isControlled = controlledValue !== undefined;
793
+ const currentValue = isControlled ? controlledValue : internalValue;
562
794
  const items = useMemo(() => options.map((opt) => typeof opt === 'string' ? { label: opt, value: opt } : opt), [options]);
563
- /**
564
- * Group-level change handler. Converts raw checkbox change events
565
- * into a typed updated values array.
566
- */
795
+ const enabledItems = useMemo(() => items.filter((i) => !i.disabled && !disabled), [items, disabled]);
796
+ // Select All State calculation
797
+ const allSelected = useMemo(() => {
798
+ if (!enabledItems.length)
799
+ return false;
800
+ return enabledItems.every((item) => currentValue.includes(item.value));
801
+ }, [enabledItems, currentValue]);
802
+ const isIndeterminate = useMemo(() => {
803
+ if (allSelected)
804
+ return false;
805
+ return enabledItems.some((item) => currentValue.includes(item.value));
806
+ }, [allSelected, enabledItems, currentValue]);
807
+ const handleSelectAllChange = (e) => {
808
+ let updated;
809
+ if (allSelected || isIndeterminate) {
810
+ const enabledValues = new Set(enabledItems.map((i) => i.value));
811
+ updated = currentValue.filter((v) => !enabledValues.has(v));
812
+ }
813
+ else {
814
+ const enabledValues = enabledItems.map((i) => i.value);
815
+ const uniqueSet = new Set([...currentValue, ...enabledValues]);
816
+ updated = Array.from(uniqueSet);
817
+ }
818
+ if (!isControlled) {
819
+ setInternalValue(updated);
820
+ }
821
+ onChange === null || onChange === void 0 ? void 0 : onChange(updated, e);
822
+ };
567
823
  const handleGroupChange = (e) => {
568
824
  const raw = e.target.value;
569
825
  const checked = e.target.checked;
570
- // Convert HTML string value → typed T value
571
- // Locate in normalized items
572
826
  const matched = items.find((i) => String(i.value) === raw);
573
- const itemValue = matched === null || matched === void 0 ? void 0 : matched.value;
827
+ const itemValue = matched ? matched.value : raw;
574
828
  let updated;
575
829
  if (checked) {
576
- updated = [...value, itemValue];
830
+ updated = [...currentValue, itemValue];
577
831
  }
578
832
  else {
579
- updated = value.filter((v) => v !== itemValue);
833
+ updated = currentValue.filter((v) => v !== itemValue);
834
+ }
835
+ if (!isControlled) {
836
+ setInternalValue(updated);
580
837
  }
581
838
  onChange === null || onChange === void 0 ? void 0 : onChange(updated, e);
582
839
  };
583
- return (jsxs("div", { className: buildClassName('mb-3 space-y-3', containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsx(Label, { className: labelClass, children: label }), jsx(BodyText, { muted: true, small: true, children: labelHint })] })), jsx("div", { className: buildClassName('flex gap-3', inline ? 'flex-wrap' : 'flex-col'), children: items.map((item, index) => {
840
+ const layoutClass = columns
841
+ ? columnClasses[columns]
842
+ : inline
843
+ ? 'flex flex-wrap gap-3'
844
+ : 'flex flex-col gap-3';
845
+ return (jsxs("div", { className: buildClassName('mb-3 space-y-3', containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsx(Label, { className: labelClass, children: label }), labelHint && (jsx(BodyText, { muted: true, small: true, children: labelHint }))] })), showSelectAll && items.length > 0 && (jsx("div", { className: "pb-1 border-b border-gray-200 dark:border-gray-800", children: jsx(Checkbox, { id: `${groupId}-select-all`, label: selectAllLabel, size: size, variant: variant, checked: allSelected, indeterminate: isIndeterminate, disabled: disabled || enabledItems.length === 0, onChange: handleSelectAllChange }) })), jsx("div", { className: layoutClass, children: items.map((item, index) => {
584
846
  const key = typeof item.value === 'string' || typeof item.value === 'number' ? item.value : index;
585
- return (jsx(Checkbox, { name: name, id: `${groupId}-${String(item.value)}`, label: item.label, value: String(item.value), checked: value.includes(item.value), onChange: handleGroupChange }, key));
586
- }) })] }));
847
+ return (jsx(Checkbox, { name: name, id: `${groupId}-${String(item.value)}`, label: item.label, description: item.description, value: String(item.value), size: size, variant: variant, disabled: disabled || item.disabled, checked: currentValue.includes(item.value), onChange: handleGroupChange }, key));
848
+ }) }), (error || helperText) && (jsx("div", { className: "mt-1.5 ml-0.5", children: error ? (jsx(TextContent, { error: true, small: true, children: error })) : (jsx(TextContent, { muted: true, small: true, children: helperText })) }))] }));
587
849
  };
588
850
  CheckboxGroup.displayName = 'CheckboxGroup';
589
851
 
@@ -1010,24 +1272,142 @@ function DataTable({ items, columns, sorting = {}, onSort, loading, pagination,
1010
1272
  }
1011
1273
  DataTable.displayName = 'DataTable';
1012
1274
 
1013
- const Title = ({ children, className }) => (jsx("h1", { className: buildClassName('text-3xl font-bold text-[var(--ui-text)]', className), children: children }));
1014
- const SubTitle = ({ children, className }) => (jsx("h2", { className: buildClassName('text-2xl font-semibold text-[var(--ui-text)]', className), children: children }));
1015
- const SubTitle2 = ({ children, className }) => (jsx("h3", { className: buildClassName('text-xl font-semibold text-[var(--ui-text)]', className), children: children }));
1016
- const SubTitle3 = ({ children, className }) => (jsx("h4", { className: buildClassName('text-lg font-medium text-[var(--ui-text)]', className), children: children }));
1017
- const SubTitle4 = ({ children, className }) => (jsx("h5", { className: buildClassName('text-base font-medium text-[var(--ui-text)]', className), children: children }));
1018
- const HeadingText = {
1275
+ const defaultLevelSizeMap = {
1276
+ 1: 'text-3xl font-bold',
1277
+ 2: 'text-2xl font-semibold',
1278
+ 3: 'text-xl font-semibold',
1279
+ 4: 'text-lg font-medium',
1280
+ 5: 'text-base font-medium',
1281
+ 6: 'text-sm font-medium',
1282
+ };
1283
+ const sizeClassMap = {
1284
+ xs: 'text-xs',
1285
+ sm: 'text-sm',
1286
+ md: 'text-base',
1287
+ lg: 'text-lg',
1288
+ xl: 'text-xl',
1289
+ '2xl': 'text-2xl',
1290
+ '3xl': 'text-3xl',
1291
+ '4xl': 'text-4xl',
1292
+ '5xl': 'text-5xl',
1293
+ };
1294
+ const weightClassMap = {
1295
+ normal: 'font-normal',
1296
+ medium: 'font-medium',
1297
+ semibold: 'font-semibold',
1298
+ bold: 'font-bold',
1299
+ extrabold: 'font-extrabold',
1300
+ };
1301
+ const alignClassMap = {
1302
+ left: 'text-left',
1303
+ center: 'text-center',
1304
+ right: 'text-right',
1305
+ };
1306
+ const trackingClassMap = {
1307
+ tighter: 'tracking-tighter',
1308
+ tight: 'tracking-tight',
1309
+ normal: 'tracking-normal',
1310
+ wide: 'tracking-wide',
1311
+ };
1312
+ const HeadingTextComponent = forwardRef((props, ref) => {
1313
+ const { level = 1, size, weight, muted = false, error = false, success = false, warning = false, info = false, invert = false, align, tracking, truncate = false, className, children, ...rest } = props;
1314
+ const tag = `h${Math.min(6, Math.max(1, level))}`;
1315
+ const colorClass = error
1316
+ ? 'text-[var(--ui-text-danger)]'
1317
+ : success
1318
+ ? 'text-[var(--ui-success)]'
1319
+ : warning
1320
+ ? 'text-[var(--ui-warning)]'
1321
+ : info
1322
+ ? 'text-[var(--ui-info)]'
1323
+ : muted
1324
+ ? invert
1325
+ ? 'text-gray-300 dark:text-gray-600'
1326
+ : 'text-[var(--ui-text-muted)]'
1327
+ : invert
1328
+ ? 'text-white dark:text-black'
1329
+ : 'text-[var(--ui-text)]';
1330
+ return React__default.createElement(tag, {
1331
+ ref,
1332
+ className: buildClassName(colorClass, size ? sizeClassMap[size] : defaultLevelSizeMap[level], weight && weightClassMap[weight], align && alignClassMap[align], tracking && trackingClassMap[tracking], truncate && 'truncate', className),
1333
+ ...rest,
1334
+ children,
1335
+ });
1336
+ });
1337
+ HeadingTextComponent.displayName = 'HeadingText';
1338
+ const Title = forwardRef((props, ref) => (jsx(HeadingTextComponent, { ref: ref, level: 1, ...props })));
1339
+ Title.displayName = 'HeadingText.Title';
1340
+ const SubTitle = forwardRef((props, ref) => (jsx(HeadingTextComponent, { ref: ref, level: 2, ...props })));
1341
+ SubTitle.displayName = 'HeadingText.SubTitle';
1342
+ const SubTitle2 = forwardRef((props, ref) => (jsx(HeadingTextComponent, { ref: ref, level: 3, ...props })));
1343
+ SubTitle2.displayName = 'HeadingText.SubTitle2';
1344
+ const SubTitle3 = forwardRef((props, ref) => (jsx(HeadingTextComponent, { ref: ref, level: 4, ...props })));
1345
+ SubTitle3.displayName = 'HeadingText.SubTitle3';
1346
+ const SubTitle4 = forwardRef((props, ref) => (jsx(HeadingTextComponent, { ref: ref, level: 5, ...props })));
1347
+ SubTitle4.displayName = 'HeadingText.SubTitle4';
1348
+ const HeadingText = Object.assign(HeadingTextComponent, {
1019
1349
  Title,
1020
1350
  SubTitle,
1021
1351
  SubTitle2,
1022
1352
  SubTitle3,
1023
1353
  SubTitle4,
1024
- };
1354
+ });
1025
1355
 
1356
+ const COLUMN_GRID_CLASS = {
1357
+ 1: 'grid-cols-1',
1358
+ 2: 'grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-3',
1359
+ 3: 'grid grid-cols-1 md:grid-cols-3 gap-x-6 gap-y-3',
1360
+ 4: 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-x-6 gap-y-3',
1361
+ };
1362
+ function CopyableValue({ text, children }) {
1363
+ const [copied, setCopied] = useState(false);
1364
+ const handleCopy = async (e) => {
1365
+ e.stopPropagation();
1366
+ try {
1367
+ await navigator.clipboard.writeText(text);
1368
+ setCopied(true);
1369
+ setTimeout(() => setCopied(false), 2000);
1370
+ }
1371
+ catch {
1372
+ // Ignore clipboard error
1373
+ }
1374
+ };
1375
+ return (jsxs("span", { className: "inline-flex items-center gap-1.5 group/copy", children: [jsx("span", { children: children }), jsx("button", { type: "button", onClick: handleCopy, className: "opacity-0 group-hover/copy:opacity-100 focus:opacity-100 transition-opacity p-0.5 text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 cursor-pointer", title: "Copy to clipboard", "aria-label": "Copy to clipboard", children: copied ? (jsx("svg", { className: "size-3.5 text-emerald-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" }) })) : (jsx("svg", { className: "size-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" }) })) })] }));
1376
+ }
1026
1377
  const DetailedInformation = forwardRef((props, ref) => {
1027
- const { className, title, subTitle, details, compact = false, divider = true, titleClass, detailsClass, bordered = false, ...divProps } = props;
1028
- const hasTitles = title || subTitle;
1378
+ const { className, title, subTitle, action, details = [], columns = 1, layout = 'horizontal', variant = 'flat', compact = false, bordered = false, divider = true, emptyValue = '- -', titleClass, detailsClass, labelClass, ...divProps } = props;
1379
+ const isCard = variant === 'card';
1380
+ const isBordered = bordered || variant === 'bordered' || isCard;
1381
+ const hasTitles = title || subTitle || action;
1029
1382
  const filteredDetails = details.filter((detail) => !detail.hidden);
1030
- return (jsxs("div", { className: buildClassName('p-2 md:p-4', compact ? 'md:p-2' : '', bordered ? 'border border-[var(--ui-border)]' : '', className), ref: ref, ...divProps, children: [hasTitles && (jsxs("div", { className: "px-4 sm:px-0 pt-2 sm:pt-0 mb-2", children: [title && (jsx(HeadingText.SubTitle2, { className: buildClassName(titleClass), children: title })), subTitle && (jsx("p", { className: "mt-1 max-w-2xl text-sm/6 text-gray-500 dark:text-gray-300", children: subTitle }))] })), filteredDetails.length ? (jsx("div", { className: buildClassName('border-[var(--ui-border-muted)]', hasTitles && 'border-t', detailsClass), children: jsx("dl", { className: buildClassName(divider && 'divide-y divide-[var(--ui-border-muted)]'), children: filteredDetails.map((detail) => (jsxs("div", { className: buildClassName('px-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 items-center', compact ? 'py-1.5' : 'py-2.5'), children: [jsx("dt", { className: "text-sm/6 font-medium text-gray-900 dark:text-gray-100", children: detail.label }), jsx("dd", { className: buildClassName('mt-1 text-sm/6 text-gray-700 dark:text-gray-300 sm:col-span-2 sm:mt-0', detail.contentClass), children: detail.value || '- -' })] }, detail.label))) }) })) : null] }));
1383
+ const isMultiColumn = columns > 1;
1384
+ return (jsxs("div", { ref: ref, className: buildClassName(isCard
1385
+ ? compact
1386
+ ? 'p-3 sm:p-4 bg-gray-50/60 dark:bg-gray-800/40 rounded-2xl border border-gray-200 dark:border-gray-700'
1387
+ : 'p-4 sm:p-6 bg-gray-50/60 dark:bg-gray-800/40 rounded-2xl border border-gray-200 dark:border-gray-700'
1388
+ : isBordered
1389
+ ? compact
1390
+ ? 'p-3 sm:p-4 border border-[var(--ui-border)] rounded-2xl'
1391
+ : 'p-4 sm:p-6 border border-[var(--ui-border)] rounded-2xl'
1392
+ : compact
1393
+ ? 'p-2 md:p-3'
1394
+ : 'p-3 md:p-4', className), ...divProps, children: [hasTitles && (jsxs("div", { className: "mb-4 flex items-start justify-between gap-4", children: [jsxs("div", { children: [title && (jsx(HeadingText.SubTitle2, { className: buildClassName(titleClass), children: title })), subTitle && (jsx("p", { className: "mt-1 max-w-2xl text-sm/6 text-gray-500 dark:text-gray-400 font-normal", children: subTitle }))] }), action && jsx("div", { className: "shrink-0 flex items-center", children: action })] })), filteredDetails.length ? (jsx("div", { className: buildClassName('border-[var(--ui-border-muted)]', hasTitles && !isMultiColumn && 'border-t pt-2', detailsClass), children: jsx("dl", { className: buildClassName(COLUMN_GRID_CLASS[columns] || COLUMN_GRID_CLASS[1], !isMultiColumn && divider && 'divide-y divide-[var(--ui-border-muted)]'), children: filteredDetails.map((detail, index) => {
1395
+ var _a;
1396
+ const itemFallback = (_a = detail.emptyValue) !== null && _a !== void 0 ? _a : emptyValue;
1397
+ const hasValue = detail.value !== undefined && detail.value !== null && detail.value !== '';
1398
+ const displayValue = hasValue ? detail.value : itemFallback;
1399
+ const copyText = typeof detail.value === 'string' || typeof detail.value === 'number'
1400
+ ? String(detail.value)
1401
+ : '';
1402
+ const isVertical = layout === 'vertical' || isMultiColumn;
1403
+ return (jsxs("div", { className: buildClassName(isMultiColumn && isCard
1404
+ ? compact
1405
+ ? 'p-3 bg-white dark:bg-gray-900 rounded-xl border border-gray-200/80 dark:border-gray-700/80 shadow-2xs flex flex-col gap-1'
1406
+ : 'p-4 bg-white dark:bg-gray-900 rounded-xl border border-gray-200/80 dark:border-gray-700/80 shadow-2xs flex flex-col gap-1'
1407
+ : isVertical
1408
+ ? 'flex flex-col gap-1 py-2 px-1'
1409
+ : 'grid grid-cols-3 gap-4 items-center py-2.5 px-1', compact && !isMultiColumn ? 'py-1.5' : ''), children: [jsxs("dt", { className: buildClassName('text-sm/6 font-medium text-gray-900 dark:text-gray-100 flex items-center gap-1.5', labelClass, detail.labelClass), children: [detail.icon && jsx("span", { className: "shrink-0 text-gray-400", children: detail.icon }), jsx("span", { children: detail.label }), detail.helpText && (jsx("span", { className: "text-xs text-gray-400 font-normal cursor-help", title: detail.helpText, children: "(?)" }))] }), jsx("dd", { className: buildClassName('text-sm/6 text-gray-700 dark:text-gray-300', !isVertical && 'sm:col-span-2 sm:mt-0', detail.contentClass), children: detail.copyable && copyText ? (jsx(CopyableValue, { text: copyText, children: displayValue })) : (displayValue) })] }, detail.label || index));
1410
+ }) }) })) : null] }));
1031
1411
  });
1032
1412
  DetailedInformation.displayName = 'DetailedInformation';
1033
1413
 
@@ -1040,15 +1420,15 @@ const alignmentMap = {
1040
1420
  center: 'items-center justify-center h-full',
1041
1421
  start: 'justify-start h-full',
1042
1422
  end: 'justify-end h-full',
1043
- top: 'items-center justify-center',
1423
+ top: 'items-start justify-center w-full',
1044
1424
  bottom: 'items-end justify-center h-full',
1045
1425
  };
1046
1426
  const panelAlignmentMap = {
1047
- center: 'data-[closed]:scale-95 rounded-lg',
1048
- start: 'data-[closed]:-translate-x-1/2 rounded-r-lg',
1049
- end: 'data-[closed]:translate-x-1/2 rounded-l-lg',
1050
- top: 'data-[closed]:-translate-y-1/2 rounded-b-lg',
1051
- bottom: 'data-[closed]:translate-y-1/2 rounded-t-lg',
1427
+ center: 'data-[closed]:scale-95 rounded-2xl',
1428
+ start: 'data-[closed]:-translate-x-1/2 rounded-r-2xl',
1429
+ end: 'data-[closed]:translate-x-1/2 rounded-l-2xl',
1430
+ top: 'data-[closed]:-translate-y-1/2 rounded-b-2xl',
1431
+ bottom: 'data-[closed]:translate-y-1/2 rounded-t-2xl md:rounded-t-3xl max-h-[92svh]',
1052
1432
  };
1053
1433
  const panelSizeMap = {
1054
1434
  xs: 'max-w-xs',
@@ -1059,15 +1439,56 @@ const panelSizeMap = {
1059
1439
  '2xl': 'max-w-2xl',
1060
1440
  full: 'w-full',
1061
1441
  };
1062
- const Drawer = ({ isOpen, onClose = () => { }, align = 'end', size = 'md', backdrop = true, title, description, children, panelClass = '', contentClass = '', titleClass = '', descriptionClass = '', titleSticky = false, showBackButton = false, }) => {
1063
- const showTitleSection = Boolean(title || description);
1064
- return (jsxs(Dialog, { open: isOpen, as: "div", className: "relative z-50 focus:outline-none", onClose: onClose, transition: true, children: [backdrop && jsx(DialogBackdrop, { className: "fixed inset-0 bg-black/20 transition-opacity" }), jsx("div", { className: "fixed inset-0 w-screen h-[100svh]", children: jsx("div", { className: buildClassName('flex', alignmentMap[align]), children: jsxs(DialogPanel, { transition: true, className: buildClassName('w-full bg-white dark:bg-gray-800 backdrop-blur-2xl shadow-sm', 'overflow-y-auto space-y-4 duration-200 ease-out data-[closed]:opacity-0', panelAlignmentMap[align], panelSizeMap[size], panelClass), children: [showTitleSection && (jsxs("div", { className: buildClassName('space-y-2 pt-2 md:pt-4 px-4 md:px-6', titleSticky && 'sticky top-0 bg-white dark:bg-gray-800 z-10'), children: [jsxs(DialogTitle, { as: "h3", className: buildClassName('text-lg font-medium dark:text-white flex items-center gap-2', titleClass), children: [showBackButton && (jsx(Button, { iconOnly: true, "aria-label": "Close drawer", variant: "plain", theme: "secondary", size: "sm", onClick: onClose, children: jsx(SvgChevronLeft, { className: "size-5" }) })), title] }), description && (jsx("p", { className: buildClassName('text-sm dark:text-white/60', descriptionClass), children: description }))] })), jsx("div", { className: contentClass, children: children })] }) }) })] }));
1442
+ const Drawer = ({ isOpen, onClose = () => { }, closeOnOutsideClick = true, align = 'end', size = 'md', backdrop = true, title, description, children, footer, footerSticky = false, panelClass = '', contentClass = '', titleClass = '', descriptionClass = '', titleSticky = false, showBackButton = false, showCloseButton = true, dragHandle, }) => {
1443
+ const showTitleSection = Boolean(title || description || showBackButton || showCloseButton);
1444
+ const shouldShowDragHandle = dragHandle !== null && dragHandle !== void 0 ? dragHandle : align === 'bottom';
1445
+ const handleDialogClose = () => {
1446
+ if (closeOnOutsideClick) {
1447
+ onClose === null || onClose === void 0 ? void 0 : onClose();
1448
+ }
1449
+ };
1450
+ return (jsxs(Dialog, { open: isOpen, as: "div", className: "relative z-50 focus:outline-none", onClose: handleDialogClose, transition: true, children: [backdrop && (jsx(DialogBackdrop, { className: "fixed inset-0 bg-black/40 backdrop-blur-xs transition-opacity duration-200" })), jsx("div", { className: "fixed inset-0 w-screen h-[100svh]", children: jsx("div", { className: buildClassName('flex', alignmentMap[align]), children: jsxs(DialogPanel, { transition: true, className: buildClassName('w-full max-w-full bg-white dark:bg-gray-800 shadow-xl flex flex-col min-w-0', 'overflow-y-auto overflow-x-hidden duration-200 ease-out data-[closed]:opacity-0', panelAlignmentMap[align], panelSizeMap[size], panelClass), children: [shouldShowDragHandle && (jsx("div", { className: "w-12 h-1.5 rounded-full bg-gray-300 dark:bg-gray-600 mx-auto my-2.5 shrink-0 cursor-grab", "data-testid": "drawer-drag-handle" })), showTitleSection && (jsxs("div", { className: buildClassName('space-y-1 py-3 px-4 md:px-6 flex flex-col border-b border-gray-100 dark:border-gray-700/60 shrink-0', titleSticky && 'sticky top-0 bg-white dark:bg-gray-800 z-10'), children: [jsxs("div", { className: "flex items-center justify-between gap-3 min-w-0", children: [jsxs("div", { className: "flex items-center gap-2 min-w-0 flex-1", children: [showBackButton && (jsx(Button, { iconOnly: true, "aria-label": "Back", variant: "plain", theme: "secondary", size: "xs", onClick: onClose, children: jsx(SvgChevronLeft, { className: "size-4" }) })), title && (jsx(DialogTitle, { as: "h3", className: buildClassName('text-lg font-semibold text-gray-900 dark:text-white truncate tracking-tight min-w-0', titleClass), children: title }))] }), showCloseButton && (jsx(Button, { iconOnly: true, "aria-label": "Close drawer", variant: "plain", theme: "secondary", size: "xs", onClick: onClose, className: "rounded-full text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 shrink-0", children: jsx(SvgX, { className: "size-4" }) }))] }), description && (jsx("p", { className: buildClassName('text-sm text-gray-500 dark:text-gray-400 font-normal truncate', descriptionClass), children: description }))] })), jsx("div", { className: buildClassName('flex-1 p-4 md:p-6 space-y-4 min-w-0', contentClass), children: children }), footer && (jsx("div", { className: buildClassName('p-4 border-t border-gray-100 dark:border-gray-700/60 bg-gray-50/50 dark:bg-gray-800/80 shrink-0 min-w-0', footerSticky && 'sticky bottom-0 z-10'), children: footer }))] }) }) })] }));
1065
1451
  };
1066
1452
  Drawer.displayName = 'Drawer';
1067
1453
 
1068
- const BASE_ITEM_CLASS = 'flex w-full px-3 py-2 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700 rounded-lg';
1069
- const Dropdown = ({ triggerButton, renderTriggerButton, items, renderItem, itemsContainerClass, transition = true, anchor = 'bottom start', menuItemClass, onMenuClick, }) => {
1070
- return (jsxs(Menu, { as: "div", className: "relative inline-block", children: [renderTriggerButton ? (jsx(MenuButton, { as: Fragment$1, children: (state) => renderTriggerButton(state) })) : (jsx(MenuButton, { as: Fragment$1, children: triggerButton })), jsx(MenuItems, { transition: transition, anchor: anchor, className: buildClassName('w-52 origin-top-right rounded-xl shadow-sm p-1 text-sm transition duration-100 ease-out', 'border border-[var(--ui-border-muted)] bg-white text-gray-800', 'dark:bg-gray-800 dark:text-gray-200', 'focus:outline-none data-[closed]:scale-95 data-[closed]:opacity-0', '[--anchor-gap:--spacing(1)]', itemsContainerClass), "data-testid": "dropdown-items", as: "ul", children: items.map((item, idx) => (jsxs(Fragment$1, { children: [jsx(MenuItem, { as: "li", disabled: item.disabled, children: ({ disabled }) => (jsx("div", { onClick: () => !disabled && (onMenuClick === null || onMenuClick === void 0 ? void 0 : onMenuClick(item, idx)), className: buildClassName(BASE_ITEM_CLASS, menuItemClass, disabled && 'cursor-not-allowed opacity-70'), "data-testid": `dropdown-item-${item.id}`, children: renderItem ? renderItem(item) : item.label })) }), item.divider && (jsx("li", { className: "my-1 h-px bg-gray-200 dark:bg-white/5", "data-testid": "dropdown-divider" }, item.id))] }, item.id))) })] }));
1454
+ const WIDTH_CLASS = {
1455
+ auto: 'w-auto min-w-[120px]',
1456
+ sm: 'w-44',
1457
+ md: 'w-56',
1458
+ lg: 'w-64',
1459
+ xl: 'w-72',
1460
+ };
1461
+ const Dropdown = ({ triggerButton, renderTriggerButton, className, items, renderItem, itemsContainerClass, transition = true, anchor = 'bottom start', width = 'md', menuItemClass, onMenuClick, ...restProps }) => {
1462
+ var _a, _b, _c, _d;
1463
+ const widthClass = WIDTH_CLASS[width] || width || WIDTH_CLASS.md;
1464
+ const { theme, variant, size, disabled } = restProps;
1465
+ return (jsxs(Menu, { as: "div", className: "relative inline-block", children: [renderTriggerButton ? (jsx(MenuButton, { as: Fragment$1, children: (state) => {
1466
+ const rendered = renderTriggerButton(state);
1467
+ if (isValidElement(rendered) && className) {
1468
+ return cloneElement(rendered, {
1469
+ className: buildClassName(rendered.props.className, className),
1470
+ });
1471
+ }
1472
+ return rendered;
1473
+ } })) : isValidElement(triggerButton) ? (jsx(MenuButton, { as: Fragment$1, children: cloneElement(triggerButton, {
1474
+ className: buildClassName(triggerButton.props.className, className),
1475
+ theme: (_a = triggerButton.props.theme) !== null && _a !== void 0 ? _a : theme,
1476
+ variant: (_b = triggerButton.props.variant) !== null && _b !== void 0 ? _b : variant,
1477
+ size: (_c = triggerButton.props.size) !== null && _c !== void 0 ? _c : size,
1478
+ disabled: (_d = triggerButton.props.disabled) !== null && _d !== void 0 ? _d : disabled,
1479
+ }) })) : (jsx(MenuButton, { as: Fragment$1, children: triggerButton })), jsx(MenuItems, { transition: transition, anchor: anchor, className: buildClassName('origin-top-right rounded-xl shadow-lg p-1.5 text-sm transition duration-100 ease-out z-50', 'border border-gray-200/80 dark:border-gray-700/80 bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200', 'focus:outline-none data-[closed]:scale-95 data-[closed]:opacity-0', '[--anchor-gap:--spacing(1)]', widthClass, itemsContainerClass), "data-testid": "dropdown-items", as: "ul", children: items.map((item, idx) => (jsxs(Fragment$1, { children: [item.groupTitle && (jsx("li", { className: "px-2.5 pt-2 pb-1 text-[11px] font-semibold text-gray-400 dark:text-gray-500 uppercase tracking-wider select-none", "data-testid": `dropdown-group-${item.id}`, children: item.groupTitle }, `group-${item.id}`)), jsx(MenuItem, { as: "li", disabled: item.disabled, children: ({ disabled, active }) => (jsx("div", { onClick: () => {
1480
+ var _a;
1481
+ if (!disabled) {
1482
+ (_a = item.onClick) === null || _a === void 0 ? void 0 : _a.call(item, item);
1483
+ onMenuClick === null || onMenuClick === void 0 ? void 0 : onMenuClick(item, idx);
1484
+ }
1485
+ }, className: buildClassName('flex items-center justify-between gap-3 w-full px-2.5 py-1.5 rounded-lg text-sm transition-colors cursor-pointer select-none', item.danger
1486
+ ? buildClassName('text-rose-600 dark:text-rose-400', active
1487
+ ? 'bg-rose-50 dark:bg-rose-950/40'
1488
+ : 'hover:bg-rose-50 dark:hover:bg-rose-950/40')
1489
+ : buildClassName('text-gray-700 dark:text-gray-200', active
1490
+ ? 'bg-gray-100 dark:bg-gray-700/80'
1491
+ : 'hover:bg-gray-100/80 dark:hover:bg-gray-700/80'), disabled && 'opacity-40 cursor-not-allowed pointer-events-none', menuItemClass), "data-testid": `dropdown-item-${item.id}`, children: renderItem ? (renderItem(item)) : (jsxs(Fragment, { children: [jsxs("div", { className: "flex items-center gap-2.5 min-w-0 flex-1", children: [item.icon && (jsx("span", { className: buildClassName('shrink-0', item.danger ? 'text-rose-500' : 'text-gray-400 dark:text-gray-500'), children: item.icon })), jsxs("div", { className: "flex flex-col min-w-0", children: [item.label && jsx("span", { className: "font-medium truncate", children: item.label }), item.description && (jsx("span", { className: "text-xs text-gray-400 dark:text-gray-500 font-normal truncate", children: item.description }))] })] }), item.shortcut && (jsx("kbd", { className: "shrink-0 text-[10px] font-medium font-mono px-1.5 py-0.5 rounded bg-gray-100 dark:bg-gray-700/80 border border-gray-200/60 dark:border-gray-600/60 text-gray-400 dark:text-gray-400", children: item.shortcut }))] })) })) }), item.divider && idx < items.length - 1 && (jsx("li", { className: "my-1 h-px bg-gray-200/80 dark:bg-gray-700/80", "data-testid": "dropdown-divider" }, `divider-${item.id}`))] }, item.id))) })] }));
1071
1492
  };
1072
1493
  Dropdown.displayName = 'Dropdown';
1073
1494
 
@@ -1094,90 +1515,294 @@ const EmptyState = ({ title, description, className, action = null, }) => {
1094
1515
  return (jsx("div", { className: buildClassName('w-full flex justify-center px-4 pt-16', className), children: jsxs("div", { className: "max-w-md flex flex-col items-center gap-4", children: [jsx(SvgFolderPlus, { className: "size-8 text-[var(--ui-text)]" }), jsx(HeadingText.SubTitle, { children: title }), description && (jsx(BodyText, { as: "p", className: "text-center", muted: true, small: true, children: description })), action] }) }));
1095
1516
  };
1096
1517
 
1097
- function buildInputClass(className, { isFocusWithin = false, disabled = false } = {}) {
1098
- return buildClassName('transition-all', 'block w-full rounded-lg bg-white px-3.5 py-2 text-base sm:text-sm/6', 'text-gray-900 outline outline-1 -outline-offset-1', 'outline-gray-300 dark:outline-gray-600', 'invalid:group-[.has-error]:outline-red-500', disabled
1518
+ const sizeClasses$4 = {
1519
+ sm: {
1520
+ input: 'px-2.5 py-1 text-xs leading-5',
1521
+ group: 'w-8 text-xs',
1522
+ icon: 'size-3.5',
1523
+ text: 'text-xs',
1524
+ },
1525
+ md: {
1526
+ input: 'px-3.5 py-2 text-sm leading-6',
1527
+ group: 'w-10 text-sm',
1528
+ icon: 'size-4',
1529
+ text: 'text-sm',
1530
+ },
1531
+ lg: {
1532
+ input: 'px-4 py-2.5 text-base leading-7',
1533
+ group: 'w-12 text-base',
1534
+ icon: 'size-5',
1535
+ text: 'text-base',
1536
+ },
1537
+ };
1538
+ function buildInputClass(className, { isFocusWithin = false, disabled = false, hasError = false, size = 'md', } = {}) {
1539
+ const currentSize = sizeClasses$4[size] || sizeClasses$4.md;
1540
+ return buildClassName('transition-all duration-150', 'block w-full rounded-lg bg-white', currentSize.input, 'text-gray-900 outline outline-1 -outline-offset-1', hasError
1541
+ ? '!outline-red-500 !ring-1 !ring-red-500/50 dark:!outline-red-500'
1542
+ : 'outline-gray-300 dark:outline-gray-600', disabled
1099
1543
  ? 'pointer-events-none bg-gray-100 dark:bg-gray-800 dark:text-gray-600 dark:placeholder-gray-600 placeholder-gray-400 outline-gray-200 dark:outline-gray-700'
1100
1544
  : 'bg-white dark:bg-gray-700 dark:text-white outline-gray-300 dark:outline-gray-600 dark:placeholder-gray-400 placeholder-gray-400', isFocusWithin
1101
1545
  ? 'focus-within:outline-[var(--ui-focus-ring)] focus-within:outline-2 focus-within:-outline-offset-2'
1102
1546
  : 'focus:outline-[var(--ui-focus-ring)] focus:outline-2 focus:-outline-offset-2', className);
1103
1547
  }
1104
1548
  const Input = forwardRef((props, ref) => {
1105
- const { name, id = name, type, label, labelHint, className, labelClass, labelWrapperClass, containerClass, error, showErrorMessage = true, rightGroup, leftGroup, ...inputProps } = props;
1549
+ const { name, id = name, type, label, labelHint, className, labelClass, labelWrapperClass, containerClass, error, showErrorMessage = true, rightGroup, leftGroup, prefix, suffix, clearable = false, onClear, showCount = false, helperText, size = 'md', value, defaultValue, onChange, maxLength, ...inputProps } = props;
1550
+ const innerRef = useRef(null);
1551
+ useImperativeHandle(ref, () => innerRef.current);
1552
+ const [uncontrolledValue, setUncontrolledValue] = useState(String(defaultValue !== null && defaultValue !== void 0 ? defaultValue : ''));
1553
+ const isControlled = value !== undefined;
1554
+ const currentVal = isControlled ? String(value !== null && value !== void 0 ? value : '') : uncontrolledValue;
1555
+ const charLength = currentVal.length;
1556
+ const handleInputChange = (e) => {
1557
+ if (!isControlled) {
1558
+ setUncontrolledValue(e.target.value);
1559
+ }
1560
+ onChange === null || onChange === void 0 ? void 0 : onChange(e);
1561
+ };
1562
+ const handleClear = () => {
1563
+ if (innerRef.current) {
1564
+ innerRef.current.value = '';
1565
+ const event = new Event('input', {
1566
+ bubbles: true,
1567
+ });
1568
+ Object.defineProperty(event, 'target', { writable: true, value: { value: '' } });
1569
+ if (!isControlled) {
1570
+ setUncontrolledValue('');
1571
+ }
1572
+ onChange === null || onChange === void 0 ? void 0 : onChange(event);
1573
+ }
1574
+ onClear === null || onClear === void 0 ? void 0 : onClear();
1575
+ };
1106
1576
  const hasError = Boolean(error);
1107
- const hasRightGroup = rightGroup !== null && rightGroup !== undefined;
1577
+ const currentSize = sizeClasses$4[size] || sizeClasses$4.md;
1578
+ const showClearButton = clearable && charLength > 0 && !inputProps.disabled;
1579
+ const actualRightGroup = rightGroup !== null && rightGroup !== void 0 ? rightGroup : (showClearButton ? (jsx("button", { type: "button", onClick: handleClear, className: "p-1 rounded-full text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors cursor-pointer", title: "Clear text", tabIndex: -1, children: jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: currentSize.icon, children: jsx("path", { d: "M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" }) }) })) : null);
1580
+ const hasRightGroup = actualRightGroup !== null && actualRightGroup !== undefined;
1108
1581
  const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
1109
- return (jsxs("div", { className: buildClassName('group', hasError && 'has-error', containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between mb-2', labelWrapperClass), children: [jsx(Label, { className: labelClass, htmlFor: id, children: label }), jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxs("div", { className: "relative", children: [hasLeftGroup && (jsx("span", { className: "absolute top-0 h-full flex items-center justify-center w-10", children: leftGroup })), jsx("input", { ref: ref, autoComplete: "off", ...inputProps, name: name, id: id, type: type, className: buildClassName(buildInputClass(className, { disabled: inputProps.disabled }), hasRightGroup ? 'pe-10' : '', hasLeftGroup ? 'ps-10' : '') }), hasRightGroup && (jsx("span", { className: "absolute end-0.75 top-0 h-full flex items-center justify-center w-10", children: rightGroup }))] }), showErrorMessage && error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
1582
+ return (jsxs("div", { className: buildClassName('group', hasError && 'has-error', containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between mb-1.5', labelWrapperClass), children: [jsx(Label, { className: labelClass, htmlFor: id, children: label }), labelHint && jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxs("div", { className: "flex rounded-lg shadow-2xs", children: [prefix && (jsx("span", { className: "inline-flex items-center px-3 rounded-l-lg border border-r-0 border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 text-gray-500 dark:text-gray-400 text-xs font-medium shrink-0", children: prefix })), jsxs("div", { className: "relative flex-1 min-w-0", children: [hasLeftGroup && (jsx("span", { className: buildClassName('absolute top-0 h-full flex items-center justify-center text-gray-400 dark:text-gray-500', currentSize.group), children: leftGroup })), jsx("input", { ref: innerRef, autoComplete: "off", ...inputProps, value: value, defaultValue: defaultValue, onChange: handleInputChange, maxLength: maxLength, name: name, id: id, type: type, className: buildClassName(buildInputClass(className, {
1583
+ disabled: inputProps.disabled,
1584
+ hasError,
1585
+ size,
1586
+ }), prefix ? '!rounded-l-none' : '', suffix ? '!rounded-r-none' : '', hasRightGroup ? (size === 'lg' ? 'pe-12' : size === 'sm' ? 'pe-8' : 'pe-10') : '', hasLeftGroup ? (size === 'lg' ? 'ps-12' : size === 'sm' ? 'ps-8' : 'ps-10') : '') }), hasRightGroup && (jsx("span", { className: buildClassName('absolute end-0 top-0 h-full flex items-center justify-center text-gray-400 dark:text-gray-500', currentSize.group), children: actualRightGroup }))] }), suffix && (jsx("span", { className: "inline-flex items-center px-3 rounded-r-lg border border-l-0 border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 text-gray-500 dark:text-gray-400 text-xs font-medium shrink-0", children: suffix }))] }), (showErrorMessage && error) || helperText || showCount ? (jsxs("div", { className: "mt-1.5 flex items-center justify-between gap-2 text-xs", children: [jsx("div", { className: "min-w-0 flex-1", children: showErrorMessage && error ? (jsx(TextContent, { error: true, small: true, children: error })) : helperText ? (jsx(TextContent, { muted: true, small: true, children: helperText })) : null }), showCount && (jsxs("span", { className: buildClassName('text-[11px] font-medium shrink-0 ml-auto', maxLength && charLength >= maxLength
1587
+ ? 'text-red-500 font-semibold'
1588
+ : 'text-gray-400 dark:text-gray-500'), children: [charLength, maxLength ? ` / ${maxLength}` : ''] }))] })) : null] }));
1110
1589
  });
1111
1590
  Input.displayName = 'Input';
1112
1591
 
1113
1592
  const PASSWORD_TYPE = 'password';
1114
1593
  const PASSWORD_PLACEHOLDER = '••••••••••';
1115
- const PasswordInput = (props) => {
1116
- const { ...inputProps } = props;
1594
+ function evaluatePasswordStrength(val) {
1595
+ if (!val)
1596
+ return { score: 0, label: 'Weak', colorClass: 'bg-gray-200 dark:bg-gray-700', percent: 0 };
1597
+ let score = 0;
1598
+ if (val.length >= 8)
1599
+ score += 1;
1600
+ if (val.length >= 12)
1601
+ score += 1;
1602
+ if (/[0-9]/.test(val))
1603
+ score += 1;
1604
+ if (/[a-z]/.test(val) && /[A-Z]/.test(val))
1605
+ score += 1;
1606
+ if (/[^A-Za-z0-9]/.test(val))
1607
+ score += 1;
1608
+ if (score <= 1) {
1609
+ return { score: 1, label: 'Weak', colorClass: 'bg-red-500', percent: 25 };
1610
+ }
1611
+ if (score === 2) {
1612
+ return { score: 2, label: 'Fair', colorClass: 'bg-amber-500', percent: 50 };
1613
+ }
1614
+ if (score === 3) {
1615
+ return { score: 3, label: 'Good', colorClass: 'bg-blue-500', percent: 75 };
1616
+ }
1617
+ return { score: 4, label: 'Strong', colorClass: 'bg-emerald-500', percent: 100 };
1618
+ }
1619
+ const PasswordInput = forwardRef((props, ref) => {
1620
+ const { showStrength = false, placeholder = PASSWORD_PLACEHOLDER, onChange, value, defaultValue, ...inputProps } = props;
1117
1621
  const [showPassword, setShowPassword] = useState(false);
1118
- return (jsx(Input, { ...inputProps, type: showPassword ? 'text' : PASSWORD_TYPE, placeholder: PASSWORD_PLACEHOLDER, rightGroup: jsx(Button, { theme: "secondary", variant: "plain", size: "xs", className: "px-1 mr-1", type: "button", onClick: () => setShowPassword(() => !showPassword), tabIndex: -1, children: showPassword ? (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: "1.5", stroke: "currentColor", className: "size-4", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3.98 8.223A10.477 10.477 0 0 0 1.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.451 10.451 0 0 1 12 4.5c4.756 0 8.773 3.162 10.065 7.498a10.522 10.522 0 0 1-4.293 5.774M6.228 6.228 3 3m3.228 3.228 3.65 3.65m7.894 7.894L21 21m-3.228-3.228-3.65-3.65m0 0a3 3 0 1 0-4.243-4.243m4.242 4.242L9.88 9.88" }) })) : (jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: "1.5", stroke: "currentColor", className: "size-4", children: [jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" }), jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" })] })) }) }));
1119
- };
1622
+ const [uncontrolledValue, setUncontrolledValue] = useState(String(defaultValue !== null && defaultValue !== void 0 ? defaultValue : ''));
1623
+ const isControlled = value !== undefined;
1624
+ const currentVal = isControlled ? String(value !== null && value !== void 0 ? value : '') : uncontrolledValue;
1625
+ const handleInputChange = (e) => {
1626
+ if (!isControlled) {
1627
+ setUncontrolledValue(e.target.value);
1628
+ }
1629
+ onChange === null || onChange === void 0 ? void 0 : onChange(e);
1630
+ };
1631
+ const strength = evaluatePasswordStrength(currentVal);
1632
+ return (jsxs("div", { className: "space-y-1.5", children: [jsx(Input, { ref: ref, ...inputProps, value: value, defaultValue: defaultValue, onChange: handleInputChange, type: showPassword ? 'text' : PASSWORD_TYPE, placeholder: placeholder, rightGroup: jsx(Button, { theme: "secondary", variant: "plain", size: "xs", className: "px-1.5 mr-0.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200", type: "button", onClick: () => setShowPassword((prev) => !prev), tabIndex: -1, "aria-label": showPassword ? 'Hide password' : 'Show password', children: showPassword ? (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: "1.5", stroke: "currentColor", className: "size-4", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3.98 8.223A10.477 10.477 0 0 0 1.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.451 10.451 0 0 1 12 4.5c4.756 0 8.773 3.162 10.065 7.498a10.522 10.522 0 0 1-4.293 5.774M6.228 6.228 3 3m3.228 3.228 3.65 3.65m7.894 7.894L21 21m-3.228-3.228-3.65-3.65m0 0a3 3 0 1 0-4.243-4.243m4.242 4.242L9.88 9.88" }) })) : (jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: "1.5", stroke: "currentColor", className: "size-4", children: [jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" }), jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" })] })) }) }), showStrength && currentVal.length > 0 && (jsxs("div", { className: "pt-0.5 space-y-1", children: [jsxs("div", { className: "flex items-center justify-between text-[11px] font-medium text-gray-500 dark:text-gray-400", children: [jsx("span", { children: "Password Strength" }), jsx("span", { className: "font-semibold text-gray-700 dark:text-gray-200", children: strength.label })] }), jsx("div", { className: "h-1.5 w-full bg-gray-200 dark:bg-gray-800 rounded-full overflow-hidden flex gap-1 p-0.5", children: jsx("div", { className: `h-full transition-all duration-300 rounded-full ${strength.colorClass}`, style: { width: `${strength.percent}%` } }) })] }))] }));
1633
+ });
1634
+ PasswordInput.displayName = 'PasswordInput';
1120
1635
 
1636
+ const SIZE_MAP = {
1637
+ sm: 'size-4',
1638
+ md: 'size-4.5',
1639
+ lg: 'size-5.5',
1640
+ };
1641
+ const THEME_MAP = {
1642
+ primary: 'checked:border-[var(--ui-primary)] checked:hover:border-[var(--ui-primary)] focus:ring-[var(--ui-primary-ring)]',
1643
+ success: 'checked:border-emerald-600 checked:hover:border-emerald-600 focus:ring-emerald-500',
1644
+ danger: 'checked:border-[var(--ui-danger)] checked:hover:border-[var(--ui-danger)] focus:ring-[var(--ui-danger-ring)]',
1645
+ };
1121
1646
  const Radio = forwardRef((props, ref) => {
1122
- var _a;
1123
- const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '',
1124
- // RadioButton-only props removed from rest
1125
- ...rest } = props;
1647
+ const { id, name, label, description, size = 'md', theme = 'primary', labelClass = '', descriptionClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '', ...rest } = props;
1126
1648
  const generatedId = useId();
1127
- const radioId = (_a = id !== null && id !== void 0 ? id : name) !== null && _a !== void 0 ? _a : generatedId;
1128
- return (jsxs("div", { className: buildClassName(wrapperClass), children: [jsxs("div", { className: buildClassName('flex items-center gap-2', containerClass), children: [jsx("div", { className: "grid place-items-center", children: jsx("input", { ref: ref, ...rest, id: radioId, name: name, type: "radio", disabled: disabled, "aria-checked": rest.checked, className: buildClassName('peer transition-all col-start-1 row-start-1 appearance-none shrink-0 size-4.5 rounded-full', 'border focus:ring-2 focus:ring-offset-2', 'checked:border-5 checked:bg-white', 'focus:ring-[var(--ui-focus-ring)] dark:focus:ring-offset-gray-800', disabled
1649
+ const radioId = id !== null && id !== void 0 ? id : (name && rest.value ? `${name}-${rest.value}` : generatedId);
1650
+ return (jsxs("div", { className: buildClassName(wrapperClass), children: [jsxs("div", { className: buildClassName('flex items-start gap-2.5', containerClass), children: [jsx("div", { className: "grid place-items-center shrink-0 pt-0.5", children: jsx("input", { ref: ref, ...rest, id: radioId, name: name, type: "radio", disabled: disabled, "aria-checked": rest.checked, className: buildClassName('peer transition-all col-start-1 row-start-1 appearance-none shrink-0 rounded-full', 'border focus:ring-2 focus:ring-offset-2', 'checked:border-5 checked:bg-white', 'dark:focus:ring-offset-gray-800', SIZE_MAP[size], THEME_MAP[theme], disabled
1129
1651
  ? 'border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-gray-800 pointer-events-none'
1130
- : buildClassName('bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600 hover:border-gray-400 dark:hover:border-gray-500', 'checked:border-[var(--ui-primary)] checked:hover:border-[var(--ui-primary)]'), className) }) }), label && (jsx(Label, { htmlFor: radioId, className: buildClassName(disabled
1131
- ? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
1132
- : 'cursor-pointer text-gray-600 dark:text-gray-300', labelClass), children: label }))] }), error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
1652
+ : 'bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600 hover:border-gray-400 dark:hover:border-gray-500 cursor-pointer', className) }) }), (label || description) && (jsxs("div", { className: "flex flex-col gap-0.5 min-w-0", children: [label && (jsx(Label, { htmlFor: radioId, className: buildClassName(disabled
1653
+ ? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
1654
+ : 'cursor-pointer text-gray-700 dark:text-gray-200 font-medium', labelClass), children: label })), description && (jsx("p", { className: buildClassName('text-xs leading-normal', disabled
1655
+ ? 'text-gray-300 dark:text-gray-600'
1656
+ : 'text-gray-500 dark:text-gray-400', descriptionClass), children: description }))] }))] }), error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
1133
1657
  });
1658
+ Radio.displayName = 'Radio';
1134
1659
 
1660
+ const COLUMN_GRID_MAP = {
1661
+ 1: 'grid-cols-1',
1662
+ 2: 'grid-cols-1 md:grid-cols-2',
1663
+ 3: 'grid-cols-1 md:grid-cols-3',
1664
+ 4: 'grid-cols-1 md:grid-cols-4',
1665
+ };
1135
1666
  const RadioGroup = (props) => {
1136
- const { name, label, labelClass, labelWrapperClass, labelHint, containerClass, options, value, row, error, showErrorMessage, onChange, } = props;
1667
+ const { name, variant = 'default', size = 'md', theme = 'primary', label, labelClass, labelWrapperClass, labelHint, containerClass, options, value, columns, disabled = false, row, error, showErrorMessage = true, onChange, } = props;
1137
1668
  const items = useMemo(() => {
1138
1669
  var _a;
1139
- return (_a = options === null || options === void 0 ? void 0 : options.map((option) => typeof option === 'string' ? { label: option, value: option } : option)) !== null && _a !== void 0 ? _a : [];
1670
+ return (_a = options === null || options === void 0 ? void 0 : options.map((option) => typeof option === 'string'
1671
+ ? {
1672
+ label: option,
1673
+ value: option,
1674
+ description: undefined,
1675
+ icon: undefined,
1676
+ disabled: undefined,
1677
+ }
1678
+ : option)) !== null && _a !== void 0 ? _a : [];
1140
1679
  }, [options]);
1141
- return (jsxs("div", { className: buildClassName(containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between mb-4', labelWrapperClass), children: [jsx(Label, { className: labelClass, children: label }), labelHint] })), jsx("div", { className: buildClassName('flex gap-4', row ? 'flex-row items-center' : 'flex-col items-start'), children: items.map((item) => (jsx(Radio, { name: name, id: `${name}-${item.value}`, label: item.label, value: item.value, checked: value === item.value, onChange: onChange }, item.value))) }), showErrorMessage && error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
1680
+ const isCards = variant === 'cards';
1681
+ return (jsxs("div", { className: buildClassName(containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between mb-3', labelWrapperClass), children: [jsx(Label, { className: labelClass, children: label }), labelHint] })), jsx("div", { className: buildClassName(columns
1682
+ ? buildClassName('grid gap-3', COLUMN_GRID_MAP[columns])
1683
+ : row || isCards
1684
+ ? 'flex flex-wrap gap-3'
1685
+ : 'flex flex-col gap-3'), children: items.map((item) => {
1686
+ const isSelected = value === item.value;
1687
+ const isItemDisabled = disabled || Boolean(item.disabled);
1688
+ if (isCards) {
1689
+ return (jsxs("label", { htmlFor: `${name}-${item.value}`, className: buildClassName('relative flex items-start gap-3 p-4 rounded-xl border transition-all cursor-pointer select-none min-w-[200px] flex-1', isSelected
1690
+ ? 'border-[var(--ui-primary)] bg-blue-50/40 dark:bg-blue-950/30 ring-2 ring-[var(--ui-primary-ring)]/30'
1691
+ : 'border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 hover:border-gray-300 dark:hover:border-gray-600', isItemDisabled &&
1692
+ 'opacity-50 cursor-not-allowed pointer-events-none bg-gray-50 dark:bg-gray-800/50'), children: [jsx(Radio, { name: name, id: `${name}-${item.value}`, value: item.value, "aria-label": item.label, size: size, theme: theme, checked: isSelected, disabled: isItemDisabled, onChange: onChange, containerClass: "pt-0.5" }), jsxs("div", { className: "flex-1 min-w-0", children: [jsxs("div", { className: "flex items-center gap-2", children: [item.icon && (jsx("span", { className: "shrink-0 text-gray-500 dark:text-gray-400", children: item.icon })), jsx("span", { className: "text-sm font-semibold text-gray-900 dark:text-white truncate", children: item.label })] }), item.description && (jsx("p", { className: "mt-1 text-xs text-gray-500 dark:text-gray-400 font-normal leading-relaxed", children: item.description }))] })] }, item.value));
1693
+ }
1694
+ return (jsx(Radio, { name: name, id: `${name}-${item.value}`, label: item.label, description: item.description, value: item.value, size: size, theme: theme, checked: isSelected, disabled: isItemDisabled, onChange: onChange }, item.value));
1695
+ }) }), showErrorMessage && error && (jsx("div", { className: "mt-1.5", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
1142
1696
  };
1697
+ RadioGroup.displayName = 'RadioGroup';
1143
1698
 
1144
- /**
1145
- * GPU-accelerated Radio Switch with perfect highlight alignment.
1146
- * Supports wrapping, icons, descriptions, and dark mode.
1147
- */
1148
- const RadioSwitch = ({ label, items, selected, onChange, wrapperClass, switchClass, contentClass, descriptionClass, }) => {
1699
+ const SIZE_CONTAINER_CLASS$1 = {
1700
+ sm: 'p-1.5 gap-1 rounded-lg text-xs',
1701
+ md: 'p-2 gap-1.5 rounded-xl text-sm',
1702
+ lg: 'p-2 gap-2 rounded-2xl text-base',
1703
+ };
1704
+ const SIZE_ITEM_CLASS = {
1705
+ sm: 'px-4 py-2 text-xs rounded-md min-w-[64px]',
1706
+ md: 'px-5 py-2.5 text-sm rounded-lg min-w-[76px]',
1707
+ lg: 'px-7 py-3 text-base rounded-xl min-w-[92px]',
1708
+ };
1709
+ const SIZE_ITEM_DESC_CLASS = {
1710
+ sm: 'px-4 py-2.5 text-xs rounded-md min-w-[80px]',
1711
+ md: 'px-5 py-3 text-sm rounded-lg min-w-[96px]',
1712
+ lg: 'px-7 py-3.5 text-base rounded-xl min-w-[112px]',
1713
+ };
1714
+ const THEME_SWITCH_CLASS = {
1715
+ primary: 'bg-[var(--ui-primary)] text-white shadow-xs',
1716
+ secondary: 'bg-white dark:bg-gray-700 text-gray-900 dark:text-white shadow-xs',
1717
+ dark: 'bg-gray-900 dark:bg-black text-white shadow-xs',
1718
+ };
1719
+ const RadioSwitch = ({ label, items, selected, size = 'md', theme = 'primary', disabled = false, onChange, wrapperClass, switchClass, contentClass, descriptionClass, }) => {
1149
1720
  const containerRef = useRef(null);
1150
- const [style, setStyle] = useState({ left: 0, top: 0, width: 0, height: 0 });
1721
+ const [style, setStyle] = useState({ left: 0, top: 0, width: 0, height: 0, ready: false });
1151
1722
  const getLabel = (i) => (typeof i === 'string' ? i : i.label);
1152
1723
  const getValue = (i) => (typeof i === 'string' ? i : i.value);
1153
1724
  const getDescription = (i) => { var _a; return typeof i === 'string' ? '' : ((_a = i.description) !== null && _a !== void 0 ? _a : ''); };
1154
- /** ---------------------------------------------
1155
- * Update highlight when selected changes
1156
- * ----------------------------------------------*/
1157
- useEffect(() => {
1725
+ const isItemDisabled = (i) => disabled || (typeof i === 'string' ? false : Boolean(i.disabled));
1726
+ const hasDescription = items.some((i) => typeof i !== 'string' && Boolean(i.description));
1727
+ // Position calculation for GPU sliding highlight
1728
+ useLayoutEffect(() => {
1158
1729
  if (!containerRef.current)
1159
1730
  return;
1160
- const buttons = Array.from(containerRef.current.querySelectorAll('[data-radio-item]'));
1161
- const el = buttons.find((b) => b.dataset.value === selected);
1162
- if (!el)
1731
+ const updatePosition = () => {
1732
+ if (!containerRef.current)
1733
+ return;
1734
+ const activeEl = containerRef.current.querySelector('[data-selected="true"]');
1735
+ if (!activeEl)
1736
+ return;
1737
+ const parentRect = containerRef.current.getBoundingClientRect();
1738
+ const rect = activeEl.getBoundingClientRect();
1739
+ const left = activeEl.offsetLeft || rect.left - parentRect.left;
1740
+ const top = activeEl.offsetTop || rect.top - parentRect.top;
1741
+ const width = activeEl.offsetWidth || rect.width;
1742
+ const height = activeEl.offsetHeight || rect.height;
1743
+ setStyle({
1744
+ left,
1745
+ top,
1746
+ width,
1747
+ height,
1748
+ ready: true,
1749
+ });
1750
+ };
1751
+ updatePosition();
1752
+ const resizeObserver = new ResizeObserver(updatePosition);
1753
+ resizeObserver.observe(containerRef.current);
1754
+ return () => resizeObserver.disconnect();
1755
+ }, [selected, items, size]);
1756
+ // ARIA Keyboard navigation handler
1757
+ const handleGroupKeyDown = (e) => {
1758
+ const enabledItems = items.filter((it) => !isItemDisabled(it));
1759
+ if (enabledItems.length === 0)
1163
1760
  return;
1164
- const rect = el.getBoundingClientRect();
1165
- const parentRect = containerRef.current.getBoundingClientRect();
1166
- const left = rect.left - parentRect.left;
1167
- const top = rect.top - parentRect.top;
1168
- setStyle({
1169
- left,
1170
- top,
1171
- width: rect.width,
1172
- height: rect.height,
1173
- });
1174
- }, [selected, items]);
1175
- return (jsxs(RadioGroup$1, { value: selected, onChange: onChange, "aria-label": label, ref: containerRef, className: buildClassName('relative flex flex-wrap gap-1', 'transition-colors duration-200 overflow-hidden', wrapperClass), children: [jsx("div", { className: buildClassName('absolute rounded-md bg-blue-600 dark:bg-blue-500', 'transition-transform transition-size duration-300 ease-[cubic-bezier(.25,.8,.25,1)]', switchClass), style: {
1761
+ const currentVal = selected;
1762
+ const currentEnabledIndex = enabledItems.findIndex((it) => getValue(it) === currentVal);
1763
+ let nextItem = null;
1764
+ if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
1765
+ e.preventDefault();
1766
+ const nextIdx = (currentEnabledIndex + 1) % enabledItems.length;
1767
+ nextItem = enabledItems[nextIdx];
1768
+ }
1769
+ else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
1770
+ e.preventDefault();
1771
+ const prevIdx = (currentEnabledIndex - 1 + enabledItems.length) % enabledItems.length;
1772
+ nextItem = enabledItems[prevIdx];
1773
+ }
1774
+ else if (e.key === 'Home') {
1775
+ e.preventDefault();
1776
+ nextItem = enabledItems[0];
1777
+ }
1778
+ else if (e.key === 'End') {
1779
+ e.preventDefault();
1780
+ nextItem = enabledItems[enabledItems.length - 1];
1781
+ }
1782
+ if (nextItem) {
1783
+ onChange(getValue(nextItem));
1784
+ }
1785
+ };
1786
+ return (jsxs("div", { ref: containerRef, role: "radiogroup", tabIndex: 0, "aria-label": label, onKeyDown: handleGroupKeyDown, className: buildClassName('relative inline-grid grid-flow-col auto-cols-fr items-stretch bg-gray-100 dark:bg-gray-800/80 border border-gray-200/80 dark:border-gray-700/80 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary-ring)]', SIZE_CONTAINER_CLASS$1[size], disabled && 'opacity-60 pointer-events-none', wrapperClass), children: [jsx("div", { className: buildClassName('absolute transition-all duration-200 ease-[cubic-bezier(.25,.8,.25,1)] pointer-events-none', THEME_SWITCH_CLASS[theme], size === 'sm' ? 'rounded-md' : 'rounded-lg', style.ready ? 'opacity-100' : 'opacity-0', switchClass), style: {
1176
1787
  transform: `translate(${style.left}px, ${style.top}px)`,
1177
1788
  width: style.width,
1178
1789
  height: style.height,
1179
- } }), items.map((item) => (jsx(Radio$1, { value: getValue(item), "data-radio-item": true, "data-value": getValue(item), className: buildClassName('relative z-10 cursor-pointer select-none group', 'px-4 py-2 rounded-md transition-colors', 'text-gray-700 dark:text-gray-300 data-checked:text-white'), children: jsxs("div", { className: buildClassName('flex flex-col items-center justify-center gap-1', contentClass), children: [jsx(BodyText, { className: buildClassName('font-semibold', selected === getValue(item) && 'text-white'), children: getLabel(item) }), getDescription(item) && (jsx(BodyText, { className: buildClassName('text-xs opacity-80', descriptionClass, selected === getValue(item) && 'text-white'), children: getDescription(item) }))] }) }, getValue(item))))] }));
1790
+ } }), items.map((item) => {
1791
+ const itemVal = getValue(item);
1792
+ const isSelected = selected === itemVal;
1793
+ const disabledState = isItemDisabled(item);
1794
+ return (jsx("button", { type: "button", role: "radio", "aria-checked": isSelected, "aria-disabled": disabledState, tabIndex: isSelected && !disabledState ? 0 : -1, disabled: disabledState, "data-radio-item": true, "data-value": itemVal, "data-selected": isSelected ? 'true' : undefined, onClick: () => {
1795
+ if (!disabledState) {
1796
+ onChange(itemVal);
1797
+ }
1798
+ }, className: buildClassName('relative z-10 appearance-none m-0 select-none group transition-colors flex flex-col items-center justify-center w-full text-center font-semibold focus:outline-none', hasDescription ? SIZE_ITEM_DESC_CLASS[size] : SIZE_ITEM_CLASS[size], disabledState ? 'cursor-not-allowed opacity-50' : 'cursor-pointer', isSelected
1799
+ ? theme === 'secondary'
1800
+ ? 'text-gray-900 dark:text-white'
1801
+ : 'text-white'
1802
+ : buildClassName('text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white', !disabledState && 'hover:bg-black/[0.04] dark:hover:bg-white/[0.06]')), children: getDescription(item) ? (jsxs("div", { className: buildClassName('flex flex-col items-center justify-center gap-1', contentClass), children: [jsx("span", { className: "font-semibold leading-tight text-center truncate", children: getLabel(item) }), jsx("span", { className: buildClassName('text-[12px] leading-tight opacity-80 font-normal text-center truncate', descriptionClass), children: getDescription(item) })] })) : (jsx("span", { className: buildClassName('truncate leading-tight text-center', contentClass), children: getLabel(item) })) }, itemVal));
1803
+ })] }));
1180
1804
  };
1805
+ RadioSwitch.displayName = 'RadioSwitch';
1181
1806
 
1182
1807
  /**
1183
1808
  * Checks if a value is an array.
@@ -1207,30 +1832,86 @@ function isEmpty(value) {
1207
1832
  return false; // for numbers, booleans, functions, etc.
1208
1833
  }
1209
1834
 
1210
- // export interface RangeInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {
1211
- // label?: string
1212
- // labelClass?: string
1213
- // labelHint?: React.ReactNode
1214
- // containerClass?: string
1215
- // showValue?: boolean
1216
- // valueSuffix?: string
1217
- // error?: string | null
1218
- // showErrorMessage?: boolean
1219
- // }
1835
+ const sizeClasses$3 = {
1836
+ sm: {
1837
+ track: 'h-1.5',
1838
+ thumb: '[&::-webkit-slider-thumb]:h-3.5 [&::-webkit-slider-thumb]:w-3.5 [&::-moz-range-thumb]:h-3.5 [&::-moz-range-thumb]:w-3.5',
1839
+ text: 'text-xs',
1840
+ },
1841
+ md: {
1842
+ track: 'h-2',
1843
+ thumb: '[&::-webkit-slider-thumb]:h-4.5 [&::-webkit-slider-thumb]:w-4.5 [&::-moz-range-thumb]:h-4.5 [&::-moz-range-thumb]:w-4.5',
1844
+ text: 'text-sm',
1845
+ },
1846
+ lg: {
1847
+ track: 'h-3',
1848
+ thumb: '[&::-webkit-slider-thumb]:h-6 [&::-webkit-slider-thumb]:w-6 [&::-moz-range-thumb]:h-6 [&::-moz-range-thumb]:w-6',
1849
+ text: 'text-base',
1850
+ },
1851
+ };
1220
1852
  const RangeInput = forwardRef((props, ref) => {
1221
- const { label, labelClass = '', labelHint = null, containerClass = '', className = '', showValue = true, valueSuffix = '', error = null, showErrorMessage = true, id, min = 0, max = 100, value, ...inputProps } = props;
1853
+ const { label, labelClass = '', labelHint = null, containerClass = '', className = '', size = 'md', showValue = true, valueSuffix = '', showTooltip = false, marks, error = null, helperText, showErrorMessage = true, id, min = 0, max = 100, value, defaultValue, onChange, ...inputProps } = props;
1854
+ const [hovering, setHovering] = useState(false);
1855
+ const [internalValue, setInternalValue] = useState(Number(defaultValue !== null && defaultValue !== void 0 ? defaultValue : min));
1856
+ const isControlled = value !== undefined;
1857
+ const currentVal = Number(isControlled ? value : internalValue);
1858
+ const numMin = Number(min);
1859
+ const numMax = Number(max);
1860
+ const percent = numMax > numMin ? ((currentVal - numMin) / (numMax - numMin)) * 100 : 0;
1861
+ const currentSize = sizeClasses$3[size] || sizeClasses$3.md;
1222
1862
  const hasError = !isEmpty(error);
1223
- return (jsxs("div", { className: buildClassName('group space-y-2', containerClass), children: [label && (jsxs("div", { className: "flex items-center justify-between", children: [jsx(Label, { htmlFor: id, className: labelClass, children: label }), labelHint] })), jsxs("div", { className: "flex items-center gap-3", children: [jsx("input", { ref: ref, type: "range", id: id, min: min, max: max, value: value, ...inputProps, className: buildClassName('w-full h-2 rounded-lg appearance-none cursor-pointer', 'bg-gray-200 dark:bg-gray-700', 'accent-blue-600', // for WebKit
1224
- '[&::-webkit-slider-thumb]:appearance-none', '[&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:w-4', '[&::-webkit-slider-thumb]:rounded-full', '[&::-webkit-slider-thumb]:bg-blue-600', '[&::-moz-range-thumb]:h-4 [&::-moz-range-thumb]:w-4', '[&::-moz-range-thumb]:rounded-full', '[&::-moz-range-thumb]:bg-blue-600', hasError && 'accent-red-600', className) }), showValue && (jsxs("span", { className: "text-sm text-gray-600 dark:text-gray-300 min-w-[3rem] text-right", children: [value, valueSuffix] }))] }), showErrorMessage && hasError && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
1863
+ const handleSliderChange = (e) => {
1864
+ if (!isControlled) {
1865
+ setInternalValue(Number(e.target.value));
1866
+ }
1867
+ onChange === null || onChange === void 0 ? void 0 : onChange(e);
1868
+ };
1869
+ // Parse custom marks if object provided
1870
+ const markEntries = typeof marks === 'object' && marks !== null
1871
+ ? Object.entries(marks).map(([k, v]) => ({ value: Number(k), label: v }))
1872
+ : typeof marks === 'boolean' && marks
1873
+ ? [
1874
+ { value: numMin, label: String(numMin) },
1875
+ { value: (numMin + numMax) / 2, label: String(Math.round((numMin + numMax) / 2)) },
1876
+ { value: numMax, label: String(numMax) },
1877
+ ]
1878
+ : null;
1879
+ return (jsxs("div", { className: buildClassName('group space-y-2', containerClass), children: [label && (jsxs("div", { className: "flex items-center justify-between mb-1", children: [jsx(Label, { htmlFor: id, className: labelClass, children: label }), labelHint] })), jsxs("div", { className: "relative flex items-center gap-3", children: [jsxs("div", { className: "relative flex-1 py-2", children: [showTooltip && hovering && (jsxs("div", { className: "absolute -top-7 transform -translate-x-1/2 px-2 py-0.5 rounded bg-gray-900 dark:bg-gray-100 text-white dark:text-gray-900 text-[11px] font-semibold shadow-md pointer-events-none transition-all z-20", style: { left: `${percent}%` }, children: [currentVal, valueSuffix, jsx("div", { className: "absolute top-full left-1/2 -translate-x-1/2 border-4 border-transparent border-t-gray-900 dark:border-t-gray-100" })] })), jsx("input", { ref: ref, type: "range", id: id, min: min, max: max, value: currentVal, onChange: handleSliderChange, onMouseEnter: () => setHovering(true), onMouseLeave: () => setHovering(false), ...inputProps, style: {
1880
+ background: `linear-gradient(to right, ${hasError ? '#ef4444' : '#2563eb'} 0%, ${hasError ? '#ef4444' : '#2563eb'} ${percent}%, ${document.documentElement.classList.contains('dark') ? '#374151' : '#e5e7eb'} ${percent}%, ${document.documentElement.classList.contains('dark') ? '#374151' : '#e5e7eb'} 100%)`,
1881
+ }, className: buildClassName('w-full rounded-lg appearance-none cursor-pointer transition-all', currentSize.track, currentSize.thumb, '[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full', '[&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:shadow-md', hasError
1882
+ ? '[&::-webkit-slider-thumb]:border-red-600 [&::-moz-range-thumb]:border-red-600'
1883
+ : '[&::-webkit-slider-thumb]:border-blue-600 [&::-moz-range-thumb]:border-blue-600', '[&::-moz-range-thumb]:appearance-none [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:bg-white [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:shadow-md', className) }), markEntries && (jsx("div", { className: "relative w-full h-4 mt-1", children: markEntries.map((m) => {
1884
+ const markPercent = ((m.value - numMin) / (numMax - numMin)) * 100;
1885
+ return (jsxs("div", { className: "absolute top-0 transform -translate-x-1/2 flex flex-col items-center", style: { left: `${markPercent}%` }, children: [jsx("span", { className: "w-1 h-1.5 bg-gray-300 dark:bg-gray-600 rounded-full" }), jsx("span", { className: "text-[10px] font-medium text-gray-400 dark:text-gray-500 mt-0.5", children: m.label })] }, m.value));
1886
+ }) }))] }), showValue && (jsxs("span", { className: buildClassName('font-medium text-gray-700 dark:text-gray-300 min-w-[3rem] text-right shrink-0', currentSize.text), children: [currentVal, valueSuffix] }))] }), (showErrorMessage && hasError) || helperText ? (jsx("div", { className: "mt-1", children: showErrorMessage && hasError ? (jsx(TextContent, { error: true, small: true, children: error })) : helperText ? (jsx(TextContent, { muted: true, small: true, children: helperText })) : null })) : null] }));
1225
1887
  });
1226
-
1227
- const RangeSlider = ({ label, labelHint, labelClass = '', containerClass = '', className = '', min, max, valueMin, valueMax, onChange, valueSuffix = '', error = null, showErrorMessage = true, }) => {
1888
+ RangeInput.displayName = 'RangeInput';
1889
+
1890
+ const sizeClasses$2 = {
1891
+ sm: {
1892
+ track: 'h-1.5',
1893
+ thumb: 'h-4 w-4',
1894
+ thumbPx: 16,
1895
+ text: 'text-xs',
1896
+ },
1897
+ md: {
1898
+ track: 'h-2',
1899
+ thumb: 'h-5 w-5',
1900
+ thumbPx: 20,
1901
+ text: 'text-sm',
1902
+ },
1903
+ lg: {
1904
+ track: 'h-3',
1905
+ thumb: 'h-6 w-6',
1906
+ thumbPx: 24,
1907
+ text: 'text-base',
1908
+ },
1909
+ };
1910
+ const RangeSlider = ({ label, labelHint, labelClass = '', containerClass = '', className = '', size = 'md', min, max, step = 1, valueMin, valueMax, onChange, valueSuffix = '', showTooltips = false, marks, error = null, helperText, showErrorMessage = true, }) => {
1228
1911
  const trackRef = useRef(null);
1229
- // Refs for drag logic
1230
1912
  const activeThumbRef = useRef(null);
1231
- // State for UI styling
1232
1913
  const [activeThumbState, setActiveThumbState] = useState(null);
1233
- // Keep prop values in refs so drag handlers always use latest values (no stale closure)
1914
+ const [hoveringThumb, setHoveringThumb] = useState(null);
1234
1915
  const valueMinRef = useRef(valueMin);
1235
1916
  const valueMaxRef = useRef(valueMax);
1236
1917
  const onChangeRef = useRef(onChange);
@@ -1238,20 +1919,28 @@ const RangeSlider = ({ label, labelHint, labelClass = '', containerClass = '', c
1238
1919
  valueMaxRef.current = valueMax;
1239
1920
  onChangeRef.current = onChange;
1240
1921
  const hasError = !isEmpty(error);
1241
- const percent = (v) => ((v - min) / (max - min)) * 100;
1242
- // Stable handler references registered on window — these never change identity
1243
- // so removeEventListener always finds the right listener to remove.
1922
+ const currentSize = sizeClasses$2[size] || sizeClasses$2.md;
1923
+ const clampValue = (val) => Math.min(Math.max(val, min), max);
1924
+ const snapToStep = (val) => {
1925
+ const clamped = clampValue(val);
1926
+ const steps = Math.round((clamped - min) / step);
1927
+ return Math.min(Math.max(min + steps * step, min), max);
1928
+ };
1929
+ const percent = (v) => ((clampValue(v) - min) / (max - min)) * 100;
1244
1930
  const stablePointerMoveRef = useRef((e) => {
1245
1931
  if (!trackRef.current || !activeThumbRef.current)
1246
1932
  return;
1247
1933
  const rect = trackRef.current.getBoundingClientRect();
1248
1934
  const x = Math.min(Math.max(e.clientX - rect.left, 0), rect.width);
1249
1935
  const rawVal = min + (x / rect.width) * (max - min);
1936
+ const snappedVal = snapToStep(rawVal);
1250
1937
  if (activeThumbRef.current === 'min') {
1251
- onChangeRef.current(Math.min(rawVal, valueMaxRef.current - 1), valueMaxRef.current);
1938
+ const nextMin = Math.min(snappedVal, valueMaxRef.current - step);
1939
+ onChangeRef.current(nextMin, valueMaxRef.current);
1252
1940
  }
1253
1941
  else {
1254
- onChangeRef.current(valueMinRef.current, Math.max(rawVal, valueMinRef.current + 1));
1942
+ const nextMax = Math.max(snappedVal, valueMinRef.current + step);
1943
+ onChangeRef.current(valueMinRef.current, nextMax);
1255
1944
  }
1256
1945
  });
1257
1946
  const stablePointerUpRef = useRef(() => {
@@ -1260,7 +1949,6 @@ const RangeSlider = ({ label, labelHint, labelClass = '', containerClass = '', c
1260
1949
  window.removeEventListener('pointermove', stablePointerMoveRef.current);
1261
1950
  window.removeEventListener('pointerup', stablePointerUpRef.current);
1262
1951
  });
1263
- // Remove listeners on unmount in case a drag is in progress
1264
1952
  useEffect(() => {
1265
1953
  const move = stablePointerMoveRef.current;
1266
1954
  const up = stablePointerUpRef.current;
@@ -1269,59 +1957,91 @@ const RangeSlider = ({ label, labelHint, labelClass = '', containerClass = '', c
1269
1957
  window.removeEventListener('pointerup', up);
1270
1958
  };
1271
1959
  }, []);
1272
- // ---------------------------------------------
1273
- // Pointer Move (drag)
1274
- // ---------------------------------------------
1275
1960
  const startDrag = (thumb) => (_e) => {
1276
1961
  activeThumbRef.current = thumb;
1277
1962
  setActiveThumbState(thumb);
1278
1963
  window.addEventListener('pointermove', stablePointerMoveRef.current);
1279
1964
  window.addEventListener('pointerup', stablePointerUpRef.current);
1280
1965
  };
1281
- // ---------------------------------------------
1282
- // Click Track to move closest thumb
1283
- // ---------------------------------------------
1284
1966
  const handleTrackClick = (e) => {
1285
1967
  if (!trackRef.current)
1286
1968
  return;
1287
1969
  const rect = trackRef.current.getBoundingClientRect();
1288
1970
  const x = Math.min(Math.max(e.clientX - rect.left, 0), rect.width);
1289
1971
  const rawVal = min + (x / rect.width) * (max - min);
1972
+ const snappedVal = snapToStep(rawVal);
1290
1973
  const pxMin = (percent(valueMin) / 100) * rect.width;
1291
1974
  const pxMax = (percent(valueMax) / 100) * rect.width;
1292
- // If click is left of min thumb → move min thumb
1293
1975
  if (x <= pxMin) {
1294
- onChange(rawVal, valueMax);
1976
+ onChange(Math.min(snappedVal, valueMax - step), valueMax);
1295
1977
  return;
1296
1978
  }
1297
- // If click is right of max thumb → move max thumb
1298
1979
  if (x >= pxMax) {
1299
- onChange(valueMin, rawVal);
1980
+ onChange(valueMin, Math.max(snappedVal, valueMin + step));
1300
1981
  return;
1301
1982
  }
1302
- // Else click is between → move closest thumb
1303
- const distMin = Math.abs(rawVal - valueMin);
1304
- const distMax = Math.abs(rawVal - valueMax);
1983
+ const distMin = Math.abs(snappedVal - valueMin);
1984
+ const distMax = Math.abs(snappedVal - valueMax);
1305
1985
  if (distMin < distMax) {
1306
- onChange(rawVal, valueMax);
1986
+ onChange(Math.min(snappedVal, valueMax - step), valueMax);
1307
1987
  }
1308
1988
  else {
1309
- onChange(valueMin, rawVal);
1989
+ onChange(valueMin, Math.max(snappedVal, valueMin + step));
1310
1990
  }
1311
1991
  };
1312
- return (jsxs("div", { className: buildClassName('group space-y-2', hasError && 'has-error', containerClass), children: [label && (jsxs("div", { className: "flex items-center justify-between mb-1", children: [jsx(Label, { className: labelClass, children: label }), labelHint] })), jsxs("div", { ref: trackRef, onMouseDown: handleTrackClick, className: "relative h-8 select-none w-full", "data-testid": "range-track", children: [jsx("div", { className: "absolute top-1/2 left-0 right-0 h-2 rounded-full bg-gray-200 dark:bg-gray-700 -translate-y-1/2" }), jsx("div", { className: "absolute top-1/2 h-2 bg-blue-600 dark:bg-blue-500 -translate-y-1/2 rounded-full \n transition-all duration-150 ease-out", style: {
1992
+ // Keyboard accessibility handler for thumbs
1993
+ const handleKeyDown = (thumb) => (e) => {
1994
+ let delta = 0;
1995
+ if (e.key === 'ArrowLeft' || e.key === 'ArrowDown')
1996
+ delta = -step;
1997
+ else if (e.key === 'ArrowRight' || e.key === 'ArrowUp')
1998
+ delta = step;
1999
+ else if (e.key === 'Home') {
2000
+ if (thumb === 'min')
2001
+ onChange(min, valueMax);
2002
+ else
2003
+ onChange(valueMin, valueMin + step);
2004
+ return;
2005
+ }
2006
+ else if (e.key === 'End') {
2007
+ if (thumb === 'min')
2008
+ onChange(valueMax - step, valueMax);
2009
+ else
2010
+ onChange(valueMin, max);
2011
+ return;
2012
+ }
2013
+ if (delta !== 0) {
2014
+ e.preventDefault();
2015
+ if (thumb === 'min') {
2016
+ const next = snapToStep(valueMin + delta);
2017
+ onChange(Math.min(next, valueMax - step), valueMax);
2018
+ }
2019
+ else {
2020
+ const next = snapToStep(valueMax + delta);
2021
+ onChange(valueMin, Math.max(next, valueMin + step));
2022
+ }
2023
+ }
2024
+ };
2025
+ const markEntries = marks
2026
+ ? Object.entries(marks).map(([k, v]) => ({ value: Number(k), label: v }))
2027
+ : null;
2028
+ return (jsxs("div", { className: buildClassName('group space-y-2', hasError && 'has-error', containerClass), children: [label && (jsxs("div", { className: "flex items-center justify-between mb-1", children: [jsx(Label, { className: labelClass, children: label }), labelHint] })), jsxs("div", { ref: trackRef, onMouseDown: handleTrackClick, className: "relative h-8 select-none w-full flex items-center cursor-pointer", "data-testid": "range-track", children: [jsx("div", { className: buildClassName('absolute top-1/2 left-0 right-0 rounded-full bg-gray-200 dark:bg-gray-700 -translate-y-1/2', currentSize.track) }), jsx("div", { className: buildClassName('absolute top-1/2 -translate-y-1/2 rounded-full transition-all duration-150 ease-out', hasError ? 'bg-red-500' : 'bg-blue-600 dark:bg-blue-500', currentSize.track), style: {
1313
2029
  left: `${percent(valueMin)}%`,
1314
2030
  width: `${percent(valueMax) - percent(valueMin)}%`,
1315
- } }), jsx("div", { onPointerDown: startDrag('min'), className: buildClassName('absolute top-1/2 -translate-y-1/2 h-5 w-5 rounded-full', 'bg-white border-2 border-blue-600 shadow-md cursor-pointer touch-none', 'transition-all duration-150 ease-out', // animation
1316
- activeThumbState === 'min' && 'ring-4 ring-blue-200', className), style: {
1317
- left: `calc(${percent(valueMin)}% - 10px)`,
2031
+ } }), jsx("div", { role: "slider", tabIndex: 0, "aria-label": "Minimum value", "aria-valuenow": valueMin, "aria-valuemin": min, "aria-valuemax": valueMax - step, onPointerDown: startDrag('min'), onMouseEnter: () => setHoveringThumb('min'), onMouseLeave: () => setHoveringThumb(null), onKeyDown: handleKeyDown('min'), className: buildClassName('absolute top-1/2 -translate-y-1/2 rounded-full focus:outline-none', 'bg-white border-2 shadow-md cursor-pointer touch-none', hasError ? 'border-red-600' : 'border-blue-600', 'transition-all duration-150 ease-out', currentSize.thumb, (activeThumbState === 'min' || hoveringThumb === 'min') &&
2032
+ (hasError ? 'ring-4 ring-red-200' : 'ring-4 ring-blue-200'), className), style: {
2033
+ left: `calc(${percent(valueMin)}% - ${currentSize.thumbPx / 2}px)`,
1318
2034
  zIndex: activeThumbState === 'min' ? 50 : 30,
1319
- }, "data-testid": "thumb-min" }), jsx("div", { onPointerDown: startDrag('max'), className: buildClassName('absolute top-1/2 -translate-y-1/2 h-5 w-5 rounded-full', 'bg-white border-2 border-blue-600 shadow-md cursor-pointer touch-none', 'transition-all duration-150 ease-out', // animation
1320
- activeThumbState === 'max' && 'ring-4 ring-blue-200', className), style: {
1321
- left: `calc(${percent(valueMax)}% - 10px)`,
2035
+ }, "data-testid": "thumb-min", children: showTooltips && (activeThumbState === 'min' || hoveringThumb === 'min') && (jsxs("div", { className: "absolute -top-7 left-1/2 -translate-x-1/2 px-2 py-0.5 rounded bg-gray-900 dark:bg-gray-100 text-white dark:text-gray-900 text-[11px] font-semibold shadow-md pointer-events-none z-50 whitespace-nowrap", children: [Math.round(valueMin), valueSuffix, jsx("div", { className: "absolute top-full left-1/2 -translate-x-1/2 border-4 border-transparent border-t-gray-900 dark:border-t-gray-100" })] })) }), jsx("div", { role: "slider", tabIndex: 0, "aria-label": "Maximum value", "aria-valuenow": valueMax, "aria-valuemin": valueMin + step, "aria-valuemax": max, onPointerDown: startDrag('max'), onMouseEnter: () => setHoveringThumb('max'), onMouseLeave: () => setHoveringThumb(null), onKeyDown: handleKeyDown('max'), className: buildClassName('absolute top-1/2 -translate-y-1/2 rounded-full focus:outline-none', 'bg-white border-2 shadow-md cursor-pointer touch-none', hasError ? 'border-red-600' : 'border-blue-600', 'transition-all duration-150 ease-out', currentSize.thumb, (activeThumbState === 'max' || hoveringThumb === 'max') &&
2036
+ (hasError ? 'ring-4 ring-red-200' : 'ring-4 ring-blue-200'), className), style: {
2037
+ left: `calc(${percent(valueMax)}% - ${currentSize.thumbPx / 2}px)`,
1322
2038
  zIndex: activeThumbState === 'max' ? 50 : 40,
1323
- }, "data-testid": "thumb-max" })] }), jsxs("div", { className: "flex justify-between text-sm text-gray-700 dark:text-gray-300", children: [jsxs("span", { children: [Math.round(valueMin), valueSuffix] }), jsxs("span", { children: [Math.round(valueMax), valueSuffix] })] }), showErrorMessage && hasError && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
2039
+ }, "data-testid": "thumb-max", children: showTooltips && (activeThumbState === 'max' || hoveringThumb === 'max') && (jsxs("div", { className: "absolute -top-7 left-1/2 -translate-x-1/2 px-2 py-0.5 rounded bg-gray-900 dark:bg-gray-100 text-white dark:text-gray-900 text-[11px] font-semibold shadow-md pointer-events-none z-50 whitespace-nowrap", children: [Math.round(valueMax), valueSuffix, jsx("div", { className: "absolute top-full left-1/2 -translate-x-1/2 border-4 border-transparent border-t-gray-900 dark:border-t-gray-100" })] })) })] }), markEntries && (jsx("div", { className: "relative w-full h-4 mt-1", children: markEntries.map((m) => {
2040
+ const markPercent = percent(m.value);
2041
+ return (jsxs("div", { className: "absolute top-0 transform -translate-x-1/2 flex flex-col items-center", style: { left: `${markPercent}%` }, children: [jsx("span", { className: "w-1 h-1.5 bg-gray-300 dark:bg-gray-600 rounded-full" }), jsx("span", { className: "text-[10px] font-medium text-gray-400 dark:text-gray-500 mt-0.5", children: m.label })] }, m.value));
2042
+ }) })), jsxs("div", { className: buildClassName('flex justify-between font-medium text-gray-700 dark:text-gray-300', currentSize.text), children: [jsxs("span", { children: [Math.round(valueMin), valueSuffix] }), jsxs("span", { children: [Math.round(valueMax), valueSuffix] })] }), (showErrorMessage && hasError) || helperText ? (jsx("div", { className: "mt-1", children: showErrorMessage && hasError ? (jsx(TextContent, { error: true, small: true, children: error })) : helperText ? (jsx(TextContent, { muted: true, small: true, children: helperText })) : null })) : null] }));
1324
2043
  };
2044
+ RangeSlider.displayName = 'RangeSlider';
1325
2045
 
1326
2046
  const useDebounce = (value, delay) => {
1327
2047
  const [debouncedValue, setDebouncedValue] = useState(value);
@@ -1389,28 +2109,69 @@ var SvgCirclePlus = function SvgCirclePlus(props) {
1389
2109
  })));
1390
2110
  };
1391
2111
 
1392
- const constructInputClass = (disabled) => buildInputClass('', { disabled });
2112
+ const sizeClasses$1 = {
2113
+ sm: {
2114
+ input: 'px-2.5 py-1 text-xs leading-5 min-h-[30px]',
2115
+ group: 'w-8 text-xs',
2116
+ icon: 'size-3.5',
2117
+ badgeSize: 'sm',
2118
+ },
2119
+ md: {
2120
+ input: 'px-3.5 py-2 text-sm leading-6 min-h-[38px]',
2121
+ group: 'w-10 text-sm',
2122
+ icon: 'size-4',
2123
+ badgeSize: 'md',
2124
+ },
2125
+ lg: {
2126
+ input: 'px-4 py-2.5 text-base leading-7 min-h-[46px]',
2127
+ group: 'w-12 text-base',
2128
+ icon: 'size-5',
2129
+ badgeSize: 'md',
2130
+ },
2131
+ };
1393
2132
  const SelectBox = forwardRef((props, forwardedRef) => {
1394
- const { labelKey = 'label', valueKey = 'value', name, id = name, options, selected, label = null, labelWrapperClass = '', labelClass = '', labelHint = null, disabled = false, containerClass = '', onChange = () => { }, placeholder, dropdownContainerClass = 'z-20', multiple = false, maxSelection, immediate = true, showErrorMessage = true, error = null, async = false, searchable = true, required = false, allowAdd = false, addNewText = 'Create', onAdd = () => { }, allowClear = false, modalDropdown = false, noOptionsText = 'No options available', } = props;
2133
+ const { labelKey = 'label', valueKey = 'value', name, id = name, options = [], groups, selected, size = 'md', label = null, labelWrapperClass = '', labelClass = '', labelHint = null, disabled = false, containerClass = '', onChange = () => { }, placeholder, dropdownContainerClass = 'z-20', multiple = false, maxSelection, showSelectAll = false, immediate = true, showErrorMessage = true, error = null, helperText, leftGroup, renderOption, async = false, searchable = true, required = false, allowAdd = false, addNewText = 'Create', onAdd = () => { }, allowClear = false, modalDropdown = false, noOptionsText = 'No options available', } = props;
1395
2134
  const hasError = Boolean(error);
1396
2135
  const [query, setQuery] = useState('');
1397
2136
  const debouncedQuery = useDebounce(query, 500);
1398
2137
  const [optionsLoading, setOptionsLoading] = useState(false);
1399
- const shouldShowTypeToSearch = async && !query && !optionsLoading && options.length === 0;
1400
- const inputClass = useMemo(() => constructInputClass(disabled), [disabled]);
1401
- const chevronIconClass = buildClassName('size-4', 'stroke-gray-500 group-data-[hover]:stroke-gray-800', 'dark:stroke-gray-300 dark:group-data-[hover]:stroke-gray-100', disabled && 'dark:stroke-gray-600 stroke-gray-400');
2138
+ // Normalize options from options prop or groups prop
2139
+ const allOptions = useMemo(() => {
2140
+ if (groups === null || groups === void 0 ? void 0 : groups.length) {
2141
+ return groups.flatMap((g) => g.options);
2142
+ }
2143
+ return options;
2144
+ }, [groups, options]);
2145
+ const shouldShowTypeToSearch = async && !query && !optionsLoading && allOptions.length === 0;
2146
+ const currentSize = sizeClasses$1[size] || sizeClasses$1.md;
2147
+ const baseInputClass = useMemo(() => buildInputClass('', { disabled, hasError, size }), [disabled, hasError, size]);
2148
+ const chevronIconClass = buildClassName(currentSize.icon, 'stroke-gray-500 group-data-[hover]:stroke-gray-800', 'dark:stroke-gray-300 dark:group-data-[hover]:stroke-gray-100', disabled && 'dark:stroke-gray-600 stroke-gray-400');
1402
2149
  const inputRef = useRef(null);
1403
2150
  const containerRef = useRef(null);
1404
- // Expose the input element to parent
1405
2151
  useImperativeHandle(forwardedRef, () => inputRef.current, [inputRef]);
1406
2152
  const filteredOptions = useMemo(() => {
1407
2153
  if (query === '' || async)
1408
- return options;
1409
- return options.filter((opt) => {
1410
- const label = typeof opt === 'string' ? opt : opt[labelKey];
1411
- return label === null || label === void 0 ? void 0 : label.toLowerCase().includes(query.toLowerCase());
2154
+ return allOptions;
2155
+ return allOptions.filter((opt) => {
2156
+ const lbl = typeof opt === 'string' ? opt : opt[labelKey];
2157
+ return lbl === null || lbl === void 0 ? void 0 : lbl.toLowerCase().includes(query.toLowerCase());
1412
2158
  });
1413
- }, [options, query, async, labelKey]);
2159
+ }, [allOptions, query, async, labelKey]);
2160
+ const filteredGroups = useMemo(() => {
2161
+ if (!(groups === null || groups === void 0 ? void 0 : groups.length))
2162
+ return null;
2163
+ if (query === '' || async)
2164
+ return groups;
2165
+ return groups
2166
+ .map((g) => ({
2167
+ ...g,
2168
+ options: g.options.filter((opt) => {
2169
+ const lbl = typeof opt === 'string' ? opt : opt[labelKey];
2170
+ return lbl === null || lbl === void 0 ? void 0 : lbl.toLowerCase().includes(query.toLowerCase());
2171
+ }),
2172
+ }))
2173
+ .filter((g) => g.options.length > 0);
2174
+ }, [groups, query, async, labelKey]);
1414
2175
  const getDisplayValue = useCallback((opt) => {
1415
2176
  return typeof opt === 'string' ? opt : opt[labelKey];
1416
2177
  }, [labelKey]);
@@ -1426,69 +2187,48 @@ const SelectBox = forwardRef((props, forwardedRef) => {
1426
2187
  onChange(next);
1427
2188
  (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
1428
2189
  };
1429
- // A strongly-typed internal handler (what you expect callers to receive)
1430
2190
  const handleOnChangeInternal = useCallback((value) => {
1431
- // normal empty value
1432
2191
  if (value === null) {
1433
2192
  onChange(multiple ? [] : null);
1434
2193
  setQuery('');
1435
2194
  return;
1436
2195
  }
1437
- // enforce maxSelection
1438
2196
  if (multiple && Array.isArray(value) && maxSelection && value.length > maxSelection) {
1439
- // ignore the change
1440
2197
  return;
1441
2198
  }
1442
2199
  onChange(value);
1443
2200
  setQuery('');
1444
2201
  }, [onChange, multiple, maxSelection]);
1445
- /**
1446
- * Headless UI typings are overly broad (they include null[] and EnsureArray<T> etc).
1447
- * We therefore pass a wrapper to the Combobox with a cast (only at the call site),
1448
- * while keeping our internal logic strongly typed.
1449
- */
1450
2202
  const comboboxOnChangeWrapper = useCallback((raw) => {
1451
- // normalize actual runtime shapes Headless UI uses: T | T[] | null
1452
2203
  if (raw === null || raw === undefined) {
1453
2204
  handleOnChangeInternal(null);
1454
2205
  return;
1455
2206
  }
1456
2207
  if (Array.isArray(raw)) {
1457
- // sometimes union typing suggests arrays of nulls — guard it
1458
2208
  const allNulls = raw.length > 0 && raw.every((v) => v === null);
1459
2209
  if (allNulls) {
1460
2210
  handleOnChangeInternal([]);
1461
2211
  return;
1462
2212
  }
1463
- // cast array to T[]
1464
2213
  handleOnChangeInternal(raw);
1465
2214
  return;
1466
2215
  }
1467
- // single value
1468
2216
  handleOnChangeInternal(raw);
1469
2217
  }, [handleOnChangeInternal]);
1470
- // support create (allowAdd) as a real ComboboxOption
1471
2218
  const handleAddAndSelect = useCallback(async (valueToAdd) => {
1472
2219
  try {
1473
2220
  const result = await onAdd(valueToAdd);
1474
- // if the parent returned a new option (string or object), select it
1475
2221
  if (result) {
1476
- // If parent returned raw string or object, call onChange with that
1477
2222
  handleOnChangeInternal(result);
1478
2223
  }
1479
2224
  else {
1480
- // if parent didn't return anything, select the raw string value
1481
2225
  handleOnChangeInternal(valueToAdd);
1482
2226
  }
1483
2227
  }
1484
2228
  catch (err) {
1485
- // eslint-disable-next-line no-console
1486
2229
  console.error('onAdd handler error', err);
1487
2230
  }
1488
2231
  }, [onAdd, handleOnChangeInternal]);
1489
- // Kept in a ref so `handleSearch` stays referentially stable even when the
1490
- // caller passes a non-memoized `onSearch` — otherwise the effect below would
1491
- // re-fire (and re-search) on every render instead of only on query changes.
1492
2232
  const onSearchRef = useRef(props.onSearch);
1493
2233
  useEffect(() => {
1494
2234
  onSearchRef.current = props.onSearch;
@@ -1510,16 +2250,232 @@ const SelectBox = forwardRef((props, forwardedRef) => {
1510
2250
  if (async && debouncedQuery)
1511
2251
  handleSearch(debouncedQuery);
1512
2252
  }, [debouncedQuery, async, handleSearch]);
1513
- return (jsxs("div", { className: buildClassName('group space-y-2', hasError && 'has-error', containerClass), ref: containerRef, children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsx(Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxs(Combobox, { multiple: multiple, immediate: immediate, name: name, value: selected, onChange: comboboxOnChangeWrapper, onClose: () => setQuery(''), disabled: disabled, children: [multiple ? (jsxs("div", { className: buildClassName(inputClass, 'flex flex-wrap gap-2 peer relative'), ref: forwardedRef, children: [isArray(selected) &&
2253
+ const isAllSelected = useMemo(() => {
2254
+ if (!multiple || !Array.isArray(selected) || !allOptions.length)
2255
+ return false;
2256
+ return allOptions.length === selected.length;
2257
+ }, [multiple, selected, allOptions]);
2258
+ const handleToggleSelectAll = () => {
2259
+ if (isAllSelected) {
2260
+ onChange([]);
2261
+ }
2262
+ else {
2263
+ onChange(allOptions);
2264
+ }
2265
+ };
2266
+ const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
2267
+ return (jsxs("div", { className: buildClassName('group space-y-2', hasError && 'has-error', containerClass), ref: containerRef, children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsx(Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), labelHint && jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxs(Combobox, { multiple: multiple, immediate: immediate, name: name, value: selected, onChange: comboboxOnChangeWrapper, onClose: () => setQuery(''), disabled: disabled, children: [multiple ? (jsxs("div", { className: buildClassName(baseInputClass, 'flex flex-wrap gap-2 peer relative', hasLeftGroup ? (size === 'lg' ? 'ps-12' : size === 'sm' ? 'ps-8' : 'ps-10') : ''), ref: forwardedRef, children: [hasLeftGroup && (jsx("span", { className: buildClassName('absolute left-0 top-0 h-full flex items-center justify-center text-gray-400 dark:text-gray-500', currentSize.group), children: leftGroup })), isArray(selected) &&
1514
2268
  (selected === null || selected === void 0 ? void 0 : selected.length) > 0 &&
1515
- selected.map((option, idx) => (jsx(Badge, { theme: "secondary", removable: !disabled, onRemove: () => removeSelection(idx), children: getDisplayValue(option) }, `${getValue(option)}-${idx}`))), jsx(ComboboxInput, { className: "outline-none flex-1 dark:placeholder-gray-400 placeholder-gray-400", "aria-label": name, value: query, autoComplete: "off", placeholder: (selected === null || selected === void 0 ? void 0 : selected.length) ? '' : placeholder, onChange: (e) => setQuery(e.target.value), id: id, ref: inputRef, onKeyDown: (e) => {
2269
+ selected.map((option, idx) => (jsx(Badge, { theme: "secondary", size: currentSize.badgeSize, removable: !disabled, onRemove: () => removeSelection(idx), children: getDisplayValue(option) }, `${getValue(option)}-${idx}`))), jsx(ComboboxInput, { className: "outline-none flex-1 dark:placeholder-gray-400 placeholder-gray-400 bg-transparent", "aria-label": name, value: query, autoComplete: "off", placeholder: (selected === null || selected === void 0 ? void 0 : selected.length) ? '' : placeholder, onChange: (e) => setQuery(e.target.value), id: id, ref: inputRef, onKeyDown: (e) => {
1516
2270
  if (multiple && e.key === 'Backspace' && !query) {
1517
2271
  if (Array.isArray(selected) && selected.length > 0) {
1518
2272
  removeSelection(selected.length - 1);
1519
2273
  }
1520
2274
  }
1521
- }, "data-testid": "combobox-input" }), jsx(ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5", "data-testid": "combobox-button", children: jsx(SvgChevronDown, { className: chevronIconClass }) })] })) : (jsxs("div", { className: "relative", ref: forwardedRef, children: [jsx(ComboboxInput, { "aria-label": name, displayValue: (option) => (option ? getDisplayValue(option) : null), autoComplete: "off", placeholder: placeholder, onChange: (event) => setQuery(event.target.value), className: inputClass, disabled: disabled, id: id, readOnly: !searchable, ref: inputRef, "data-testid": "combobox-input" }), allowClear && !isEmpty(selected) && (jsx(Button, { size: "xs", iconOnly: true, "aria-label": "Clear selection", theme: "secondary", variant: "plain", className: "group absolute inset-y-0 right-8 h-6 top-2", onClick: () => handleOnChangeInternal(multiple ? [] : null), disabled: disabled, "data-testid": "combobox-clear-button", noOutlineOnFocus: true, children: jsx(SvgX, { className: "size-4 stroke-gray-500 dark:stroke-gray-300" }) })), jsx(ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5", "data-testid": "combobox-button", children: jsx(SvgChevronDown, { className: chevronIconClass }) })] })), jsx(ComboboxOptions, { anchor: { to: 'bottom', gap: 5 }, as: "div", modal: modalDropdown, className: buildClassName('w-[var(--input-width)] rounded-lg border border-gray-200 dark:border-white/5 bg-white dark:bg-gray-800 p-1 [--anchor-gap:var(--spacing-1)]', '[--anchor-max-height:20rem]', 'origin-top border transition duration-200 ease-[cubic-bezier(0.4, 0.0, 0.2, 1)] data-[closed]:translate-y-[-15%] data-[closed]:opacity-0', dropdownContainerClass), transition: true, "data-testid": "combobox-options", children: optionsLoading ? (jsx(BodyText, { "data-testid": "options-loading", className: "flex items-center justify-center py-2 px-3 select-none", children: jsx(Loader, { size: "sm" }) })) : shouldShowTypeToSearch ? (jsx(BodyText, { className: "flex items-center justify-between py-2 px-3 select-none", children: "Type to search..." })) : (jsxs("div", { children: [filteredOptions.map((option) => (jsx(ComboboxOption, { disabled: option.disabled, value: option, className: "group rounded-lg py-1.5 px-3 select-none data-[focus]:bg-black/3 dark:data-[focus]:bg-white/10 data-disabled:opacity-50 cursor-pointer data-disabled:pointer-events-none", children: jsxs("div", { className: "flex items-center justify-between gap-2 dark:text-white group-data-[selected]:text-blue-600", children: [jsx("span", { className: "text-sm/6", children: getDisplayValue(option) }), jsx(SvgCheck, { className: "invisible size-5 dark:stroke-gray-300 group-data-[selected]:visible" })] }) }, getValue(option)))), filteredOptions.length === 0 && (jsx("div", { "data-testid": "no-result-found", className: "flex items-center gap-2 py-1.5 px-3 select-none text-gray-500 dark:text-gray-400", children: query ? (jsxs(Fragment, { children: ["No results found for ", jsx("span", { className: "font-semibold", children: query })] })) : (noOptionsText) })), allowAdd && (jsx(ComboboxOption, { value: query, className: "group rounded-lg py-1.5 px-3 cursor-pointer text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700", onClick: () => void handleAddAndSelect(query), children: jsxs("div", { className: "flex items-center gap-2", children: [jsx(SvgCirclePlus, { className: "size-4.5" }), jsxs(TextContent, { children: [addNewText, " \"", query, "\""] })] }) }))] })) })] }), showErrorMessage && error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
2275
+ }, "data-testid": "combobox-input" }), jsx(ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5 flex items-center justify-center", "data-testid": "combobox-button", children: jsx(SvgChevronDown, { className: chevronIconClass }) })] })) : (jsxs("div", { className: "relative", ref: forwardedRef, children: [hasLeftGroup && (jsx("span", { className: buildClassName('absolute left-0 top-0 h-full flex items-center justify-center text-gray-400 dark:text-gray-500 z-10', currentSize.group), children: leftGroup })), jsx(ComboboxInput, { "aria-label": name, displayValue: (option) => (option ? getDisplayValue(option) : null), autoComplete: "off", placeholder: placeholder, onChange: (event) => setQuery(event.target.value), className: buildClassName(baseInputClass, hasLeftGroup ? (size === 'lg' ? 'ps-12' : size === 'sm' ? 'ps-8' : 'ps-10') : ''), disabled: disabled, id: id, readOnly: !searchable, ref: inputRef, "data-testid": "combobox-input" }), allowClear && !isEmpty(selected) && (jsx(Button, { size: "xs", iconOnly: true, "aria-label": "Clear selection", theme: "secondary", variant: "plain", className: "group absolute inset-y-0 right-8 h-6 top-2", onClick: () => handleOnChangeInternal(multiple ? [] : null), disabled: disabled, "data-testid": "combobox-clear-button", noOutlineOnFocus: true, children: jsx(SvgX, { className: "size-4 stroke-gray-500 dark:stroke-gray-300" }) })), jsx(ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5 flex items-center justify-center", "data-testid": "combobox-button", children: jsx(SvgChevronDown, { className: chevronIconClass }) })] })), jsxs(ComboboxOptions, { anchor: { to: 'bottom', gap: 5 }, as: "div", modal: modalDropdown, className: buildClassName('w-[var(--input-width)] rounded-lg border border-gray-200 dark:border-white/5 bg-white dark:bg-gray-800 p-1 [--anchor-gap:var(--spacing-1)]', '[--anchor-max-height:20rem]', 'origin-top border transition duration-200 ease-[cubic-bezier(0.4, 0.0, 0.2, 1)] data-[closed]:translate-y-[-15%] data-[closed]:opacity-0', dropdownContainerClass), transition: true, "data-testid": "combobox-options", children: [multiple && showSelectAll && allOptions.length > 0 && (jsxs("div", { className: "pb-1 mb-1 border-b border-gray-200 dark:border-gray-700/80 px-3 pt-1 flex items-center justify-between", children: [jsx("button", { type: "button", onClick: handleToggleSelectAll, className: "text-xs font-semibold text-blue-600 dark:text-blue-400 hover:underline cursor-pointer", children: isAllSelected ? 'Deselect All' : 'Select All' }), jsxs("span", { className: "text-[11px] font-medium text-gray-400", children: [Array.isArray(selected) ? selected.length : 0, " of ", allOptions.length] })] })), optionsLoading ? (jsx(BodyText, { "data-testid": "options-loading", className: "flex items-center justify-center py-2 px-3 select-none", children: jsx(Loader, { size: "sm" }) })) : shouldShowTypeToSearch ? (jsx(BodyText, { className: "flex items-center justify-between py-2 px-3 select-none", children: "Type to search..." })) : (jsxs("div", { children: [filteredGroups
2276
+ ? filteredGroups.map((grp, gIdx) => (jsxs("div", { children: [jsx("div", { className: "text-[11px] font-bold uppercase tracking-wider text-gray-400 dark:text-gray-500 px-3 py-1 select-none", children: grp.group }), grp.options.map((option) => {
2277
+ const optVal = getValue(option);
2278
+ const isOptSelected = multiple
2279
+ ? isArray(selected) && selected.some((s) => getValue(s) === optVal)
2280
+ : selected && getValue(selected) === optVal;
2281
+ return (jsx(ComboboxOption, { disabled: option.disabled, value: option, className: "group rounded-lg py-1.5 px-3 select-none data-[focus]:bg-black/3 dark:data-[focus]:bg-white/10 data-disabled:opacity-50 cursor-pointer data-disabled:pointer-events-none", children: renderOption ? (renderOption(option, Boolean(isOptSelected))) : (jsxs("div", { className: "flex items-center justify-between gap-2 dark:text-white group-data-[selected]:text-blue-600", children: [jsx("span", { className: "text-sm/6", children: getDisplayValue(option) }), jsx(SvgCheck, { className: "invisible size-5 dark:stroke-gray-300 group-data-[selected]:visible" })] })) }, optVal));
2282
+ })] }, gIdx)))
2283
+ : /* Render Standard Flat Options */
2284
+ filteredOptions.map((option) => {
2285
+ const optVal = getValue(option);
2286
+ const isOptSelected = multiple
2287
+ ? isArray(selected) && selected.some((s) => getValue(s) === optVal)
2288
+ : selected && getValue(selected) === optVal;
2289
+ return (jsx(ComboboxOption, { disabled: option.disabled, value: option, className: "group rounded-lg py-1.5 px-3 select-none data-[focus]:bg-black/3 dark:data-[focus]:bg-white/10 data-disabled:opacity-50 cursor-pointer data-disabled:pointer-events-none", children: renderOption ? (renderOption(option, Boolean(isOptSelected))) : (jsxs("div", { className: "flex items-center justify-between gap-2 dark:text-white group-data-[selected]:text-blue-600", children: [jsx("span", { className: "text-sm/6", children: getDisplayValue(option) }), jsx(SvgCheck, { className: "invisible size-5 dark:stroke-gray-300 group-data-[selected]:visible" })] })) }, optVal));
2290
+ }), filteredOptions.length === 0 && !(filteredGroups === null || filteredGroups === void 0 ? void 0 : filteredGroups.length) && (jsx("div", { "data-testid": "no-result-found", className: "flex items-center gap-2 py-1.5 px-3 select-none text-gray-500 dark:text-gray-400", children: query ? (jsxs(Fragment, { children: ["No results found for ", jsx("span", { className: "font-semibold", children: query })] })) : (noOptionsText) })), allowAdd && (jsx(ComboboxOption, { value: query, className: "group rounded-lg py-1.5 px-3 cursor-pointer text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700", onClick: () => void handleAddAndSelect(query), children: jsxs("div", { className: "flex items-center gap-2", children: [jsx(SvgCirclePlus, { className: "size-4.5" }), jsxs(TextContent, { children: [addNewText, " \"", query, "\""] })] }) }))] }))] })] }), showErrorMessage && error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) })), !error && helperText && (jsx("div", { className: "mt-1", children: jsx(TextContent, { muted: true, small: true, children: helperText }) }))] }));
2291
+ });
2292
+ SelectBox.displayName = 'SelectBox';
2293
+
2294
+ const VARIANT_CLASS = {
2295
+ text: 'rounded-md h-4 w-full my-1',
2296
+ circular: 'rounded-full shrink-0',
2297
+ rectangular: 'rounded-none w-full h-24',
2298
+ rounded: 'rounded-xl w-full h-24',
2299
+ };
2300
+ const ANIMATION_CLASS = {
2301
+ pulse: 'animate-pulse bg-gray-200 dark:bg-gray-700',
2302
+ wave: 'bg-gradient-to-r from-gray-200 via-gray-100 to-gray-200 dark:from-gray-800 dark:via-gray-700 dark:to-gray-800 bg-[length:200%_100%] animate-[shimmer_1.5s_infinite]',
2303
+ none: 'bg-gray-200 dark:bg-gray-700',
2304
+ };
2305
+ const Skeleton = forwardRef((props, ref) => {
2306
+ const { as, variant = 'text', animation = 'pulse', width, height, borderRadius, className, style: customStyle, ...restProps } = props;
2307
+ const Component = as || 'div';
2308
+ const inlineStyles = {
2309
+ ...(width !== undefined ? { width: typeof width === 'number' ? `${width}px` : width } : {}),
2310
+ ...(height !== undefined
2311
+ ? { height: typeof height === 'number' ? `${height}px` : height }
2312
+ : {}),
2313
+ ...(borderRadius !== undefined
2314
+ ? { borderRadius: typeof borderRadius === 'number' ? `${borderRadius}px` : borderRadius }
2315
+ : {}),
2316
+ ...customStyle,
2317
+ };
2318
+ return (jsx(Component, { ref: ref, "aria-hidden": "true", style: inlineStyles, className: buildClassName('block select-none pointer-events-none', VARIANT_CLASS[variant], ANIMATION_CLASS[animation], className), "data-testid": "skeleton", ...restProps }));
2319
+ });
2320
+ Skeleton.displayName = 'Skeleton';
2321
+
2322
+ const MinusIcon = ({ className }) => (jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", className: className, "aria-hidden": "true", children: jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" }) }));
2323
+ const PlusIcon = ({ className }) => (jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", className: className, "aria-hidden": "true", children: [jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }), jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })] }));
2324
+ const BUTTON_SIZE_CLASS = {
2325
+ sm: 'size-7',
2326
+ md: 'size-9',
2327
+ lg: 'size-10',
2328
+ };
2329
+ const ICON_SIZE_CLASS = {
2330
+ sm: 'size-3.5',
2331
+ md: 'size-4',
2332
+ lg: 'size-4.5',
2333
+ };
2334
+ const INPUT_TEXT_CLASS = {
2335
+ sm: 'w-12 text-xs py-1',
2336
+ md: 'w-16 text-sm py-1.5',
2337
+ lg: 'w-20 text-base py-2',
2338
+ };
2339
+ const QuantityStepper = forwardRef((props, ref) => {
2340
+ const { value, onChange, min = 0, max = 99999, step = 1, label, size = 'md', prefix, suffix, disabled = false, className, onFocus, ...restProps } = props;
2341
+ const handleDecrement = () => {
2342
+ if (disabled)
2343
+ return;
2344
+ const next = Math.max(min, Number((value - step).toFixed(4)));
2345
+ onChange(next);
2346
+ };
2347
+ const handleIncrement = () => {
2348
+ if (disabled)
2349
+ return;
2350
+ const next = Math.min(max, Number((value + step).toFixed(4)));
2351
+ onChange(next);
2352
+ };
2353
+ const handleInputChange = (e) => {
2354
+ const raw = e.target.value;
2355
+ if (raw === '') {
2356
+ onChange(0);
2357
+ return;
2358
+ }
2359
+ const parsed = parseFloat(raw);
2360
+ if (!isNaN(parsed)) {
2361
+ onChange(Math.max(min, Math.min(max, parsed)));
2362
+ }
2363
+ };
2364
+ const isAtMin = disabled || value <= min;
2365
+ const isAtMax = disabled || value >= max;
2366
+ return (jsxs("div", { className: buildClassName('flex flex-col gap-1 inline-flex', className), children: [label && (jsx("label", { className: "block text-xs font-medium text-gray-700 dark:text-gray-300", children: label })), jsxs("div", { className: buildClassName('inline-flex items-center rounded-lg border border-gray-300 bg-white dark:border-gray-600 dark:bg-gray-800 shadow-xs focus-within:ring-2 focus-within:ring-[var(--ui-primary-ring)] focus-within:border-[var(--ui-primary)] transition-all', disabled ? 'opacity-50 cursor-not-allowed bg-gray-50 dark:bg-gray-900' : ''), "data-testid": "quantity-stepper", children: [jsx("button", { type: "button", disabled: isAtMin, onClick: handleDecrement, tabIndex: -1, "aria-label": "Decrease quantity", className: buildClassName('flex items-center justify-center rounded-l-md text-gray-500 hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200 transition-colors', BUTTON_SIZE_CLASS[size], isAtMin ? 'opacity-30 hover:bg-transparent cursor-not-allowed' : 'cursor-pointer'), "data-testid": "stepper-decrement", children: jsx(MinusIcon, { className: ICON_SIZE_CLASS[size] }) }), prefix && jsx("span", { className: "pl-1 text-gray-400 dark:text-gray-500", children: prefix }), jsx("input", { ref: ref, type: "number", min: min, max: max, step: step, value: value === 0 ? '' : value, placeholder: "0", disabled: disabled, onFocus: (e) => {
2367
+ e.target.select();
2368
+ onFocus === null || onFocus === void 0 ? void 0 : onFocus(e);
2369
+ }, onChange: handleInputChange, className: buildClassName('text-center font-semibold text-gray-900 dark:text-white bg-transparent focus:outline-none [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none', INPUT_TEXT_CLASS[size]), "data-testid": "stepper-input", ...restProps }), suffix && jsx("span", { className: "pr-1 text-xs text-gray-500 dark:text-gray-400", children: suffix }), jsx("button", { type: "button", disabled: isAtMax, onClick: handleIncrement, tabIndex: -1, "aria-label": "Increase quantity", className: buildClassName('flex items-center justify-center rounded-r-md text-gray-500 hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200 transition-colors', BUTTON_SIZE_CLASS[size], isAtMax ? 'opacity-30 hover:bg-transparent cursor-not-allowed' : 'cursor-pointer'), "data-testid": "stepper-increment", children: jsx(PlusIcon, { className: ICON_SIZE_CLASS[size] }) })] })] }));
2370
+ });
2371
+ QuantityStepper.displayName = 'QuantityStepper';
2372
+
2373
+ const SIZE_CONTAINER_CLASS = {
2374
+ sm: 'p-0.5 gap-0.5 rounded-lg text-xs',
2375
+ md: 'p-1 gap-1 rounded-xl text-sm',
2376
+ lg: 'p-1.5 gap-1.5 rounded-xl text-base',
2377
+ };
2378
+ const SIZE_OPTION_CLASS = {
2379
+ sm: 'px-2 py-1 text-xs font-medium rounded-md',
2380
+ md: 'px-3 py-1.5 text-sm font-semibold rounded-lg',
2381
+ lg: 'px-4 py-2 text-base font-semibold rounded-lg',
2382
+ };
2383
+ const SIZE_ICON_CLASS = {
2384
+ sm: 'size-3.5',
2385
+ md: 'size-4',
2386
+ lg: 'size-5',
2387
+ };
2388
+ const SegmentedControl = forwardRef((props, ref) => {
2389
+ const { options, value, onChange, disabled = false, size = 'md', fullWidth = false, className, ...restProps } = props;
2390
+ const handleKeyDown = (e, currentIndex) => {
2391
+ const enabledOptions = options.filter((opt) => !opt.disabled && !disabled);
2392
+ if (enabledOptions.length === 0)
2393
+ return;
2394
+ if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
2395
+ e.preventDefault();
2396
+ const currentPos = enabledOptions.findIndex((opt) => opt.value === options[currentIndex].value);
2397
+ const nextPos = (currentPos + 1) % enabledOptions.length;
2398
+ onChange(enabledOptions[nextPos].value);
2399
+ }
2400
+ else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
2401
+ e.preventDefault();
2402
+ const currentPos = enabledOptions.findIndex((opt) => opt.value === options[currentIndex].value);
2403
+ const prevPos = (currentPos - 1 + enabledOptions.length) % enabledOptions.length;
2404
+ onChange(enabledOptions[prevPos].value);
2405
+ }
2406
+ else if (e.key === 'Home') {
2407
+ e.preventDefault();
2408
+ onChange(enabledOptions[0].value);
2409
+ }
2410
+ else if (e.key === 'End') {
2411
+ e.preventDefault();
2412
+ onChange(enabledOptions[enabledOptions.length - 1].value);
2413
+ }
2414
+ };
2415
+ return (jsx("div", { ref: ref, role: "tablist", "aria-orientation": "horizontal", className: buildClassName('inline-flex items-center border border-gray-200 bg-gray-100/80 dark:border-gray-700 dark:bg-gray-800/80', SIZE_CONTAINER_CLASS[size], fullWidth ? 'w-full flex' : '', disabled ? 'opacity-60 cursor-not-allowed' : '', className), "data-testid": "segmented-control", ...restProps, children: options.map((option, index) => {
2416
+ const isSelected = value === option.value;
2417
+ const isOptionDisabled = disabled || option.disabled;
2418
+ return (jsxs("button", { type: "button", role: "tab", "aria-selected": isSelected, "aria-disabled": isOptionDisabled, tabIndex: isSelected && !isOptionDisabled ? 0 : -1, disabled: isOptionDisabled, title: option.title, onClick: () => {
2419
+ if (!isOptionDisabled) {
2420
+ onChange(option.value);
2421
+ }
2422
+ }, onKeyDown: (e) => handleKeyDown(e, index), className: buildClassName('inline-flex items-center justify-center gap-1.5 transition-all duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary-ring)]', SIZE_OPTION_CLASS[size], fullWidth ? 'flex-1' : '', isOptionDisabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer', isSelected
2423
+ ? 'bg-white text-[var(--ui-primary)] dark:bg-gray-700 dark:text-blue-400 shadow-xs font-semibold'
2424
+ : 'text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100'), "data-testid": `segmented-option-${option.value}`, children: [option.icon && (jsx("span", { className: buildClassName('shrink-0 inline-flex items-center justify-center', SIZE_ICON_CLASS[size]), children: option.icon })), jsx("span", { children: option.label })] }, option.value));
2425
+ }) }));
1522
2426
  });
2427
+ SegmentedControl.displayName = 'SegmentedControl';
2428
+
2429
+ const THEME_PILL_CLASS = {
2430
+ success: {
2431
+ bg: 'bg-emerald-500/10 text-emerald-700 dark:text-emerald-300 border-emerald-500/20 dark:border-emerald-500/30',
2432
+ dot: 'bg-emerald-500',
2433
+ },
2434
+ warning: {
2435
+ bg: 'bg-amber-500/10 text-amber-700 dark:text-amber-300 border-amber-500/20 dark:border-amber-500/30',
2436
+ dot: 'bg-amber-500',
2437
+ },
2438
+ danger: {
2439
+ bg: 'bg-rose-500/10 text-rose-700 dark:text-rose-300 border-rose-500/20 dark:border-rose-500/30',
2440
+ dot: 'bg-rose-500',
2441
+ },
2442
+ info: {
2443
+ bg: 'bg-blue-500/10 text-blue-700 dark:text-blue-300 border-blue-500/20 dark:border-blue-500/30',
2444
+ dot: 'bg-blue-500',
2445
+ },
2446
+ secondary: {
2447
+ bg: 'bg-gray-500/10 text-gray-600 dark:text-gray-400 border-gray-500/20 dark:border-gray-500/30',
2448
+ dot: 'bg-gray-400',
2449
+ },
2450
+ };
2451
+ const SIZE_CLASS = {
2452
+ sm: 'px-2.5 py-0.5 text-xs',
2453
+ md: 'px-3 py-1 text-sm',
2454
+ };
2455
+ const DOT_SIZE_CLASS = {
2456
+ sm: 'size-1.5',
2457
+ md: 'size-2',
2458
+ };
2459
+ const StatusPill = forwardRef((props, ref) => {
2460
+ var _a;
2461
+ const { theme = 'secondary', size = 'sm', dot = true, pulse = false, className, children, ...restProps } = props;
2462
+ const style = (_a = THEME_PILL_CLASS[theme]) !== null && _a !== void 0 ? _a : THEME_PILL_CLASS.secondary;
2463
+ return (jsxs("span", { ref: ref, className: buildClassName('inline-flex items-center gap-1.5 rounded-full border font-semibold whitespace-nowrap transition-colors', SIZE_CLASS[size], style.bg, className), "data-testid": `status-pill-${theme}`, ...restProps, children: [dot && (jsx("span", { className: buildClassName('shrink-0 rounded-full', DOT_SIZE_CLASS[size], style.dot, pulse ? 'animate-pulse' : ''), "data-testid": "status-pill-dot", "aria-hidden": "true" })), jsx("span", { children: children })] }));
2464
+ });
2465
+ StatusPill.displayName = 'StatusPill';
2466
+
2467
+ const ChevronUpIcon = ({ className }) => (jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: className, "aria-hidden": "true", children: jsx("polyline", { points: "18 15 12 9 6 15" }) }));
2468
+ const StickyActionBar = forwardRef((props, ref) => {
2469
+ const { summaryContent, actionsContent, drawerContent, drawerTitle = 'Summary Details', position = 'fixed', containerMaxWidth = 'max-w-5xl', className, ...restProps } = props;
2470
+ const [isDrawerOpen, setIsDrawerOpen] = useState(false);
2471
+ const positionClass = position === 'fixed'
2472
+ ? 'fixed bottom-0 left-0 right-0 z-30'
2473
+ : position === 'sticky'
2474
+ ? 'sticky bottom-0 z-30'
2475
+ : 'relative z-10';
2476
+ return (jsxs(Fragment, { children: [jsx("div", { ref: ref, className: buildClassName(positionClass, 'border-t border-gray-200/80 bg-white/90 p-3.5 backdrop-blur-md shadow-lg dark:border-gray-800 dark:bg-gray-900/90 transition-all', className), "data-testid": "sticky-action-bar", ...restProps, children: jsxs("div", { className: buildClassName('mx-auto flex items-center justify-between gap-3', containerMaxWidth), children: [summaryContent && (jsx("div", { className: "flex items-center gap-2", children: drawerContent ? (jsxs("button", { type: "button", onClick: () => setIsDrawerOpen(true), className: "flex items-center gap-2 text-left transition-opacity hover:opacity-80 cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary-ring)] rounded-lg p-1 -m-1", "aria-label": "Expand summary details", "data-testid": "summary-trigger", children: [jsx("div", { children: summaryContent }), jsx(ChevronUpIcon, { className: "size-4 text-[var(--ui-primary)] dark:text-blue-400 shrink-0" })] })) : (jsx("div", { children: summaryContent })) })), actionsContent && (jsx("div", { className: "flex items-center gap-2 shrink-0 ml-auto", "data-testid": "actions-stack", children: actionsContent }))] }) }), drawerContent && (jsx(Drawer, { isOpen: isDrawerOpen, onClose: () => setIsDrawerOpen(false), align: "bottom", size: "full", title: drawerTitle, showBackButton: true, contentClass: "px-4 pb-6 max-h-[calc(100svh-6rem)] overflow-y-auto", children: drawerContent }))] }));
2477
+ });
2478
+ StickyActionBar.displayName = 'StickyActionBar';
1523
2479
 
1524
2480
  const Tabs = ({ tabs, defaultIndex = 0, selectedIndex, onChange, variant = 'underline', orientation = 'horizontal', stretch = false, className = '', listClass = '', panelsClass = '', }) => {
1525
2481
  const isVertical = orientation === 'vertical';
@@ -1540,7 +2496,7 @@ const Tabs = ({ tabs, defaultIndex = 0, selectedIndex, onChange, variant = 'unde
1540
2496
  }) })] }));
1541
2497
  };
1542
2498
  function buildTabClass({ selected, variant, stretch, isVertical, tabClass, }) {
1543
- const base = buildClassName('inline-flex items-center gap-1.5 text-sm font-medium transition focus-visible:outline-none', 'disabled:opacity-40 disabled:cursor-not-allowed disabled:pointer-events-none', stretch && !isVertical && 'flex-1 justify-center');
2499
+ const base = buildClassName('inline-flex items-center gap-1.5 text-sm font-medium transition focus:outline-none focus-visible:outline-none focus:ring-0 select-none', 'disabled:opacity-40 disabled:cursor-not-allowed disabled:pointer-events-none', stretch && !isVertical && 'flex-1 justify-center');
1544
2500
  let variantClass;
1545
2501
  switch (variant) {
1546
2502
  case 'underline':
@@ -1565,13 +2521,58 @@ function buildTabClass({ selected, variant, stretch, isVertical, tabClass, }) {
1565
2521
  return buildClassName(base, variantClass, tabClass);
1566
2522
  }
1567
2523
 
2524
+ const sizeClasses = {
2525
+ sm: {
2526
+ textarea: 'px-2.5 py-1.5 text-xs leading-5 min-h-[60px]',
2527
+ text: 'text-xs',
2528
+ },
2529
+ md: {
2530
+ textarea: 'px-3.5 py-2 text-sm leading-6 min-h-[80px]',
2531
+ text: 'text-sm',
2532
+ },
2533
+ lg: {
2534
+ textarea: 'px-4 py-2.5 text-base leading-7 min-h-[100px]',
2535
+ text: 'text-base',
2536
+ },
2537
+ };
2538
+ const resizeClasses = {
2539
+ none: 'resize-none',
2540
+ vertical: 'resize-y',
2541
+ horizontal: 'resize-x',
2542
+ both: 'resize',
2543
+ };
1568
2544
  const Textarea = forwardRef((props, ref) => {
1569
- const { name, id = name, label = null, labelHint = null, className = '', labelClass = '', labelWrapperClass = '', containerClass = '', error, showErrorMessage = true, rightGroup = null, leftGroup = null, ...inputProps } = props;
2545
+ const { name, id = name, label = null, labelHint = null, className = '', labelClass = '', labelWrapperClass = '', containerClass = '', error, showErrorMessage = true, rightGroup = null, leftGroup = null, size = 'md', autoSize = false, showCount = false, helperText, resize = 'vertical', value, defaultValue, onChange, maxLength, ...inputProps } = props;
2546
+ const innerRef = useRef(null);
2547
+ useImperativeHandle(ref, () => innerRef.current);
2548
+ const [uncontrolledValue, setUncontrolledValue] = useState(String(defaultValue !== null && defaultValue !== void 0 ? defaultValue : ''));
2549
+ const isControlled = value !== undefined;
2550
+ const currentVal = isControlled ? String(value !== null && value !== void 0 ? value : '') : uncontrolledValue;
2551
+ const charLength = currentVal.length;
2552
+ // Handle Auto-Resize behavior
2553
+ useEffect(() => {
2554
+ if (autoSize && innerRef.current) {
2555
+ innerRef.current.style.height = 'auto';
2556
+ innerRef.current.style.height = `${innerRef.current.scrollHeight}px`;
2557
+ }
2558
+ }, [autoSize, currentVal]);
2559
+ const handleTextareaChange = (e) => {
2560
+ if (!isControlled) {
2561
+ setUncontrolledValue(e.target.value);
2562
+ }
2563
+ onChange === null || onChange === void 0 ? void 0 : onChange(e);
2564
+ };
1570
2565
  const hasError = !isEmpty(error);
1571
2566
  const hasRightGroup = rightGroup != null && rightGroup !== undefined;
1572
2567
  const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
1573
- return (jsxs("div", { className: buildClassName('group', hasError && 'has-error', containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between mb-2', labelWrapperClass), children: [jsx(Label, { className: labelClass, htmlFor: id, children: label }), jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxs("div", { className: "relative", children: [hasLeftGroup && (jsx("span", { className: "absolute top-0 h-full flex items-center justify-center w-10", children: leftGroup })), jsx("textarea", { ref: ref, autoComplete: "off", ...inputProps, name: name, id: id, className: buildClassName('transition-all', 'block w-full rounded-lg bg-white px-3.5 py-2 text-base sm:text-sm/6', 'text-gray-900 outline dark:outline outline-1 -outline-offset-1', 'outline-gray-300 dark:outline-gray-600', 'focus:outline-blue-600 dark:focus:outline-blue-600 focus:outline-2 focus:-outline-offset-2', 'dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-500 placeholder:text-gray-400 dark:text-white', 'invalid:group-[.has-error]:outline-red-500', 'disabled:pointer-events-none disabled:bg-gray-100 disabled:outline-gray-200', 'dark:disabled:bg-gray-800 dark:disabled:text-gray-600 dark:disabled:placeholder-gray-600 dark:disabled:outline-gray-700', hasRightGroup ? 'pe-10' : '', hasLeftGroup ? 'ps-10' : '', className) }), hasRightGroup && (jsx("span", { className: "absolute end-0.75 top-0 h-full flex items-center justify-center", children: rightGroup }))] }), showErrorMessage && error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
2568
+ const currentSize = sizeClasses[size] || sizeClasses.md;
2569
+ return (jsxs("div", { className: buildClassName('group', hasError && 'has-error', containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between mb-1.5', labelWrapperClass), children: [jsx(Label, { className: labelClass, htmlFor: id, children: label }), labelHint && jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxs("div", { className: "relative", children: [hasLeftGroup && (jsx("span", { className: "absolute top-2.5 left-3 flex items-center justify-center text-gray-400 dark:text-gray-500", children: leftGroup })), jsx("textarea", { ref: innerRef, autoComplete: "off", ...inputProps, value: value, defaultValue: defaultValue, onChange: handleTextareaChange, maxLength: maxLength, name: name, id: id, className: buildClassName('transition-all duration-150', 'block w-full rounded-lg bg-white', currentSize.textarea, autoSize ? 'resize-none overflow-hidden' : resizeClasses[resize], 'text-gray-900 outline outline-1 -outline-offset-1', hasError
2570
+ ? '!outline-red-500 !ring-1 !ring-red-500/50 dark:!outline-red-500'
2571
+ : 'outline-gray-300 dark:outline-gray-600', 'focus:outline-[var(--ui-focus-ring)] focus:outline-2 focus:-outline-offset-2', 'dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 placeholder:text-gray-400 dark:text-white', 'disabled:pointer-events-none disabled:bg-gray-100 disabled:outline-gray-200', 'dark:disabled:bg-gray-800 dark:disabled:text-gray-600 dark:disabled:placeholder-gray-600 dark:disabled:outline-gray-700', hasRightGroup ? 'pe-10' : '', hasLeftGroup ? 'ps-10' : '', className) }), hasRightGroup && (jsx("span", { className: "absolute end-3 top-2.5 flex items-center justify-center text-gray-400 dark:text-gray-500", children: rightGroup }))] }), (showErrorMessage && error) || helperText || showCount ? (jsxs("div", { className: "mt-1.5 flex items-center justify-between gap-2 text-xs", children: [jsx("div", { className: "min-w-0 flex-1", children: showErrorMessage && error ? (jsx(TextContent, { error: true, small: true, children: error })) : helperText ? (jsx(TextContent, { muted: true, small: true, children: helperText })) : null }), showCount && (jsxs("span", { className: buildClassName('text-[11px] font-medium shrink-0 ml-auto', maxLength && charLength >= maxLength
2572
+ ? 'text-red-500 font-semibold'
2573
+ : 'text-gray-400 dark:text-gray-500'), children: [charLength, maxLength ? ` / ${maxLength}` : ''] }))] })) : null] }));
1574
2574
  });
2575
+ Textarea.displayName = 'Textarea';
1575
2576
 
1576
2577
  /**
1577
2578
  * Icons for each toast type
@@ -1582,23 +2583,76 @@ const iconMap = {
1582
2583
  info: jsx(SvgInfo, { className: "size-5 text-[var(--ui-info)]" }),
1583
2584
  warning: jsx(SvgTriangleAlert, { className: "size-5 text-[var(--ui-warning)]" }),
1584
2585
  };
2586
+ const filledIconMap = {
2587
+ success: jsx(SvgCircleCheck, { className: "size-5 text-white" }),
2588
+ error: jsx(SvgCircleAlert, { className: "size-5 text-white" }),
2589
+ info: jsx(SvgInfo, { className: "size-5 text-white" }),
2590
+ warning: jsx(SvgTriangleAlert, { className: "size-5 text-white" }),
2591
+ };
1585
2592
  const textColorMap = {
1586
2593
  success: 'text-[var(--ui-success-text)]',
1587
2594
  error: 'text-[var(--ui-danger-text)]',
1588
2595
  info: 'text-[var(--ui-info-text)]',
1589
2596
  warning: 'text-[var(--ui-warning-text)]',
1590
2597
  };
1591
- const borderStyle = {
1592
- success: 'border-t-4 border-[var(--ui-success)]',
1593
- error: 'border-t-4 border-[var(--ui-danger)]',
1594
- info: 'border-t-4 border-[var(--ui-info)]',
1595
- warning: 'border-t-4 border-[var(--ui-warning)]',
2598
+ const progressBarColorMap = {
2599
+ success: 'bg-[var(--ui-success)]',
2600
+ error: 'bg-[var(--ui-danger)]',
2601
+ info: 'bg-[var(--ui-info)]',
2602
+ warning: 'bg-[var(--ui-warning)]',
2603
+ };
2604
+ const variantStyleMap = {
2605
+ accent: {
2606
+ success: 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-success)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-gray-200/80 dark:border-gray-800/80',
2607
+ error: 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-danger)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-gray-200/80 dark:border-gray-800/80',
2608
+ info: 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-info)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-gray-200/80 dark:border-gray-800/80',
2609
+ warning: 'bg-white dark:bg-gray-900 border-l-4 border-[var(--ui-warning)] text-gray-900 dark:text-gray-100 shadow-lg border-y border-r border-gray-200/80 dark:border-gray-800/80',
2610
+ },
2611
+ filled: {
2612
+ success: 'bg-[var(--ui-success)] text-white shadow-lg',
2613
+ error: 'bg-[var(--ui-danger)] text-white shadow-lg',
2614
+ info: 'bg-[var(--ui-info)] text-white shadow-lg',
2615
+ warning: 'bg-[var(--ui-warning)] text-white shadow-lg',
2616
+ },
2617
+ outlined: {
2618
+ success: 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-success)] text-gray-900 dark:text-gray-100 shadow-md',
2619
+ error: 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-danger)] text-gray-900 dark:text-gray-100 shadow-md',
2620
+ info: 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-info)] text-gray-900 dark:text-gray-100 shadow-md',
2621
+ warning: 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-warning)] text-gray-900 dark:text-gray-100 shadow-md',
2622
+ },
2623
+ glass: {
2624
+ success: 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-emerald-500/30 text-gray-900 dark:text-gray-100 shadow-xl',
2625
+ error: 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-rose-500/30 text-gray-900 dark:text-gray-100 shadow-xl',
2626
+ info: 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-blue-500/30 text-gray-900 dark:text-gray-100 shadow-xl',
2627
+ warning: 'backdrop-blur-md bg-white/85 dark:bg-gray-900/85 border border-amber-500/30 text-gray-900 dark:text-gray-100 shadow-xl',
2628
+ },
1596
2629
  };
1597
- const Toast = ({ message, type, onClose, className, id, isTop = true, }) => {
2630
+ const Toast = ({ id, title, message, type, variant = 'accent', action, icon, showProgressBar = true, duration = 4000, isPaused = false, onClose, className, isTop = true, }) => {
2631
+ const [progress, setProgress] = useState(100);
2632
+ useEffect(() => {
2633
+ if (!showProgressBar || !duration || isPaused)
2634
+ return;
2635
+ const intervalTime = 50;
2636
+ const step = (intervalTime / duration) * 100;
2637
+ const timer = setInterval(() => {
2638
+ setProgress((prev) => {
2639
+ if (prev <= 0) {
2640
+ clearInterval(timer);
2641
+ return 0;
2642
+ }
2643
+ return prev - step;
2644
+ });
2645
+ }, intervalTime);
2646
+ return () => clearInterval(timer);
2647
+ }, [showProgressBar, duration, isPaused]);
1598
2648
  const enterFrom = isTop ? 'opacity-0 -translate-y-3 scale-95' : 'opacity-0 translate-y-3 scale-95';
1599
2649
  const leaveTo = isTop ? 'opacity-0 -translate-y-2 scale-95' : 'opacity-0 translate-y-2 scale-95';
1600
- return (jsx(Transition, { appear: true, show: true, enter: "transform transition duration-300", enterFrom: enterFrom, enterTo: "opacity-100 translate-y-0 scale-100", leave: "transition duration-200", leaveFrom: "opacity-100 translate-y-0 scale-100", leaveTo: leaveTo, children: jsxs("div", { role: "status", className: buildClassName('flex items-center gap-3 rounded-lg px-4 py-3 shadow-lg min-w-[260px] max-w-sm', 'bg-white dark:bg-gray-900 dark:border-gray-700', borderStyle[type], className), id: id.toString(), children: [jsx(TextContent, { className: buildClassName(textColorMap[type]), children: iconMap[type] }), jsx(TextContent, { className: "flex-1 text-sm font-medium", children: message }), jsx(Button, { onClick: onClose, "aria-label": "Close toast", className: "opacity-60 hover:opacity-100 transition", iconOnly: true, size: "xs", variant: "plain", theme: "secondary", noOutlineOnFocus: true, children: jsx(SvgX, { className: "size-4" }) })] }) }));
2650
+ const renderedIcon = icon !== null && icon !== void 0 ? icon : (variant === 'filled' ? filledIconMap[type] : iconMap[type]);
2651
+ return (jsx(Transition, { appear: true, show: true, enter: "transform transition duration-300 ease-out", enterFrom: enterFrom, enterTo: "opacity-100 translate-y-0 scale-100", leave: "transition duration-200 ease-in", leaveFrom: "opacity-100 translate-y-0 scale-100", leaveTo: leaveTo, children: jsxs("div", { role: "status", id: id.toString(), className: buildClassName('relative overflow-hidden flex items-start gap-3 rounded-xl px-4 py-3.5 min-w-[280px] max-w-md transition-all', variantStyleMap[variant][type], className), children: [jsx("div", { className: "shrink-0 mt-0.5", children: variant === 'filled' ? (renderedIcon) : (jsx(TextContent, { className: buildClassName(textColorMap[type]), children: renderedIcon })) }), jsxs("div", { className: "flex-1 flex flex-col gap-0.5", children: [title && (jsx("div", { className: buildClassName('font-semibold text-sm leading-snug', variant === 'filled' ? 'text-white' : 'text-gray-900 dark:text-gray-100'), children: title })), jsx("div", { className: buildClassName('text-sm font-medium leading-relaxed', variant === 'filled' ? 'text-white/95' : 'text-gray-700 dark:text-gray-300'), children: message }), action && (jsx("div", { className: "mt-1.5", children: jsx("button", { type: "button", onClick: action.onClick, className: buildClassName('text-xs font-semibold underline underline-offset-2 transition-opacity focus:outline-none', variant === 'filled'
2652
+ ? 'text-white hover:opacity-85'
2653
+ : 'text-[var(--ui-primary)] hover:opacity-80'), children: action.label }) }))] }), jsx(Button, { onClick: onClose, "aria-label": "Close toast", className: buildClassName('shrink-0 mt-0.5 opacity-60 hover:opacity-100 transition', variant === 'filled' && 'text-white hover:text-white'), iconOnly: true, size: "xs", variant: "plain", theme: variant === 'filled' ? 'primary' : 'secondary', noOutlineOnFocus: true, children: jsx(SvgX, { className: "size-4" }) }), showProgressBar && duration > 0 && (jsx("div", { className: "absolute bottom-0 left-0 right-0 h-1 bg-black/10 dark:bg-white/10 overflow-hidden", children: jsx("div", { className: buildClassName('h-full transition-all duration-75 ease-linear', variant === 'filled' ? 'bg-white/70' : progressBarColorMap[type]), style: { width: `${Math.max(0, progress)}%` } }) }))] }) }));
1601
2654
  };
2655
+ Toast.displayName = 'Toast';
1602
2656
 
1603
2657
  const ToastContext = createContext(undefined);
1604
2658
  let toastCounter = 0;
@@ -1608,64 +2662,138 @@ const useToast = () => {
1608
2662
  throw new Error('useToast must be used within ToastProvider');
1609
2663
  return context;
1610
2664
  };
1611
- // Maximum visible toasts at once
1612
- const MAX_TOASTS = 5;
1613
2665
  const placementClass = {
1614
- 'top-right': 'top-10 right-4 items-end',
1615
- 'top-left': 'top-10 left-4 items-start',
1616
- 'top-center': 'top-10 left-1/2 -translate-x-1/2 items-center',
1617
- 'bottom-right': 'bottom-10 right-4 items-end',
1618
- 'bottom-left': 'bottom-10 left-4 items-start',
1619
- 'bottom-center': 'bottom-10 left-1/2 -translate-x-1/2 items-center',
1620
- };
1621
- const ToastProvider = ({ children, placement = 'top-center' }) => {
2666
+ 'top-right': 'top-20 right-6 items-end',
2667
+ 'top-left': 'top-20 left-6 items-start',
2668
+ 'top-center': 'top-20 left-1/2 -translate-x-1/2 items-center',
2669
+ 'bottom-right': 'bottom-6 right-6 items-end',
2670
+ 'bottom-left': 'bottom-6 left-6 items-start',
2671
+ 'bottom-center': 'bottom-6 left-1/2 -translate-x-1/2 items-center',
2672
+ };
2673
+ const ToastProvider = ({ children, placement = 'top-right', maxToasts = 5, className, }) => {
1622
2674
  const [toasts, setToasts] = useState([]);
1623
- const timeoutRefs = useRef(new Map());
2675
+ const [pausedIds, setPausedIds] = useState(new Set());
2676
+ const metaRefs = useRef(new Map());
1624
2677
  const clearTimer = (id) => {
1625
- const timeout = timeoutRefs.current.get(id);
1626
- if (timeout) {
1627
- clearTimeout(timeout);
1628
- timeoutRefs.current.delete(id);
2678
+ const meta = metaRefs.current.get(id);
2679
+ if (meta === null || meta === void 0 ? void 0 : meta.timerId) {
2680
+ clearTimeout(meta.timerId);
2681
+ meta.timerId = null;
1629
2682
  }
1630
2683
  };
1631
2684
  const closeToast = useCallback((id) => {
1632
2685
  clearTimer(id);
2686
+ metaRefs.current.delete(id);
2687
+ setPausedIds((prev) => {
2688
+ const next = new Set(prev);
2689
+ next.delete(id);
2690
+ return next;
2691
+ });
1633
2692
  setToasts((prev) => prev.filter((t) => t.id !== id));
1634
2693
  }, []);
1635
- const scheduleClose = useCallback((toast) => {
1636
- if (!toast.autoClose)
1637
- return;
1638
- clearTimer(toast.id);
1639
- const timeout = setTimeout(() => {
1640
- closeToast(toast.id);
1641
- }, toast.duration);
1642
- timeoutRefs.current.set(toast.id, timeout);
2694
+ const dismissAll = useCallback(() => {
2695
+ metaRefs.current.forEach((meta) => {
2696
+ if (meta.timerId)
2697
+ clearTimeout(meta.timerId);
2698
+ });
2699
+ metaRefs.current.clear();
2700
+ setPausedIds(new Set());
2701
+ setToasts([]);
2702
+ }, []);
2703
+ const startAutoClose = useCallback((toastId, delay) => {
2704
+ clearTimer(toastId);
2705
+ const timerId = setTimeout(() => {
2706
+ closeToast(toastId);
2707
+ }, delay);
2708
+ const meta = metaRefs.current.get(toastId);
2709
+ if (meta) {
2710
+ meta.timerId = timerId;
2711
+ meta.startTime = Date.now();
2712
+ meta.remainingTime = delay;
2713
+ }
2714
+ else {
2715
+ metaRefs.current.set(toastId, {
2716
+ timerId,
2717
+ startTime: Date.now(),
2718
+ remainingTime: delay,
2719
+ });
2720
+ }
1643
2721
  }, [closeToast]);
1644
2722
  useEffect(() => {
1645
- const refs = timeoutRefs.current;
1646
- return () => refs.forEach((id) => clearTimeout(id));
2723
+ const refs = metaRefs.current;
2724
+ return () => refs.forEach((meta) => {
2725
+ if (meta.timerId)
2726
+ clearTimeout(meta.timerId);
2727
+ });
1647
2728
  }, []);
1648
2729
  const showToast = useCallback((message, options) => {
1649
- var _a, _b, _c, _d;
2730
+ var _a, _b, _c, _d, _e, _f;
1650
2731
  const id = ++toastCounter;
1651
2732
  const newToast = {
1652
2733
  id,
2734
+ title: options === null || options === void 0 ? void 0 : options.title,
1653
2735
  message,
1654
2736
  type: (_a = options === null || options === void 0 ? void 0 : options.type) !== null && _a !== void 0 ? _a : 'info',
1655
- duration: (_b = options === null || options === void 0 ? void 0 : options.duration) !== null && _b !== void 0 ? _b : 3000,
1656
- autoClose: (_c = options === null || options === void 0 ? void 0 : options.autoClose) !== null && _c !== void 0 ? _c : true,
1657
- pauseOnHover: (_d = options === null || options === void 0 ? void 0 : options.pauseOnHover) !== null && _d !== void 0 ? _d : true,
2737
+ variant: (_b = options === null || options === void 0 ? void 0 : options.variant) !== null && _b !== void 0 ? _b : 'accent',
2738
+ duration: (_c = options === null || options === void 0 ? void 0 : options.duration) !== null && _c !== void 0 ? _c : 4000,
2739
+ autoClose: (_d = options === null || options === void 0 ? void 0 : options.autoClose) !== null && _d !== void 0 ? _d : true,
2740
+ pauseOnHover: (_e = options === null || options === void 0 ? void 0 : options.pauseOnHover) !== null && _e !== void 0 ? _e : true,
2741
+ showProgressBar: (_f = options === null || options === void 0 ? void 0 : options.showProgressBar) !== null && _f !== void 0 ? _f : (options === null || options === void 0 ? void 0 : options.autoClose) !== false,
2742
+ action: options === null || options === void 0 ? void 0 : options.action,
2743
+ icon: options === null || options === void 0 ? void 0 : options.icon,
1658
2744
  };
1659
2745
  setToasts((prev) => {
1660
2746
  const updated = [...prev, newToast];
1661
- if (updated.length > MAX_TOASTS)
2747
+ if (updated.length > maxToasts)
1662
2748
  updated.shift();
1663
2749
  return updated;
1664
2750
  });
1665
- scheduleClose(newToast);
1666
- }, [scheduleClose]);
1667
- return (jsxs(ToastContext.Provider, { value: { showToast, closeToast }, children: [children, jsx("div", { className: buildClassName('fixed z-50 flex flex-col gap-3 pointer-events-none', placementClass[placement]), children: toasts.map((toast, index) => (jsx("div", { className: "pointer-events-auto", onMouseEnter: () => toast.pauseOnHover && clearTimer(toast.id), onMouseLeave: () => toast.pauseOnHover && scheduleClose(toast), children: jsx(Toast, { ...toast, isTop: placement.startsWith('top'), onClose: () => closeToast(toast.id) }) }, toast.id))) })] }));
1668
- };
2751
+ if (newToast.autoClose) {
2752
+ startAutoClose(id, newToast.duration);
2753
+ }
2754
+ return id;
2755
+ }, [maxToasts, startAutoClose]);
2756
+ // Hover pauses timer and tracks exact remaining milliseconds
2757
+ const handleMouseEnter = (toast) => {
2758
+ if (!toast.pauseOnHover || !toast.autoClose)
2759
+ return;
2760
+ const meta = metaRefs.current.get(toast.id);
2761
+ if (meta) {
2762
+ const elapsed = Date.now() - meta.startTime;
2763
+ meta.remainingTime = Math.max(0, meta.remainingTime - elapsed);
2764
+ clearTimer(toast.id);
2765
+ setPausedIds((prev) => new Set(prev).add(toast.id));
2766
+ }
2767
+ };
2768
+ // Mouse leave resumes timer with exact remaining milliseconds
2769
+ const handleMouseLeave = (toast) => {
2770
+ if (!toast.pauseOnHover || !toast.autoClose)
2771
+ return;
2772
+ setPausedIds((prev) => {
2773
+ const next = new Set(prev);
2774
+ next.delete(toast.id);
2775
+ return next;
2776
+ });
2777
+ const meta = metaRefs.current.get(toast.id);
2778
+ if (meta && meta.remainingTime > 0) {
2779
+ startAutoClose(toast.id, meta.remainingTime);
2780
+ }
2781
+ else if (meta && meta.remainingTime <= 0) {
2782
+ closeToast(toast.id);
2783
+ }
2784
+ };
2785
+ const toastHelpers = useMemo(() => ({
2786
+ show: (message, options) => showToast(message, options),
2787
+ success: (message, options) => showToast(message, { ...options, type: 'success' }),
2788
+ error: (message, options) => showToast(message, { ...options, type: 'error' }),
2789
+ info: (message, options) => showToast(message, { ...options, type: 'info' }),
2790
+ warning: (message, options) => showToast(message, { ...options, type: 'warning' }),
2791
+ dismiss: (id) => closeToast(id),
2792
+ dismissAll: () => dismissAll(),
2793
+ }), [showToast, closeToast, dismissAll]);
2794
+ return (jsxs(ToastContext.Provider, { value: { showToast, closeToast, toast: toastHelpers }, children: [children, jsx("div", { className: buildClassName('fixed z-[9999] flex flex-col gap-3 pointer-events-none', placementClass[placement], className), children: toasts.map((toastItem) => (jsx("div", { className: "pointer-events-auto", onMouseEnter: () => handleMouseEnter(toastItem), onMouseLeave: () => handleMouseLeave(toastItem), children: jsx(Toast, { ...toastItem, isPaused: pausedIds.has(toastItem.id), isTop: placement.startsWith('top'), onClose: () => closeToast(toastItem.id) }) }, toastItem.id))) })] }));
2795
+ };
2796
+ ToastProvider.displayName = 'ToastProvider';
1669
2797
 
1670
2798
  const DENSITY_CELL_CLASS$1 = {
1671
2799
  compact: 'px-2 py-1.5',
@@ -1849,5 +2977,5 @@ const useIsTabletOrMobile = () => {
1849
2977
  return isMobile || isTablet;
1850
2978
  };
1851
2979
 
1852
- export { Accordion, Alert, Badge, Banner, BodyText, Breadcrumb, Button, ButtonGroup, Card, Checkbox, CheckboxGroup, DOTS, DataTable, DetailedInformation, Dot, Drawer, Dropdown, EmptyState, EmptyTableRow, HeadingText, Input, Label, Loader, PASSWORD_PLACEHOLDER, PASSWORD_TYPE, Pagination, PasswordInput, Radio, RadioGroup, RadioSwitch, RangeInput, RangeSlider, SelectBox, SortIconMap, Table, TableBody, TableCell, TableFoot, TableHead, TableHeaderCell, TableRow, Tabs, TextContent, Textarea, Toast, ToastProvider, VirtualizedDataTable, buildClassName, buildInputClass, get, getSortIcon, isArray, isEmpty, nextSortDirection, updateSortQuery, useDebounce, useDebounceFn, useIsMobile, useIsTablet, useIsTabletOrMobile, usePagination, useRowSelection, useToast };
2980
+ export { Accordion, Alert, Badge, Banner, BodyText, Breadcrumb, Button, ButtonGroup, Card, CardContent, CardDescription, CardFooter, CardHeader, CardMedia, CardTitle, Checkbox, CheckboxGroup, DOTS, DataTable, DetailedInformation, Dot, Drawer, Dropdown, EmptyState, EmptyTableRow, HeadingText, Input, Label, Loader, PASSWORD_PLACEHOLDER, PASSWORD_TYPE, Pagination, PasswordInput, QuantityStepper, Radio, RadioGroup, RadioSwitch, RangeInput, RangeSlider, SegmentedControl, SelectBox, Skeleton, SortIconMap, StatusPill, StickyActionBar, Table, TableBody, TableCell, TableFoot, TableHead, TableHeaderCell, TableRow, Tabs, TextContent, Textarea, Toast, ToastProvider, VirtualizedDataTable, buildClassName, buildInputClass, evaluatePasswordStrength, get, getSortIcon, isArray, isEmpty, nextSortDirection, updateSortQuery, useDebounce, useDebounceFn, useIsMobile, useIsTablet, useIsTabletOrMobile, usePagination, useRowSelection, useToast };
1853
2981
  //# sourceMappingURL=react-tailwind.es.js.map