@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
@@ -214,15 +214,46 @@ const loaderSizeMap = {
214
214
  lg: 'size-10',
215
215
  };
216
216
  const Loader = ({ className = '', size = 'md', ...props }) => {
217
- return (jsxRuntime.jsx("svg", { className: buildClassName('text-blue-600 dark:text-white', loaderSizeMap[size], className, 'spinner'), viewBox: "0 0 50 50", ...props, children: jsxRuntime.jsx("circle", { className: "path", cx: "25", cy: "25", r: "20", fill: "none", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "5" }) }));
217
+ const hasColorClass = /\btext-/.test(className);
218
+ return (jsxRuntime.jsx("svg", { className: buildClassName(!hasColorClass && 'text-blue-600 dark:text-white', loaderSizeMap[size], className, 'spinner'), viewBox: "0 0 50 50", ...props, children: jsxRuntime.jsx("circle", { className: "path", cx: "25", cy: "25", r: "20", fill: "none", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "5" }) }));
218
219
  };
219
220
 
221
+ const sizeClassMap$2 = {
222
+ xs: 'text-xs/5',
223
+ sm: 'text-sm/6',
224
+ md: 'text-base/7',
225
+ lg: 'text-lg/8',
226
+ xl: 'text-xl/9',
227
+ };
228
+ const weightClassMap$2 = {
229
+ light: 'font-light',
230
+ normal: 'font-normal',
231
+ medium: 'font-medium',
232
+ semibold: 'font-semibold',
233
+ bold: 'font-bold',
234
+ };
220
235
  const TextContent = React.forwardRef((props, ref) => {
221
- const { xsmall = false, small = false, strong = false, muted = false, error = false, className, ...rest } = props;
222
- return React.createElement('span', {
223
- ...rest,
236
+ 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;
237
+ const textColorClass = error
238
+ ? 'text-[var(--ui-text-danger)]'
239
+ : success
240
+ ? 'text-[var(--ui-success)]'
241
+ : warning
242
+ ? 'text-[var(--ui-warning)]'
243
+ : info
244
+ ? 'text-[var(--ui-info)]'
245
+ : muted
246
+ ? invert
247
+ ? 'text-gray-300 dark:text-gray-600'
248
+ : 'text-[var(--ui-text-muted)]'
249
+ : invert
250
+ ? 'text-white dark:text-black'
251
+ : 'text-[var(--ui-text)]';
252
+ return React.createElement(as, {
224
253
  ref,
225
- 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),
254
+ 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),
255
+ ...rest,
256
+ children,
226
257
  });
227
258
  });
228
259
  TextContent.displayName = 'TextContent';
@@ -232,19 +263,29 @@ TextContent.displayName = 'TextContent';
232
263
  */
233
264
  const THEMED_BTN_CLASS = {
234
265
  primary: {
235
- 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)]',
236
- 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)]',
237
- 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)]',
266
+ 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)]',
267
+ 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)]',
268
+ 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)]',
238
269
  },
239
270
  danger: {
240
- 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)]',
241
- 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)]',
242
- 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)]',
271
+ 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)]',
272
+ 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)]',
273
+ 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)]',
243
274
  },
244
275
  secondary: {
245
- 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',
246
- 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',
247
- 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',
276
+ 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',
277
+ 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',
278
+ 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',
279
+ },
280
+ success: {
281
+ 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',
282
+ 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',
283
+ 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',
284
+ },
285
+ warning: {
286
+ 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',
287
+ 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',
288
+ 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',
248
289
  },
249
290
  };
250
291
  const LOADER_TEXT_CLASS = {
@@ -263,6 +304,16 @@ const LOADER_TEXT_CLASS = {
263
304
  outlined: 'text-gray-700 dark:text-gray-200',
264
305
  plain: 'text-gray-700 dark:text-gray-200',
265
306
  },
307
+ success: {
308
+ default: 'text-white',
309
+ outlined: 'text-emerald-600 dark:text-emerald-400',
310
+ plain: 'text-emerald-600 dark:text-emerald-400',
311
+ },
312
+ warning: {
313
+ default: 'text-white',
314
+ outlined: 'text-amber-600 dark:text-amber-400',
315
+ plain: 'text-amber-600 dark:text-amber-400',
316
+ },
266
317
  };
267
318
  /**
268
319
  * Generate size classes.
@@ -270,13 +321,15 @@ const LOADER_TEXT_CLASS = {
270
321
  const getBtnSizeClasses = (size, iconOnly = false) => {
271
322
  switch (size) {
272
323
  case 'xs':
273
- return iconOnly ? 'p-1 rounded-sm text-xs' : 'px-2 py-1 rounded-sm gap-1 text-xs';
324
+ return iconOnly ? 'h-7 w-7 p-1 rounded-sm text-xs' : 'h-7 px-2.5 rounded-sm gap-1 text-xs';
274
325
  case 'sm':
275
- return iconOnly ? 'p-1.5 rounded text-sm' : 'px-3 py-1.5 rounded gap-1.5 text-sm';
326
+ return iconOnly ? 'h-8 w-8 p-1.5 rounded text-sm' : 'h-8 px-3 rounded gap-1.5 text-sm';
276
327
  case 'md':
277
- return iconOnly ? 'p-2 rounded-md text-sm' : 'px-4 py-2 rounded-md gap-2 text-sm';
328
+ return iconOnly ? 'h-9 w-9 p-2 rounded-md text-sm' : 'h-9 px-4 rounded-md gap-2 text-sm';
278
329
  case 'lg':
279
- return iconOnly ? 'p-2.5 rounded-lg text-base' : 'px-5 py-2.5 rounded-lg gap-2 text-base';
330
+ return iconOnly
331
+ ? 'h-10 w-10 p-2.5 rounded-lg text-base'
332
+ : 'h-10 px-5 rounded-lg gap-2 text-base';
280
333
  }
281
334
  };
282
335
  /**
@@ -284,15 +337,14 @@ const getBtnSizeClasses = (size, iconOnly = false) => {
284
337
  */
285
338
  const BASE_BTN_CLASS = 'font-medium flex items-center justify-center shrink-0 cursor-pointer ' +
286
339
  'disabled:cursor-not-allowed disabled:pointer-events-none disabled:opacity-75 ' +
287
- 'transition duration-200 ease-in-out';
340
+ 'active:scale-[0.98] transition-all duration-150 ease-out focus:outline-none';
288
341
  const Button = React.forwardRef(({ as, type, ...props }, ref) => {
289
- const { className = '', children, theme = 'primary', variant = 'default', size = 'md', loading = false, loadingText = '', rounded = false, disabled, iconOnly = false, noOutlineOnFocus = false, ...restProps } = props;
342
+ 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;
290
343
  const Component = as || 'button';
291
344
  // Accessibility guard for icon-only buttons
292
345
  if (iconOnly && !restProps['aria-label']) {
293
346
  console.warn('[ui] <Button iconOnly> requires aria-label for accessibility.');
294
347
  }
295
- // Disable interaction when loading
296
348
  const resolvedDisabled = disabled || loading;
297
349
  // Clean invalid props when not rendering a real button
298
350
  const { type: _ignoreType, disabled: _ignoreDisabled, ...cleanRest } = restProps;
@@ -302,18 +354,15 @@ const Button = React.forwardRef(({ as, type, ...props }, ref) => {
302
354
  disabled: resolvedDisabled,
303
355
  }
304
356
  : {};
357
+ const classes = React.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]);
305
358
  /**
306
- * Memoized className for perf & stability.
307
- */
308
- const classes = React.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]);
309
- /**
310
- * SPECIAL CASE:
311
- * iconOnly + loading → show spinner only
359
+ * SPECIAL CASE: iconOnly + loading → show spinner only
312
360
  */
313
361
  if (loading && iconOnly) {
314
- return (jsxRuntime.jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: jsxRuntime.jsx(Loader, { "data-testid": "btn-loader-icon", size: "xs" }) }));
362
+ return (jsxRuntime.jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: jsxRuntime.jsx(Loader, { "data-testid": "btn-loader-icon", size: "xs", className: buildClassName(LOADER_TEXT_CLASS[theme][variant]) }) }));
315
363
  }
316
- return (jsxRuntime.jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: loading ? (jsxRuntime.jsxs("span", { className: "flex items-center gap-2", "aria-live": "polite", children: [jsxRuntime.jsx(Loader, { "data-testid": "btn-loader-icon", size: "xs", className: buildClassName(LOADER_TEXT_CLASS[theme][variant]) }), loadingText && (jsxRuntime.jsx(TextContent, { className: buildClassName(LOADER_TEXT_CLASS[theme][variant]), children: loadingText }))] })) : (children) }));
364
+ const loaderIcon = (jsxRuntime.jsx(Loader, { "data-testid": "btn-loader-icon", size: "xs", className: buildClassName(LOADER_TEXT_CLASS[theme][variant]) }));
365
+ return (jsxRuntime.jsx(Component, { ref: ref, className: classes, ...buttonSpecificProps, ...cleanRest, children: loading && !loadingText ? (jsxRuntime.jsxs("span", { className: "flex items-center gap-2", "aria-live": "polite", children: [loaderIcon, children] })) : loading && loadingText ? (jsxRuntime.jsxs("span", { className: "flex items-center gap-2", "aria-live": "polite", children: [loaderIcon, jsxRuntime.jsx(TextContent, { className: buildClassName(LOADER_TEXT_CLASS[theme][variant]), children: loadingText })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [leftIcon && jsxRuntime.jsx("span", { className: "shrink-0 flex items-center", children: leftIcon }), children, rightIcon && jsxRuntime.jsx("span", { className: "shrink-0 flex items-center", children: rightIcon })] })) }));
317
366
  });
318
367
  Button.displayName = 'Button';
319
368
 
@@ -344,13 +393,13 @@ const ThemeClassMap = {
344
393
  info: 'bg-[var(--ui-info-light)] text-[var(--ui-info-text)] ring-[var(--ui-info-ring-subtle)]',
345
394
  secondary: 'bg-gray-50 dark:bg-gray-700 text-gray-700 dark:text-gray-200 ring-[var(--ui-secondary-ring-subtle)]',
346
395
  };
347
- const SIZE_CLASS = {
396
+ const SIZE_CLASS$1 = {
348
397
  sm: 'text-xs py-0.5 px-2',
349
398
  md: 'text-xs py-1 px-3',
350
399
  };
351
400
  const Badge = React.forwardRef((props, ref) => {
352
401
  const { theme = 'info', className, children, removable = false, rounded = false, onRemove, size = 'md', icon, ...restProps } = props;
353
- return (jsxRuntime.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 && jsxRuntime.jsx("span", { className: "shrink-0 inline-flex", children: icon }), children, removable && (jsxRuntime.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) => {
402
+ return (jsxRuntime.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 && jsxRuntime.jsx("span", { className: "shrink-0 inline-flex", children: icon }), children, removable && (jsxRuntime.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) => {
354
403
  if (e.key === 'Enter' || e.key === ' ') {
355
404
  e.preventDefault();
356
405
  onRemove === null || onRemove === void 0 ? void 0 : onRemove();
@@ -428,21 +477,54 @@ const Banner = React.forwardRef((props, ref) => {
428
477
  });
429
478
  Banner.displayName = 'Banner';
430
479
 
480
+ const sizeClassMap$1 = {
481
+ xs: 'text-xs/5',
482
+ sm: 'text-sm/6',
483
+ md: 'text-base/7',
484
+ lg: 'text-lg/8',
485
+ xl: 'text-xl/9',
486
+ };
487
+ const weightClassMap$1 = {
488
+ light: 'font-light',
489
+ normal: 'font-normal',
490
+ medium: 'font-medium',
491
+ semibold: 'font-semibold',
492
+ bold: 'font-bold',
493
+ };
494
+ const alignClassMap$1 = {
495
+ left: 'text-left',
496
+ center: 'text-center',
497
+ right: 'text-right',
498
+ justify: 'text-justify',
499
+ };
500
+ const clampClassMap = {
501
+ 1: 'line-clamp-1',
502
+ 2: 'line-clamp-2',
503
+ 3: 'line-clamp-3',
504
+ 4: 'line-clamp-4',
505
+ };
431
506
  const BodyText = React.forwardRef((props, ref) => {
432
- const { as = 'p', small = false, strong = false, muted = false, error = false, inline = false, className, invert, ...rest } = props;
507
+ 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;
433
508
  const textColorClass = error
434
509
  ? 'text-[var(--ui-text-danger)]'
435
- : muted
436
- ? invert
437
- ? 'text-gray-300 dark:text-gray-600'
438
- : 'text-[var(--ui-text-muted)]'
439
- : invert
440
- ? 'text-white dark:text-black'
441
- : 'text-[var(--ui-text)]';
510
+ : success
511
+ ? 'text-[var(--ui-success)]'
512
+ : warning
513
+ ? 'text-[var(--ui-warning)]'
514
+ : info
515
+ ? 'text-[var(--ui-info)]'
516
+ : muted
517
+ ? invert
518
+ ? 'text-gray-300 dark:text-gray-600'
519
+ : 'text-[var(--ui-text-muted)]'
520
+ : invert
521
+ ? 'text-white dark:text-black'
522
+ : 'text-[var(--ui-text)]';
442
523
  return React.createElement(as, {
443
- ref: ref,
444
- className: buildClassName(textColorClass, small && 'text-sm/6', strong && 'font-semibold', inline && 'inline-flex items-center gap-2', className),
524
+ ref,
525
+ 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),
445
526
  ...rest,
527
+ children,
446
528
  });
447
529
  });
448
530
  BodyText.displayName = 'BodyText';
@@ -466,24 +548,60 @@ var SvgSlash = function SvgSlash(props) {
466
548
  })));
467
549
  };
468
550
 
469
- const Breadcrumb = ({ items, render }) => {
470
- const count = items.length;
471
- const isLast = (index) => index === count - 1;
472
- return (jsxRuntime.jsx("nav", { "aria-label": "Breadcrumb", className: "flex flex-wrap items-center gap-2", "data-testid": "breadcrumb-container", children: items.map((item, index) => (jsxRuntime.jsxs("span", { className: "inline-flex gap-2 items-center dark:text-white", "data-testid": `breadcrumb-item-${item.key}`, children: [jsxRuntime.jsx("span", { className: "font-medium", ...(isLast(index) ? { 'aria-current': 'page' } : {}), children: isLast(index) ? item.text : render(item) }), !isLast(index) && (jsxRuntime.jsx(SvgSlash, { "data-testid": `breadcrumb-divider-${index}`, className: "size-4" }))] }, item.key))) }));
551
+ const SIZE_CONTAINER_CLASS$2 = {
552
+ sm: 'text-xs gap-1.5',
553
+ md: 'text-sm gap-2',
554
+ lg: 'text-base gap-2.5',
555
+ };
556
+ const SIZE_ICON_CLASS$1 = {
557
+ sm: 'size-3',
558
+ md: 'size-3.5',
559
+ lg: 'size-4',
473
560
  };
561
+ function RenderSeparator({ separator, size, index, className, }) {
562
+ const testId = `breadcrumb-divider-${index}`;
563
+ if (separator === 'slash' || !separator) {
564
+ return (jsxRuntime.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) }));
565
+ }
566
+ if (separator === 'chevron') {
567
+ return (jsxRuntime.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: jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) }));
568
+ }
569
+ if (separator === 'dot') {
570
+ return (jsxRuntime.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) }));
571
+ }
572
+ return (jsxRuntime.jsx("span", { "data-testid": testId, className: buildClassName('shrink-0 select-none', className), children: separator }));
573
+ }
574
+ const Breadcrumb = React.forwardRef((props, ref) => {
575
+ const { items = [], render, separator = 'slash', size = 'md', maxItems, className, itemClass, activeItemClass, separatorClass, ...navProps } = props;
576
+ const [expanded, setExpanded] = React.useState(false);
577
+ const count = items.length;
578
+ const shouldTruncate = maxItems !== undefined && maxItems > 1 && count > maxItems && !expanded;
579
+ const getVisibleItems = () => {
580
+ if (!shouldTruncate)
581
+ return items;
582
+ // Always keep 1st item and last 1-2 items
583
+ const firstItem = items[0];
584
+ const lastItems = items.slice(count - (maxItems - 1));
585
+ return [firstItem, ...lastItems];
586
+ };
587
+ const hiddenItems = shouldTruncate ? items.slice(1, count - (maxItems - 1)) : [];
588
+ getVisibleItems();
589
+ return (jsxRuntime.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) => {
590
+ const isLast = index === count - 1;
591
+ const isCollapsedMiddle = shouldTruncate && index > 0 && index < count - (maxItems - 1);
592
+ if (isCollapsedMiddle) {
593
+ if (index === 1) {
594
+ // Render the single '...' collapse pill
595
+ return (jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2 relative group/ellipsis", children: [jsxRuntime.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" }), jsxRuntime.jsx(RenderSeparator, { separator: separator, size: size, index: index, className: separatorClass })] }, "breadcrumb-collapsed"));
596
+ }
597
+ return null;
598
+ }
599
+ const defaultContent = isLast ? (jsxRuntime.jsx("span", { className: buildClassName('text-gray-900 dark:text-white font-semibold', activeItemClass, item.className), children: item.text })) : item.to ? (jsxRuntime.jsx("a", { href: item.to, className: buildClassName('hover:text-blue-600 dark:hover:text-blue-400 transition-colors', itemClass, item.className), children: item.text })) : (jsxRuntime.jsx("span", { className: buildClassName(itemClass, item.className), children: item.text }));
600
+ return (jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2", "data-testid": `breadcrumb-item-${item.key}`, children: [jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5", ...(isLast ? { 'aria-current': 'page' } : {}), children: [item.icon && jsxRuntime.jsx("span", { className: "shrink-0 text-gray-400", children: item.icon }), isLast ? (defaultContent) : render ? (jsxRuntime.jsx("span", { className: buildClassName(itemClass, item.className), children: render(item) })) : (defaultContent)] }), !isLast && (jsxRuntime.jsx(RenderSeparator, { separator: separator, size: size, index: index, className: separatorClass }))] }, item.key));
601
+ }) }));
602
+ });
474
603
  Breadcrumb.displayName = 'Breadcrumb';
475
604
 
476
- /**
477
- * Corner-rounding classes for the group's first/last button, keyed by
478
- * orientation + size, matching Button's own size -> radius mapping
479
- * (xs: rounded-sm, sm: rounded, md: rounded-md, lg: rounded-lg).
480
- *
481
- * These must stay fully static string literals (no template-literal
482
- * interpolation) — Tailwind's JIT scanner only picks up class names that
483
- * appear verbatim in source, and the docs site scans the library's
484
- * *compiled* output, where interpolated fragments never appear as a
485
- * complete class name.
486
- */
487
605
  const EDGE_ROUNDING = {
488
606
  horizontal: {
489
607
  xs: { first: 'rounded-l-sm rounded-r-none', last: 'rounded-r-sm rounded-l-none' },
@@ -502,11 +620,6 @@ const PILL_EDGE_ROUNDING = {
502
620
  horizontal: { first: 'rounded-l-full rounded-r-none', last: 'rounded-r-full rounded-l-none' },
503
621
  vertical: { first: 'rounded-t-full rounded-b-none', last: 'rounded-b-full rounded-t-none' },
504
622
  };
505
- /**
506
- * Resolves the corner-rounding classes for a single button at `index`
507
- * out of `count` inside the group, so only the group's outer edges stay
508
- * rounded and interior seams go square.
509
- */
510
623
  const getEdgeRoundingClass = ({ index, count, size, orientation, pill, }) => {
511
624
  if (count <= 1)
512
625
  return '';
@@ -517,18 +630,46 @@ const getEdgeRoundingClass = ({ index, count, size, orientation, pill, }) => {
517
630
  ? PILL_EDGE_ROUNDING[orientation][position]
518
631
  : EDGE_ROUNDING[orientation][size][position];
519
632
  };
520
- const ButtonGroup = ({ children, orientation = 'horizontal', theme = 'secondary', variant = 'default', size = 'md', rounded = false, fullWidth = false, label, className = '', }) => {
633
+ const ButtonGroup = ({ children, orientation = 'horizontal', theme = 'secondary', variant = 'default', size = 'md', value, onChange, disabled: groupDisabled = false, rounded = false, fullWidth = false, label, className = '', }) => {
521
634
  const isVertical = orientation === 'vertical';
522
635
  const items = React.useMemo(() => React.Children.toArray(children).filter(React.isValidElement), [children]);
523
636
  const count = items.length;
524
- return (jsxRuntime.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) => {
525
- var _a, _b, _c;
526
- const childSize = (_a = child.props.size) !== null && _a !== void 0 ? _a : size;
637
+ const isToggleMode = value !== undefined;
638
+ return (jsxRuntime.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) => {
639
+ var _a, _b, _c, _d, _e, _f, _g, _h;
640
+ const childProps = child.props;
641
+ const childSize = (_a = childProps.size) !== null && _a !== void 0 ? _a : size;
642
+ const childDisabled = (_b = childProps.disabled) !== null && _b !== void 0 ? _b : groupDisabled;
643
+ // Resolve item value for toggle mode
644
+ 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'
645
+ ? childProps.children
646
+ : index);
647
+ const isSelected = isToggleMode
648
+ ? Array.isArray(value)
649
+ ? value.includes(itemVal)
650
+ : value === itemVal
651
+ : false;
652
+ const resolvedTheme = isToggleMode
653
+ ? isSelected
654
+ ? ((_e = childProps.theme) !== null && _e !== void 0 ? _e : 'primary')
655
+ : ((_f = childProps.theme) !== null && _f !== void 0 ? _f : 'secondary')
656
+ : ((_g = childProps.theme) !== null && _g !== void 0 ? _g : theme);
657
+ const resolvedVariant = (_h = childProps.variant) !== null && _h !== void 0 ? _h : variant;
658
+ const handleClick = (e) => {
659
+ var _a;
660
+ (_a = childProps.onClick) === null || _a === void 0 ? void 0 : _a.call(childProps, e);
661
+ if (isToggleMode && !childDisabled) {
662
+ onChange === null || onChange === void 0 ? void 0 : onChange(itemVal);
663
+ }
664
+ };
527
665
  return React.cloneElement(child, {
528
- theme: (_b = child.props.theme) !== null && _b !== void 0 ? _b : theme,
529
- variant: (_c = child.props.variant) !== null && _c !== void 0 ? _c : variant,
666
+ theme: resolvedTheme,
667
+ variant: resolvedVariant,
530
668
  size: childSize,
531
- className: buildClassName(child.props.className, getEdgeRoundingClass({
669
+ disabled: childDisabled,
670
+ ...(isToggleMode ? { 'aria-pressed': isSelected } : {}),
671
+ onClick: handleClick,
672
+ className: buildClassName(childProps.className, getEdgeRoundingClass({
532
673
  index,
533
674
  count,
534
675
  size: childSize,
@@ -536,23 +677,66 @@ const ButtonGroup = ({ children, orientation = 'horizontal', theme = 'secondary'
536
677
  pill: rounded,
537
678
  }), count > 1 && [
538
679
  'relative shadow-none hover:z-10 focus:z-10 focus-visible:z-10',
680
+ isSelected && 'z-10',
539
681
  index > 0 && (isVertical ? '-mt-px' : '-ml-px'),
682
+ index > 0 &&
683
+ resolvedVariant === 'default' &&
684
+ (isVertical
685
+ ? resolvedTheme === 'primary' || resolvedTheme === 'danger'
686
+ ? 'border-t border-white/25 dark:border-white/20'
687
+ : 'border-t border-gray-300/70 dark:border-gray-700/70'
688
+ : resolvedTheme === 'primary' || resolvedTheme === 'danger'
689
+ ? 'border-l border-white/25 dark:border-white/20'
690
+ : 'border-l border-gray-300/70 dark:border-gray-700/70'),
540
691
  ], fullWidth && 'flex-1'),
541
692
  });
542
693
  }) }));
543
694
  };
544
695
  ButtonGroup.displayName = 'ButtonGroup';
545
696
 
546
- const BASE_CARD_CLASS = 'bg-white rounded-md dark:bg-gray-900 block';
697
+ const RADIUS_CLASS = {
698
+ none: 'rounded-none',
699
+ sm: 'rounded-sm',
700
+ md: 'rounded-md',
701
+ lg: 'rounded-lg',
702
+ xl: 'rounded-xl',
703
+ '2xl': 'rounded-2xl',
704
+ };
705
+ const ASPECT_RATIO_CLASS = {
706
+ auto: 'aspect-auto',
707
+ square: 'aspect-square',
708
+ video: 'aspect-video',
709
+ wide: 'aspect-[21/9]',
710
+ };
547
711
  const Card = React.forwardRef(({ as, ...props }, ref) => {
548
- // Remove Card-only props from rest (avoid leaking to DOM)
549
- const { hoverable = false, bordered = true, compact = false, zeroPadding = false, ...rest } = props;
712
+ const { variant = 'outlined', radius = 'xl', hoverable = false, clickable = false, selected = false, bordered = true, compact = false, zeroPadding = false, ...rest } = props;
550
713
  const Component = as || 'div';
551
- // Extract className safely from rest
552
714
  const { className, ...cleanRest } = rest;
553
- 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);
715
+ const isOutlined = variant === 'outlined' && bordered;
716
+ const isElevated = variant === 'elevated';
717
+ const isFilled = variant === 'filled';
718
+ const isGhost = variant === 'ghost';
719
+ 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',
720
+ // Surface variants
721
+ isOutlined && 'bg-white dark:bg-gray-900 border border-[var(--ui-border)]', isElevated &&
722
+ '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',
723
+ // Hoverable & Clickable states
724
+ 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);
554
725
  return jsxRuntime.jsx(Component, { ref: ref, className: classes, ...cleanRest });
555
726
  });
727
+ const CardHeader = ({ className, bordered = false, children, ...props }) => (jsxRuntime.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 }));
728
+ const CardTitle = ({ className, as: Component = 'h3', children, ...props }) => (jsxRuntime.jsx(Component, { className: buildClassName('text-lg font-semibold text-gray-900 dark:text-white tracking-tight', className), ...props, children: children }));
729
+ const CardDescription = ({ className, children, ...props }) => (jsxRuntime.jsx("p", { className: buildClassName('text-sm text-gray-500 dark:text-gray-400 font-normal mt-1', className), ...props, children: children }));
730
+ const CardContent = ({ className, children, ...props }) => (jsxRuntime.jsx("div", { className: buildClassName('space-y-3', className), ...props, children: children }));
731
+ const CardFooter = ({ className, bordered = false, children, ...props }) => (jsxRuntime.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 }));
732
+ const CardMedia = ({ className, aspectRatio = 'video', alt = '', ...props }) => (jsxRuntime.jsx("div", { className: buildClassName('overflow-hidden -mx-5 -mt-5 mb-4 sm:-mx-6 sm:-mt-6', className), children: jsxRuntime.jsx("img", { alt: alt, className: buildClassName('w-full object-cover', ASPECT_RATIO_CLASS[aspectRatio] || ASPECT_RATIO_CLASS.video), ...props }) }));
733
+ Card.Header = CardHeader;
734
+ Card.Title = CardTitle;
735
+ Card.Description = CardDescription;
736
+ Card.Content = CardContent;
737
+ Card.Body = CardContent;
738
+ Card.Footer = CardFooter;
739
+ Card.Media = CardMedia;
556
740
  Card.displayName = 'Card';
557
741
 
558
742
  const Label = React.forwardRef((props, ref) => {
@@ -561,49 +745,127 @@ const Label = React.forwardRef((props, ref) => {
561
745
  });
562
746
  Label.displayName = 'Label';
563
747
 
748
+ const sizeStyles = {
749
+ sm: {
750
+ box: 'size-3.5',
751
+ svg: 'h-2.5 w-2.5',
752
+ text: 'text-xs',
753
+ subtext: 'text-[11px]',
754
+ },
755
+ md: {
756
+ box: 'size-4.5',
757
+ svg: 'h-3.25 w-3.25',
758
+ text: 'text-sm',
759
+ subtext: 'text-xs',
760
+ },
761
+ lg: {
762
+ box: 'size-5.5',
763
+ svg: 'h-4 w-4',
764
+ text: 'text-base',
765
+ subtext: 'text-sm',
766
+ },
767
+ };
564
768
  const Checkbox = React.forwardRef((props, ref) => {
565
769
  var _a;
566
- const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '',
567
- // Checkbox-only props removed from rest
568
- ...rest } = props;
770
+ const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, helperText, disabled = false, indeterminate = false, size = 'md', variant = 'default', description, className = '', checked, onChange, ...rest } = props;
569
771
  const generatedId = React.useId();
570
772
  const checkboxId = (_a = id !== null && id !== void 0 ? id : name) !== null && _a !== void 0 ? _a : generatedId;
571
- return (jsxRuntime.jsxs("div", { className: buildClassName(wrapperClass), children: [jsxRuntime.jsxs("div", { className: buildClassName('flex items-center', containerClass), children: [jsxRuntime.jsxs("label", { htmlFor: checkboxId, className: buildClassName('inline-flex items-center relative select-none', disabled ? 'cursor-not-allowed opacity-70' : 'cursor-pointer'), children: [jsxRuntime.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
572
- ? 'border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-gray-800 pointer-events-none'
573
- : '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) }), jsxRuntime.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: jsxRuntime.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: jsxRuntime.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 && (jsxRuntime.jsx(Label, { htmlFor: checkboxId, className: buildClassName(disabled
574
- ? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
575
- : 'cursor-pointer text-gray-600 dark:text-gray-300', 'ml-2 font-normal', labelClass), children: label }))] }), error && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) }))] }));
773
+ const innerRef = React.useRef(null);
774
+ React.useImperativeHandle(ref, () => innerRef.current);
775
+ React.useEffect(() => {
776
+ if (innerRef.current) {
777
+ innerRef.current.indeterminate = Boolean(indeterminate);
778
+ }
779
+ }, [indeterminate]);
780
+ const currentSize = sizeStyles[size] || sizeStyles.md;
781
+ const isCard = variant === 'card';
782
+ return (jsxRuntime.jsxs("div", { className: buildClassName(wrapperClass), children: [jsxRuntime.jsx("div", { className: buildClassName(isCard
783
+ ? 'rounded-xl border p-3.5 transition-all duration-150 relative'
784
+ : 'flex items-start', isCard &&
785
+ (disabled
786
+ ? 'border-gray-200 dark:border-gray-800 bg-gray-50/50 dark:bg-gray-900/40 opacity-70'
787
+ : checked || indeterminate
788
+ ? 'border-blue-500/80 bg-blue-50/30 dark:bg-blue-950/20 dark:border-blue-500/60 shadow-xs'
789
+ : '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: jsxRuntime.jsxs("label", { htmlFor: checkboxId, className: buildClassName('inline-flex items-start relative select-none w-full', disabled ? 'cursor-not-allowed opacity-70' : 'cursor-pointer'), children: [jsxRuntime.jsxs("div", { className: "relative inline-flex items-center shrink-0 pt-0.5", children: [jsxRuntime.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
790
+ ? 'border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-gray-800 pointer-events-none'
791
+ : '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 &&
792
+ '!bg-blue-600 !border-blue-600 dark:!bg-blue-600 dark:!border-blue-600', className) }), !indeterminate && (jsxRuntime.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
793
+ ? checked
794
+ ? 'opacity-100'
795
+ : 'opacity-0'
796
+ : 'opacity-0 peer-checked:opacity-100'), children: jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", stroke: "currentColor", strokeWidth: "1", children: jsxRuntime.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 && (jsxRuntime.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: jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", stroke: "currentColor", strokeWidth: "2", children: jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M4 10a1 1 0 011-1h10a1 1 0 110 2H5a1 1 0 01-1-1z", clipRule: "evenodd" }) }) }))] }), (label || description) && (jsxRuntime.jsxs("div", { className: "ml-2.5 flex flex-col justify-center min-w-0 flex-1", children: [label && (jsxRuntime.jsx(Label, { htmlFor: checkboxId, className: buildClassName(disabled
797
+ ? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
798
+ : 'cursor-pointer text-gray-800 dark:text-gray-200 font-medium', currentSize.text, labelClass), children: label })), description && (jsxRuntime.jsx("span", { className: buildClassName('mt-0.5 text-gray-500 dark:text-gray-400 font-normal leading-normal', currentSize.subtext), children: description }))] }))] }) }), (error || helperText) && (jsxRuntime.jsx("div", { className: "mt-1.5 ml-0.5", children: error ? (jsxRuntime.jsx(TextContent, { error: true, small: true, children: error })) : (jsxRuntime.jsx(TextContent, { muted: true, small: true, children: helperText })) }))] }));
576
799
  });
577
800
  Checkbox.displayName = 'Checkbox';
578
801
 
802
+ const columnClasses = {
803
+ 1: 'grid grid-cols-1 gap-3',
804
+ 2: 'grid grid-cols-1 sm:grid-cols-2 gap-3',
805
+ 3: 'grid grid-cols-1 sm:grid-cols-3 gap-3',
806
+ 4: 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3',
807
+ };
579
808
  const CheckboxGroup = (props) => {
580
- const { name, options, label, labelClass = '', labelWrapperClass = '', labelHint, containerClass = '', value = [], onChange, inline = false, } = props;
809
+ 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;
581
810
  const groupId = React.useId();
811
+ const [internalValue, setInternalValue] = React.useState(defaultValue || []);
812
+ const isControlled = controlledValue !== undefined;
813
+ const currentValue = isControlled ? controlledValue : internalValue;
582
814
  const items = React.useMemo(() => options.map((opt) => typeof opt === 'string' ? { label: opt, value: opt } : opt), [options]);
583
- /**
584
- * Group-level change handler. Converts raw checkbox change events
585
- * into a typed updated values array.
586
- */
815
+ const enabledItems = React.useMemo(() => items.filter((i) => !i.disabled && !disabled), [items, disabled]);
816
+ // Select All State calculation
817
+ const allSelected = React.useMemo(() => {
818
+ if (!enabledItems.length)
819
+ return false;
820
+ return enabledItems.every((item) => currentValue.includes(item.value));
821
+ }, [enabledItems, currentValue]);
822
+ const isIndeterminate = React.useMemo(() => {
823
+ if (allSelected)
824
+ return false;
825
+ return enabledItems.some((item) => currentValue.includes(item.value));
826
+ }, [allSelected, enabledItems, currentValue]);
827
+ const handleSelectAllChange = (e) => {
828
+ let updated;
829
+ if (allSelected || isIndeterminate) {
830
+ const enabledValues = new Set(enabledItems.map((i) => i.value));
831
+ updated = currentValue.filter((v) => !enabledValues.has(v));
832
+ }
833
+ else {
834
+ const enabledValues = enabledItems.map((i) => i.value);
835
+ const uniqueSet = new Set([...currentValue, ...enabledValues]);
836
+ updated = Array.from(uniqueSet);
837
+ }
838
+ if (!isControlled) {
839
+ setInternalValue(updated);
840
+ }
841
+ onChange === null || onChange === void 0 ? void 0 : onChange(updated, e);
842
+ };
587
843
  const handleGroupChange = (e) => {
588
844
  const raw = e.target.value;
589
845
  const checked = e.target.checked;
590
- // Convert HTML string value → typed T value
591
- // Locate in normalized items
592
846
  const matched = items.find((i) => String(i.value) === raw);
593
- const itemValue = matched === null || matched === void 0 ? void 0 : matched.value;
847
+ const itemValue = matched ? matched.value : raw;
594
848
  let updated;
595
849
  if (checked) {
596
- updated = [...value, itemValue];
850
+ updated = [...currentValue, itemValue];
597
851
  }
598
852
  else {
599
- updated = value.filter((v) => v !== itemValue);
853
+ updated = currentValue.filter((v) => v !== itemValue);
854
+ }
855
+ if (!isControlled) {
856
+ setInternalValue(updated);
600
857
  }
601
858
  onChange === null || onChange === void 0 ? void 0 : onChange(updated, e);
602
859
  };
603
- return (jsxRuntime.jsxs("div", { className: buildClassName('mb-3 space-y-3', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, children: label }), jsxRuntime.jsx(BodyText, { muted: true, small: true, children: labelHint })] })), jsxRuntime.jsx("div", { className: buildClassName('flex gap-3', inline ? 'flex-wrap' : 'flex-col'), children: items.map((item, index) => {
860
+ const layoutClass = columns
861
+ ? columnClasses[columns]
862
+ : inline
863
+ ? 'flex flex-wrap gap-3'
864
+ : 'flex flex-col gap-3';
865
+ return (jsxRuntime.jsxs("div", { className: buildClassName('mb-3 space-y-3', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, children: label }), labelHint && (jsxRuntime.jsx(BodyText, { muted: true, small: true, children: labelHint }))] })), showSelectAll && items.length > 0 && (jsxRuntime.jsx("div", { className: "pb-1 border-b border-gray-200 dark:border-gray-800", children: jsxRuntime.jsx(Checkbox, { id: `${groupId}-select-all`, label: selectAllLabel, size: size, variant: variant, checked: allSelected, indeterminate: isIndeterminate, disabled: disabled || enabledItems.length === 0, onChange: handleSelectAllChange }) })), jsxRuntime.jsx("div", { className: layoutClass, children: items.map((item, index) => {
604
866
  const key = typeof item.value === 'string' || typeof item.value === 'number' ? item.value : index;
605
- return (jsxRuntime.jsx(Checkbox, { name: name, id: `${groupId}-${String(item.value)}`, label: item.label, value: String(item.value), checked: value.includes(item.value), onChange: handleGroupChange }, key));
606
- }) })] }));
867
+ return (jsxRuntime.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));
868
+ }) }), (error || helperText) && (jsxRuntime.jsx("div", { className: "mt-1.5 ml-0.5", children: error ? (jsxRuntime.jsx(TextContent, { error: true, small: true, children: error })) : (jsxRuntime.jsx(TextContent, { muted: true, small: true, children: helperText })) }))] }));
607
869
  };
608
870
  CheckboxGroup.displayName = 'CheckboxGroup';
609
871
 
@@ -1030,24 +1292,142 @@ function DataTable({ items, columns, sorting = {}, onSort, loading, pagination,
1030
1292
  }
1031
1293
  DataTable.displayName = 'DataTable';
1032
1294
 
1033
- const Title = ({ children, className }) => (jsxRuntime.jsx("h1", { className: buildClassName('text-3xl font-bold text-[var(--ui-text)]', className), children: children }));
1034
- const SubTitle = ({ children, className }) => (jsxRuntime.jsx("h2", { className: buildClassName('text-2xl font-semibold text-[var(--ui-text)]', className), children: children }));
1035
- const SubTitle2 = ({ children, className }) => (jsxRuntime.jsx("h3", { className: buildClassName('text-xl font-semibold text-[var(--ui-text)]', className), children: children }));
1036
- const SubTitle3 = ({ children, className }) => (jsxRuntime.jsx("h4", { className: buildClassName('text-lg font-medium text-[var(--ui-text)]', className), children: children }));
1037
- const SubTitle4 = ({ children, className }) => (jsxRuntime.jsx("h5", { className: buildClassName('text-base font-medium text-[var(--ui-text)]', className), children: children }));
1038
- const HeadingText = {
1295
+ const defaultLevelSizeMap = {
1296
+ 1: 'text-3xl font-bold',
1297
+ 2: 'text-2xl font-semibold',
1298
+ 3: 'text-xl font-semibold',
1299
+ 4: 'text-lg font-medium',
1300
+ 5: 'text-base font-medium',
1301
+ 6: 'text-sm font-medium',
1302
+ };
1303
+ const sizeClassMap = {
1304
+ xs: 'text-xs',
1305
+ sm: 'text-sm',
1306
+ md: 'text-base',
1307
+ lg: 'text-lg',
1308
+ xl: 'text-xl',
1309
+ '2xl': 'text-2xl',
1310
+ '3xl': 'text-3xl',
1311
+ '4xl': 'text-4xl',
1312
+ '5xl': 'text-5xl',
1313
+ };
1314
+ const weightClassMap = {
1315
+ normal: 'font-normal',
1316
+ medium: 'font-medium',
1317
+ semibold: 'font-semibold',
1318
+ bold: 'font-bold',
1319
+ extrabold: 'font-extrabold',
1320
+ };
1321
+ const alignClassMap = {
1322
+ left: 'text-left',
1323
+ center: 'text-center',
1324
+ right: 'text-right',
1325
+ };
1326
+ const trackingClassMap = {
1327
+ tighter: 'tracking-tighter',
1328
+ tight: 'tracking-tight',
1329
+ normal: 'tracking-normal',
1330
+ wide: 'tracking-wide',
1331
+ };
1332
+ const HeadingTextComponent = React.forwardRef((props, ref) => {
1333
+ 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;
1334
+ const tag = `h${Math.min(6, Math.max(1, level))}`;
1335
+ const colorClass = error
1336
+ ? 'text-[var(--ui-text-danger)]'
1337
+ : success
1338
+ ? 'text-[var(--ui-success)]'
1339
+ : warning
1340
+ ? 'text-[var(--ui-warning)]'
1341
+ : info
1342
+ ? 'text-[var(--ui-info)]'
1343
+ : muted
1344
+ ? invert
1345
+ ? 'text-gray-300 dark:text-gray-600'
1346
+ : 'text-[var(--ui-text-muted)]'
1347
+ : invert
1348
+ ? 'text-white dark:text-black'
1349
+ : 'text-[var(--ui-text)]';
1350
+ return React.createElement(tag, {
1351
+ ref,
1352
+ className: buildClassName(colorClass, size ? sizeClassMap[size] : defaultLevelSizeMap[level], weight && weightClassMap[weight], align && alignClassMap[align], tracking && trackingClassMap[tracking], truncate && 'truncate', className),
1353
+ ...rest,
1354
+ children,
1355
+ });
1356
+ });
1357
+ HeadingTextComponent.displayName = 'HeadingText';
1358
+ const Title = React.forwardRef((props, ref) => (jsxRuntime.jsx(HeadingTextComponent, { ref: ref, level: 1, ...props })));
1359
+ Title.displayName = 'HeadingText.Title';
1360
+ const SubTitle = React.forwardRef((props, ref) => (jsxRuntime.jsx(HeadingTextComponent, { ref: ref, level: 2, ...props })));
1361
+ SubTitle.displayName = 'HeadingText.SubTitle';
1362
+ const SubTitle2 = React.forwardRef((props, ref) => (jsxRuntime.jsx(HeadingTextComponent, { ref: ref, level: 3, ...props })));
1363
+ SubTitle2.displayName = 'HeadingText.SubTitle2';
1364
+ const SubTitle3 = React.forwardRef((props, ref) => (jsxRuntime.jsx(HeadingTextComponent, { ref: ref, level: 4, ...props })));
1365
+ SubTitle3.displayName = 'HeadingText.SubTitle3';
1366
+ const SubTitle4 = React.forwardRef((props, ref) => (jsxRuntime.jsx(HeadingTextComponent, { ref: ref, level: 5, ...props })));
1367
+ SubTitle4.displayName = 'HeadingText.SubTitle4';
1368
+ const HeadingText = Object.assign(HeadingTextComponent, {
1039
1369
  Title,
1040
1370
  SubTitle,
1041
1371
  SubTitle2,
1042
1372
  SubTitle3,
1043
1373
  SubTitle4,
1044
- };
1374
+ });
1045
1375
 
1376
+ const COLUMN_GRID_CLASS = {
1377
+ 1: 'grid-cols-1',
1378
+ 2: 'grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-3',
1379
+ 3: 'grid grid-cols-1 md:grid-cols-3 gap-x-6 gap-y-3',
1380
+ 4: 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-x-6 gap-y-3',
1381
+ };
1382
+ function CopyableValue({ text, children }) {
1383
+ const [copied, setCopied] = React.useState(false);
1384
+ const handleCopy = async (e) => {
1385
+ e.stopPropagation();
1386
+ try {
1387
+ await navigator.clipboard.writeText(text);
1388
+ setCopied(true);
1389
+ setTimeout(() => setCopied(false), 2000);
1390
+ }
1391
+ catch {
1392
+ // Ignore clipboard error
1393
+ }
1394
+ };
1395
+ return (jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5 group/copy", children: [jsxRuntime.jsx("span", { children: children }), jsxRuntime.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 ? (jsxRuntime.jsx("svg", { className: "size-3.5 text-emerald-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" }) })) : (jsxRuntime.jsx("svg", { className: "size-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: jsxRuntime.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" }) })) })] }));
1396
+ }
1046
1397
  const DetailedInformation = React.forwardRef((props, ref) => {
1047
- const { className, title, subTitle, details, compact = false, divider = true, titleClass, detailsClass, bordered = false, ...divProps } = props;
1048
- const hasTitles = title || subTitle;
1398
+ const { className, title, subTitle, action, details = [], columns = 1, layout = 'horizontal', variant = 'flat', compact = false, bordered = false, divider = true, emptyValue = '- -', titleClass, detailsClass, labelClass, ...divProps } = props;
1399
+ const isCard = variant === 'card';
1400
+ const isBordered = bordered || variant === 'bordered' || isCard;
1401
+ const hasTitles = title || subTitle || action;
1049
1402
  const filteredDetails = details.filter((detail) => !detail.hidden);
1050
- return (jsxRuntime.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 && (jsxRuntime.jsxs("div", { className: "px-4 sm:px-0 pt-2 sm:pt-0 mb-2", children: [title && (jsxRuntime.jsx(HeadingText.SubTitle2, { className: buildClassName(titleClass), children: title })), subTitle && (jsxRuntime.jsx("p", { className: "mt-1 max-w-2xl text-sm/6 text-gray-500 dark:text-gray-300", children: subTitle }))] })), filteredDetails.length ? (jsxRuntime.jsx("div", { className: buildClassName('border-[var(--ui-border-muted)]', hasTitles && 'border-t', detailsClass), children: jsxRuntime.jsx("dl", { className: buildClassName(divider && 'divide-y divide-[var(--ui-border-muted)]'), children: filteredDetails.map((detail) => (jsxRuntime.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: [jsxRuntime.jsx("dt", { className: "text-sm/6 font-medium text-gray-900 dark:text-gray-100", children: detail.label }), jsxRuntime.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] }));
1403
+ const isMultiColumn = columns > 1;
1404
+ return (jsxRuntime.jsxs("div", { ref: ref, className: buildClassName(isCard
1405
+ ? compact
1406
+ ? 'p-3 sm:p-4 bg-gray-50/60 dark:bg-gray-800/40 rounded-2xl border border-gray-200 dark:border-gray-700'
1407
+ : 'p-4 sm:p-6 bg-gray-50/60 dark:bg-gray-800/40 rounded-2xl border border-gray-200 dark:border-gray-700'
1408
+ : isBordered
1409
+ ? compact
1410
+ ? 'p-3 sm:p-4 border border-[var(--ui-border)] rounded-2xl'
1411
+ : 'p-4 sm:p-6 border border-[var(--ui-border)] rounded-2xl'
1412
+ : compact
1413
+ ? 'p-2 md:p-3'
1414
+ : 'p-3 md:p-4', className), ...divProps, children: [hasTitles && (jsxRuntime.jsxs("div", { className: "mb-4 flex items-start justify-between gap-4", children: [jsxRuntime.jsxs("div", { children: [title && (jsxRuntime.jsx(HeadingText.SubTitle2, { className: buildClassName(titleClass), children: title })), subTitle && (jsxRuntime.jsx("p", { className: "mt-1 max-w-2xl text-sm/6 text-gray-500 dark:text-gray-400 font-normal", children: subTitle }))] }), action && jsxRuntime.jsx("div", { className: "shrink-0 flex items-center", children: action })] })), filteredDetails.length ? (jsxRuntime.jsx("div", { className: buildClassName('border-[var(--ui-border-muted)]', hasTitles && !isMultiColumn && 'border-t pt-2', detailsClass), children: jsxRuntime.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) => {
1415
+ var _a;
1416
+ const itemFallback = (_a = detail.emptyValue) !== null && _a !== void 0 ? _a : emptyValue;
1417
+ const hasValue = detail.value !== undefined && detail.value !== null && detail.value !== '';
1418
+ const displayValue = hasValue ? detail.value : itemFallback;
1419
+ const copyText = typeof detail.value === 'string' || typeof detail.value === 'number'
1420
+ ? String(detail.value)
1421
+ : '';
1422
+ const isVertical = layout === 'vertical' || isMultiColumn;
1423
+ return (jsxRuntime.jsxs("div", { className: buildClassName(isMultiColumn && isCard
1424
+ ? compact
1425
+ ? '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'
1426
+ : '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'
1427
+ : isVertical
1428
+ ? 'flex flex-col gap-1 py-2 px-1'
1429
+ : 'grid grid-cols-3 gap-4 items-center py-2.5 px-1', compact && !isMultiColumn ? 'py-1.5' : ''), children: [jsxRuntime.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 && jsxRuntime.jsx("span", { className: "shrink-0 text-gray-400", children: detail.icon }), jsxRuntime.jsx("span", { children: detail.label }), detail.helpText && (jsxRuntime.jsx("span", { className: "text-xs text-gray-400 font-normal cursor-help", title: detail.helpText, children: "(?)" }))] }), jsxRuntime.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 ? (jsxRuntime.jsx(CopyableValue, { text: copyText, children: displayValue })) : (displayValue) })] }, detail.label || index));
1430
+ }) }) })) : null] }));
1051
1431
  });
1052
1432
  DetailedInformation.displayName = 'DetailedInformation';
1053
1433
 
@@ -1060,15 +1440,15 @@ const alignmentMap = {
1060
1440
  center: 'items-center justify-center h-full',
1061
1441
  start: 'justify-start h-full',
1062
1442
  end: 'justify-end h-full',
1063
- top: 'items-center justify-center',
1443
+ top: 'items-start justify-center w-full',
1064
1444
  bottom: 'items-end justify-center h-full',
1065
1445
  };
1066
1446
  const panelAlignmentMap = {
1067
- center: 'data-[closed]:scale-95 rounded-lg',
1068
- start: 'data-[closed]:-translate-x-1/2 rounded-r-lg',
1069
- end: 'data-[closed]:translate-x-1/2 rounded-l-lg',
1070
- top: 'data-[closed]:-translate-y-1/2 rounded-b-lg',
1071
- bottom: 'data-[closed]:translate-y-1/2 rounded-t-lg',
1447
+ center: 'data-[closed]:scale-95 rounded-2xl',
1448
+ start: 'data-[closed]:-translate-x-1/2 rounded-r-2xl',
1449
+ end: 'data-[closed]:translate-x-1/2 rounded-l-2xl',
1450
+ top: 'data-[closed]:-translate-y-1/2 rounded-b-2xl',
1451
+ bottom: 'data-[closed]:translate-y-1/2 rounded-t-2xl md:rounded-t-3xl max-h-[92svh]',
1072
1452
  };
1073
1453
  const panelSizeMap = {
1074
1454
  xs: 'max-w-xs',
@@ -1079,15 +1459,56 @@ const panelSizeMap = {
1079
1459
  '2xl': 'max-w-2xl',
1080
1460
  full: 'w-full',
1081
1461
  };
1082
- const Drawer = ({ isOpen, onClose = () => { }, align = 'end', size = 'md', backdrop = true, title, description, children, panelClass = '', contentClass = '', titleClass = '', descriptionClass = '', titleSticky = false, showBackButton = false, }) => {
1083
- const showTitleSection = Boolean(title || description);
1084
- return (jsxRuntime.jsxs(react.Dialog, { open: isOpen, as: "div", className: "relative z-50 focus:outline-none", onClose: onClose, transition: true, children: [backdrop && jsxRuntime.jsx(react.DialogBackdrop, { className: "fixed inset-0 bg-black/20 transition-opacity" }), jsxRuntime.jsx("div", { className: "fixed inset-0 w-screen h-[100svh]", children: jsxRuntime.jsx("div", { className: buildClassName('flex', alignmentMap[align]), children: jsxRuntime.jsxs(react.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 && (jsxRuntime.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: [jsxRuntime.jsxs(react.DialogTitle, { as: "h3", className: buildClassName('text-lg font-medium dark:text-white flex items-center gap-2', titleClass), children: [showBackButton && (jsxRuntime.jsx(Button, { iconOnly: true, "aria-label": "Close drawer", variant: "plain", theme: "secondary", size: "sm", onClick: onClose, children: jsxRuntime.jsx(SvgChevronLeft, { className: "size-5" }) })), title] }), description && (jsxRuntime.jsx("p", { className: buildClassName('text-sm dark:text-white/60', descriptionClass), children: description }))] })), jsxRuntime.jsx("div", { className: contentClass, children: children })] }) }) })] }));
1462
+ 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, }) => {
1463
+ const showTitleSection = Boolean(title || description || showBackButton || showCloseButton);
1464
+ const shouldShowDragHandle = dragHandle !== null && dragHandle !== void 0 ? dragHandle : align === 'bottom';
1465
+ const handleDialogClose = () => {
1466
+ if (closeOnOutsideClick) {
1467
+ onClose === null || onClose === void 0 ? void 0 : onClose();
1468
+ }
1469
+ };
1470
+ return (jsxRuntime.jsxs(react.Dialog, { open: isOpen, as: "div", className: "relative z-50 focus:outline-none", onClose: handleDialogClose, transition: true, children: [backdrop && (jsxRuntime.jsx(react.DialogBackdrop, { className: "fixed inset-0 bg-black/40 backdrop-blur-xs transition-opacity duration-200" })), jsxRuntime.jsx("div", { className: "fixed inset-0 w-screen h-[100svh]", children: jsxRuntime.jsx("div", { className: buildClassName('flex', alignmentMap[align]), children: jsxRuntime.jsxs(react.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 && (jsxRuntime.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 && (jsxRuntime.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: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 min-w-0", children: [jsxRuntime.jsxs("div", { className: "flex items-center gap-2 min-w-0 flex-1", children: [showBackButton && (jsxRuntime.jsx(Button, { iconOnly: true, "aria-label": "Back", variant: "plain", theme: "secondary", size: "xs", onClick: onClose, children: jsxRuntime.jsx(SvgChevronLeft, { className: "size-4" }) })), title && (jsxRuntime.jsx(react.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 && (jsxRuntime.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: jsxRuntime.jsx(SvgX, { className: "size-4" }) }))] }), description && (jsxRuntime.jsx("p", { className: buildClassName('text-sm text-gray-500 dark:text-gray-400 font-normal truncate', descriptionClass), children: description }))] })), jsxRuntime.jsx("div", { className: buildClassName('flex-1 p-4 md:p-6 space-y-4 min-w-0', contentClass), children: children }), footer && (jsxRuntime.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 }))] }) }) })] }));
1085
1471
  };
1086
1472
  Drawer.displayName = 'Drawer';
1087
1473
 
1088
- const BASE_ITEM_CLASS = 'flex w-full px-3 py-2 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700 rounded-lg';
1089
- const Dropdown = ({ triggerButton, renderTriggerButton, items, renderItem, itemsContainerClass, transition = true, anchor = 'bottom start', menuItemClass, onMenuClick, }) => {
1090
- return (jsxRuntime.jsxs(react.Menu, { as: "div", className: "relative inline-block", children: [renderTriggerButton ? (jsxRuntime.jsx(react.MenuButton, { as: React.Fragment, children: (state) => renderTriggerButton(state) })) : (jsxRuntime.jsx(react.MenuButton, { as: React.Fragment, children: triggerButton })), jsxRuntime.jsx(react.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) => (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx(react.MenuItem, { as: "li", disabled: item.disabled, children: ({ disabled }) => (jsxRuntime.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 && (jsxRuntime.jsx("li", { className: "my-1 h-px bg-gray-200 dark:bg-white/5", "data-testid": "dropdown-divider" }, item.id))] }, item.id))) })] }));
1474
+ const WIDTH_CLASS = {
1475
+ auto: 'w-auto min-w-[120px]',
1476
+ sm: 'w-44',
1477
+ md: 'w-56',
1478
+ lg: 'w-64',
1479
+ xl: 'w-72',
1480
+ };
1481
+ const Dropdown = ({ triggerButton, renderTriggerButton, className, items, renderItem, itemsContainerClass, transition = true, anchor = 'bottom start', width = 'md', menuItemClass, onMenuClick, ...restProps }) => {
1482
+ var _a, _b, _c, _d;
1483
+ const widthClass = WIDTH_CLASS[width] || width || WIDTH_CLASS.md;
1484
+ const { theme, variant, size, disabled } = restProps;
1485
+ return (jsxRuntime.jsxs(react.Menu, { as: "div", className: "relative inline-block", children: [renderTriggerButton ? (jsxRuntime.jsx(react.MenuButton, { as: React.Fragment, children: (state) => {
1486
+ const rendered = renderTriggerButton(state);
1487
+ if (React.isValidElement(rendered) && className) {
1488
+ return React.cloneElement(rendered, {
1489
+ className: buildClassName(rendered.props.className, className),
1490
+ });
1491
+ }
1492
+ return rendered;
1493
+ } })) : React.isValidElement(triggerButton) ? (jsxRuntime.jsx(react.MenuButton, { as: React.Fragment, children: React.cloneElement(triggerButton, {
1494
+ className: buildClassName(triggerButton.props.className, className),
1495
+ theme: (_a = triggerButton.props.theme) !== null && _a !== void 0 ? _a : theme,
1496
+ variant: (_b = triggerButton.props.variant) !== null && _b !== void 0 ? _b : variant,
1497
+ size: (_c = triggerButton.props.size) !== null && _c !== void 0 ? _c : size,
1498
+ disabled: (_d = triggerButton.props.disabled) !== null && _d !== void 0 ? _d : disabled,
1499
+ }) })) : (jsxRuntime.jsx(react.MenuButton, { as: React.Fragment, children: triggerButton })), jsxRuntime.jsx(react.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) => (jsxRuntime.jsxs(React.Fragment, { children: [item.groupTitle && (jsxRuntime.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}`)), jsxRuntime.jsx(react.MenuItem, { as: "li", disabled: item.disabled, children: ({ disabled, active }) => (jsxRuntime.jsx("div", { onClick: () => {
1500
+ var _a;
1501
+ if (!disabled) {
1502
+ (_a = item.onClick) === null || _a === void 0 ? void 0 : _a.call(item, item);
1503
+ onMenuClick === null || onMenuClick === void 0 ? void 0 : onMenuClick(item, idx);
1504
+ }
1505
+ }, 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
1506
+ ? buildClassName('text-rose-600 dark:text-rose-400', active
1507
+ ? 'bg-rose-50 dark:bg-rose-950/40'
1508
+ : 'hover:bg-rose-50 dark:hover:bg-rose-950/40')
1509
+ : buildClassName('text-gray-700 dark:text-gray-200', active
1510
+ ? 'bg-gray-100 dark:bg-gray-700/80'
1511
+ : '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)) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5 min-w-0 flex-1", children: [item.icon && (jsxRuntime.jsx("span", { className: buildClassName('shrink-0', item.danger ? 'text-rose-500' : 'text-gray-400 dark:text-gray-500'), children: item.icon })), jsxRuntime.jsxs("div", { className: "flex flex-col min-w-0", children: [item.label && jsxRuntime.jsx("span", { className: "font-medium truncate", children: item.label }), item.description && (jsxRuntime.jsx("span", { className: "text-xs text-gray-400 dark:text-gray-500 font-normal truncate", children: item.description }))] })] }), item.shortcut && (jsxRuntime.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 && (jsxRuntime.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))) })] }));
1091
1512
  };
1092
1513
  Dropdown.displayName = 'Dropdown';
1093
1514
 
@@ -1114,90 +1535,294 @@ const EmptyState = ({ title, description, className, action = null, }) => {
1114
1535
  return (jsxRuntime.jsx("div", { className: buildClassName('w-full flex justify-center px-4 pt-16', className), children: jsxRuntime.jsxs("div", { className: "max-w-md flex flex-col items-center gap-4", children: [jsxRuntime.jsx(SvgFolderPlus, { className: "size-8 text-[var(--ui-text)]" }), jsxRuntime.jsx(HeadingText.SubTitle, { children: title }), description && (jsxRuntime.jsx(BodyText, { as: "p", className: "text-center", muted: true, small: true, children: description })), action] }) }));
1115
1536
  };
1116
1537
 
1117
- function buildInputClass(className, { isFocusWithin = false, disabled = false } = {}) {
1118
- 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
1538
+ const sizeClasses$4 = {
1539
+ sm: {
1540
+ input: 'px-2.5 py-1 text-xs leading-5',
1541
+ group: 'w-8 text-xs',
1542
+ icon: 'size-3.5',
1543
+ text: 'text-xs',
1544
+ },
1545
+ md: {
1546
+ input: 'px-3.5 py-2 text-sm leading-6',
1547
+ group: 'w-10 text-sm',
1548
+ icon: 'size-4',
1549
+ text: 'text-sm',
1550
+ },
1551
+ lg: {
1552
+ input: 'px-4 py-2.5 text-base leading-7',
1553
+ group: 'w-12 text-base',
1554
+ icon: 'size-5',
1555
+ text: 'text-base',
1556
+ },
1557
+ };
1558
+ function buildInputClass(className, { isFocusWithin = false, disabled = false, hasError = false, size = 'md', } = {}) {
1559
+ const currentSize = sizeClasses$4[size] || sizeClasses$4.md;
1560
+ 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
1561
+ ? '!outline-red-500 !ring-1 !ring-red-500/50 dark:!outline-red-500'
1562
+ : 'outline-gray-300 dark:outline-gray-600', disabled
1119
1563
  ? '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'
1120
1564
  : 'bg-white dark:bg-gray-700 dark:text-white outline-gray-300 dark:outline-gray-600 dark:placeholder-gray-400 placeholder-gray-400', isFocusWithin
1121
1565
  ? 'focus-within:outline-[var(--ui-focus-ring)] focus-within:outline-2 focus-within:-outline-offset-2'
1122
1566
  : 'focus:outline-[var(--ui-focus-ring)] focus:outline-2 focus:-outline-offset-2', className);
1123
1567
  }
1124
1568
  const Input = React.forwardRef((props, ref) => {
1125
- const { name, id = name, type, label, labelHint, className, labelClass, labelWrapperClass, containerClass, error, showErrorMessage = true, rightGroup, leftGroup, ...inputProps } = props;
1569
+ 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;
1570
+ const innerRef = React.useRef(null);
1571
+ React.useImperativeHandle(ref, () => innerRef.current);
1572
+ const [uncontrolledValue, setUncontrolledValue] = React.useState(String(defaultValue !== null && defaultValue !== void 0 ? defaultValue : ''));
1573
+ const isControlled = value !== undefined;
1574
+ const currentVal = isControlled ? String(value !== null && value !== void 0 ? value : '') : uncontrolledValue;
1575
+ const charLength = currentVal.length;
1576
+ const handleInputChange = (e) => {
1577
+ if (!isControlled) {
1578
+ setUncontrolledValue(e.target.value);
1579
+ }
1580
+ onChange === null || onChange === void 0 ? void 0 : onChange(e);
1581
+ };
1582
+ const handleClear = () => {
1583
+ if (innerRef.current) {
1584
+ innerRef.current.value = '';
1585
+ const event = new Event('input', {
1586
+ bubbles: true,
1587
+ });
1588
+ Object.defineProperty(event, 'target', { writable: true, value: { value: '' } });
1589
+ if (!isControlled) {
1590
+ setUncontrolledValue('');
1591
+ }
1592
+ onChange === null || onChange === void 0 ? void 0 : onChange(event);
1593
+ }
1594
+ onClear === null || onClear === void 0 ? void 0 : onClear();
1595
+ };
1126
1596
  const hasError = Boolean(error);
1127
- const hasRightGroup = rightGroup !== null && rightGroup !== undefined;
1597
+ const currentSize = sizeClasses$4[size] || sizeClasses$4.md;
1598
+ const showClearButton = clearable && charLength > 0 && !inputProps.disabled;
1599
+ const actualRightGroup = rightGroup !== null && rightGroup !== void 0 ? rightGroup : (showClearButton ? (jsxRuntime.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: jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: currentSize.icon, children: jsxRuntime.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);
1600
+ const hasRightGroup = actualRightGroup !== null && actualRightGroup !== undefined;
1128
1601
  const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
1129
- return (jsxRuntime.jsxs("div", { className: buildClassName('group', hasError && 'has-error', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between mb-2', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, htmlFor: id, children: label }), jsxRuntime.jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxRuntime.jsxs("div", { className: "relative", children: [hasLeftGroup && (jsxRuntime.jsx("span", { className: "absolute top-0 h-full flex items-center justify-center w-10", children: leftGroup })), jsxRuntime.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 && (jsxRuntime.jsx("span", { className: "absolute end-0.75 top-0 h-full flex items-center justify-center w-10", children: rightGroup }))] }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) }))] }));
1602
+ return (jsxRuntime.jsxs("div", { className: buildClassName('group', hasError && 'has-error', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between mb-1.5', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, htmlFor: id, children: label }), labelHint && jsxRuntime.jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxRuntime.jsxs("div", { className: "flex rounded-lg shadow-2xs", children: [prefix && (jsxRuntime.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 })), jsxRuntime.jsxs("div", { className: "relative flex-1 min-w-0", children: [hasLeftGroup && (jsxRuntime.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 })), jsxRuntime.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, {
1603
+ disabled: inputProps.disabled,
1604
+ hasError,
1605
+ size,
1606
+ }), 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 && (jsxRuntime.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 && (jsxRuntime.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 ? (jsxRuntime.jsxs("div", { className: "mt-1.5 flex items-center justify-between gap-2 text-xs", children: [jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: showErrorMessage && error ? (jsxRuntime.jsx(TextContent, { error: true, small: true, children: error })) : helperText ? (jsxRuntime.jsx(TextContent, { muted: true, small: true, children: helperText })) : null }), showCount && (jsxRuntime.jsxs("span", { className: buildClassName('text-[11px] font-medium shrink-0 ml-auto', maxLength && charLength >= maxLength
1607
+ ? 'text-red-500 font-semibold'
1608
+ : 'text-gray-400 dark:text-gray-500'), children: [charLength, maxLength ? ` / ${maxLength}` : ''] }))] })) : null] }));
1130
1609
  });
1131
1610
  Input.displayName = 'Input';
1132
1611
 
1133
1612
  const PASSWORD_TYPE = 'password';
1134
1613
  const PASSWORD_PLACEHOLDER = '••••••••••';
1135
- const PasswordInput = (props) => {
1136
- const { ...inputProps } = props;
1614
+ function evaluatePasswordStrength(val) {
1615
+ if (!val)
1616
+ return { score: 0, label: 'Weak', colorClass: 'bg-gray-200 dark:bg-gray-700', percent: 0 };
1617
+ let score = 0;
1618
+ if (val.length >= 8)
1619
+ score += 1;
1620
+ if (val.length >= 12)
1621
+ score += 1;
1622
+ if (/[0-9]/.test(val))
1623
+ score += 1;
1624
+ if (/[a-z]/.test(val) && /[A-Z]/.test(val))
1625
+ score += 1;
1626
+ if (/[^A-Za-z0-9]/.test(val))
1627
+ score += 1;
1628
+ if (score <= 1) {
1629
+ return { score: 1, label: 'Weak', colorClass: 'bg-red-500', percent: 25 };
1630
+ }
1631
+ if (score === 2) {
1632
+ return { score: 2, label: 'Fair', colorClass: 'bg-amber-500', percent: 50 };
1633
+ }
1634
+ if (score === 3) {
1635
+ return { score: 3, label: 'Good', colorClass: 'bg-blue-500', percent: 75 };
1636
+ }
1637
+ return { score: 4, label: 'Strong', colorClass: 'bg-emerald-500', percent: 100 };
1638
+ }
1639
+ const PasswordInput = React.forwardRef((props, ref) => {
1640
+ const { showStrength = false, placeholder = PASSWORD_PLACEHOLDER, onChange, value, defaultValue, ...inputProps } = props;
1137
1641
  const [showPassword, setShowPassword] = React.useState(false);
1138
- return (jsxRuntime.jsx(Input, { ...inputProps, type: showPassword ? 'text' : PASSWORD_TYPE, placeholder: PASSWORD_PLACEHOLDER, rightGroup: jsxRuntime.jsx(Button, { theme: "secondary", variant: "plain", size: "xs", className: "px-1 mr-1", type: "button", onClick: () => setShowPassword(() => !showPassword), tabIndex: -1, children: showPassword ? (jsxRuntime.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: jsxRuntime.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" }) })) : (jsxRuntime.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: [jsxRuntime.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" }), jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" })] })) }) }));
1139
- };
1642
+ const [uncontrolledValue, setUncontrolledValue] = React.useState(String(defaultValue !== null && defaultValue !== void 0 ? defaultValue : ''));
1643
+ const isControlled = value !== undefined;
1644
+ const currentVal = isControlled ? String(value !== null && value !== void 0 ? value : '') : uncontrolledValue;
1645
+ const handleInputChange = (e) => {
1646
+ if (!isControlled) {
1647
+ setUncontrolledValue(e.target.value);
1648
+ }
1649
+ onChange === null || onChange === void 0 ? void 0 : onChange(e);
1650
+ };
1651
+ const strength = evaluatePasswordStrength(currentVal);
1652
+ return (jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [jsxRuntime.jsx(Input, { ref: ref, ...inputProps, value: value, defaultValue: defaultValue, onChange: handleInputChange, type: showPassword ? 'text' : PASSWORD_TYPE, placeholder: placeholder, rightGroup: jsxRuntime.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 ? (jsxRuntime.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: jsxRuntime.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" }) })) : (jsxRuntime.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: [jsxRuntime.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" }), jsxRuntime.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 && (jsxRuntime.jsxs("div", { className: "pt-0.5 space-y-1", children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-[11px] font-medium text-gray-500 dark:text-gray-400", children: [jsxRuntime.jsx("span", { children: "Password Strength" }), jsxRuntime.jsx("span", { className: "font-semibold text-gray-700 dark:text-gray-200", children: strength.label })] }), jsxRuntime.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: jsxRuntime.jsx("div", { className: `h-full transition-all duration-300 rounded-full ${strength.colorClass}`, style: { width: `${strength.percent}%` } }) })] }))] }));
1653
+ });
1654
+ PasswordInput.displayName = 'PasswordInput';
1140
1655
 
1656
+ const SIZE_MAP = {
1657
+ sm: 'size-4',
1658
+ md: 'size-4.5',
1659
+ lg: 'size-5.5',
1660
+ };
1661
+ const THEME_MAP = {
1662
+ primary: 'checked:border-[var(--ui-primary)] checked:hover:border-[var(--ui-primary)] focus:ring-[var(--ui-primary-ring)]',
1663
+ success: 'checked:border-emerald-600 checked:hover:border-emerald-600 focus:ring-emerald-500',
1664
+ danger: 'checked:border-[var(--ui-danger)] checked:hover:border-[var(--ui-danger)] focus:ring-[var(--ui-danger-ring)]',
1665
+ };
1141
1666
  const Radio = React.forwardRef((props, ref) => {
1142
- var _a;
1143
- const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '',
1144
- // RadioButton-only props removed from rest
1145
- ...rest } = props;
1667
+ const { id, name, label, description, size = 'md', theme = 'primary', labelClass = '', descriptionClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '', ...rest } = props;
1146
1668
  const generatedId = React.useId();
1147
- const radioId = (_a = id !== null && id !== void 0 ? id : name) !== null && _a !== void 0 ? _a : generatedId;
1148
- return (jsxRuntime.jsxs("div", { className: buildClassName(wrapperClass), children: [jsxRuntime.jsxs("div", { className: buildClassName('flex items-center gap-2', containerClass), children: [jsxRuntime.jsx("div", { className: "grid place-items-center", children: jsxRuntime.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
1669
+ const radioId = id !== null && id !== void 0 ? id : (name && rest.value ? `${name}-${rest.value}` : generatedId);
1670
+ return (jsxRuntime.jsxs("div", { className: buildClassName(wrapperClass), children: [jsxRuntime.jsxs("div", { className: buildClassName('flex items-start gap-2.5', containerClass), children: [jsxRuntime.jsx("div", { className: "grid place-items-center shrink-0 pt-0.5", children: jsxRuntime.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
1149
1671
  ? 'border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-gray-800 pointer-events-none'
1150
- : 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 && (jsxRuntime.jsx(Label, { htmlFor: radioId, className: buildClassName(disabled
1151
- ? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
1152
- : 'cursor-pointer text-gray-600 dark:text-gray-300', labelClass), children: label }))] }), error && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) }))] }));
1672
+ : '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) && (jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5 min-w-0", children: [label && (jsxRuntime.jsx(Label, { htmlFor: radioId, className: buildClassName(disabled
1673
+ ? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
1674
+ : 'cursor-pointer text-gray-700 dark:text-gray-200 font-medium', labelClass), children: label })), description && (jsxRuntime.jsx("p", { className: buildClassName('text-xs leading-normal', disabled
1675
+ ? 'text-gray-300 dark:text-gray-600'
1676
+ : 'text-gray-500 dark:text-gray-400', descriptionClass), children: description }))] }))] }), error && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) }))] }));
1153
1677
  });
1678
+ Radio.displayName = 'Radio';
1154
1679
 
1680
+ const COLUMN_GRID_MAP = {
1681
+ 1: 'grid-cols-1',
1682
+ 2: 'grid-cols-1 md:grid-cols-2',
1683
+ 3: 'grid-cols-1 md:grid-cols-3',
1684
+ 4: 'grid-cols-1 md:grid-cols-4',
1685
+ };
1155
1686
  const RadioGroup = (props) => {
1156
- const { name, label, labelClass, labelWrapperClass, labelHint, containerClass, options, value, row, error, showErrorMessage, onChange, } = props;
1687
+ const { name, variant = 'default', size = 'md', theme = 'primary', label, labelClass, labelWrapperClass, labelHint, containerClass, options, value, columns, disabled = false, row, error, showErrorMessage = true, onChange, } = props;
1157
1688
  const items = React.useMemo(() => {
1158
1689
  var _a;
1159
- 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 : [];
1690
+ return (_a = options === null || options === void 0 ? void 0 : options.map((option) => typeof option === 'string'
1691
+ ? {
1692
+ label: option,
1693
+ value: option,
1694
+ description: undefined,
1695
+ icon: undefined,
1696
+ disabled: undefined,
1697
+ }
1698
+ : option)) !== null && _a !== void 0 ? _a : [];
1160
1699
  }, [options]);
1161
- return (jsxRuntime.jsxs("div", { className: buildClassName(containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between mb-4', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, children: label }), labelHint] })), jsxRuntime.jsx("div", { className: buildClassName('flex gap-4', row ? 'flex-row items-center' : 'flex-col items-start'), children: items.map((item) => (jsxRuntime.jsx(Radio, { name: name, id: `${name}-${item.value}`, label: item.label, value: item.value, checked: value === item.value, onChange: onChange }, item.value))) }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) }))] }));
1700
+ const isCards = variant === 'cards';
1701
+ return (jsxRuntime.jsxs("div", { className: buildClassName(containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between mb-3', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, children: label }), labelHint] })), jsxRuntime.jsx("div", { className: buildClassName(columns
1702
+ ? buildClassName('grid gap-3', COLUMN_GRID_MAP[columns])
1703
+ : row || isCards
1704
+ ? 'flex flex-wrap gap-3'
1705
+ : 'flex flex-col gap-3'), children: items.map((item) => {
1706
+ const isSelected = value === item.value;
1707
+ const isItemDisabled = disabled || Boolean(item.disabled);
1708
+ if (isCards) {
1709
+ return (jsxRuntime.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
1710
+ ? 'border-[var(--ui-primary)] bg-blue-50/40 dark:bg-blue-950/30 ring-2 ring-[var(--ui-primary-ring)]/30'
1711
+ : 'border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 hover:border-gray-300 dark:hover:border-gray-600', isItemDisabled &&
1712
+ 'opacity-50 cursor-not-allowed pointer-events-none bg-gray-50 dark:bg-gray-800/50'), children: [jsxRuntime.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" }), jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [item.icon && (jsxRuntime.jsx("span", { className: "shrink-0 text-gray-500 dark:text-gray-400", children: item.icon })), jsxRuntime.jsx("span", { className: "text-sm font-semibold text-gray-900 dark:text-white truncate", children: item.label })] }), item.description && (jsxRuntime.jsx("p", { className: "mt-1 text-xs text-gray-500 dark:text-gray-400 font-normal leading-relaxed", children: item.description }))] })] }, item.value));
1713
+ }
1714
+ return (jsxRuntime.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));
1715
+ }) }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1.5", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) }))] }));
1162
1716
  };
1717
+ RadioGroup.displayName = 'RadioGroup';
1163
1718
 
1164
- /**
1165
- * GPU-accelerated Radio Switch with perfect highlight alignment.
1166
- * Supports wrapping, icons, descriptions, and dark mode.
1167
- */
1168
- const RadioSwitch = ({ label, items, selected, onChange, wrapperClass, switchClass, contentClass, descriptionClass, }) => {
1719
+ const SIZE_CONTAINER_CLASS$1 = {
1720
+ sm: 'p-1.5 gap-1 rounded-lg text-xs',
1721
+ md: 'p-2 gap-1.5 rounded-xl text-sm',
1722
+ lg: 'p-2 gap-2 rounded-2xl text-base',
1723
+ };
1724
+ const SIZE_ITEM_CLASS = {
1725
+ sm: 'px-4 py-2 text-xs rounded-md min-w-[64px]',
1726
+ md: 'px-5 py-2.5 text-sm rounded-lg min-w-[76px]',
1727
+ lg: 'px-7 py-3 text-base rounded-xl min-w-[92px]',
1728
+ };
1729
+ const SIZE_ITEM_DESC_CLASS = {
1730
+ sm: 'px-4 py-2.5 text-xs rounded-md min-w-[80px]',
1731
+ md: 'px-5 py-3 text-sm rounded-lg min-w-[96px]',
1732
+ lg: 'px-7 py-3.5 text-base rounded-xl min-w-[112px]',
1733
+ };
1734
+ const THEME_SWITCH_CLASS = {
1735
+ primary: 'bg-[var(--ui-primary)] text-white shadow-xs',
1736
+ secondary: 'bg-white dark:bg-gray-700 text-gray-900 dark:text-white shadow-xs',
1737
+ dark: 'bg-gray-900 dark:bg-black text-white shadow-xs',
1738
+ };
1739
+ const RadioSwitch = ({ label, items, selected, size = 'md', theme = 'primary', disabled = false, onChange, wrapperClass, switchClass, contentClass, descriptionClass, }) => {
1169
1740
  const containerRef = React.useRef(null);
1170
- const [style, setStyle] = React.useState({ left: 0, top: 0, width: 0, height: 0 });
1741
+ const [style, setStyle] = React.useState({ left: 0, top: 0, width: 0, height: 0, ready: false });
1171
1742
  const getLabel = (i) => (typeof i === 'string' ? i : i.label);
1172
1743
  const getValue = (i) => (typeof i === 'string' ? i : i.value);
1173
1744
  const getDescription = (i) => { var _a; return typeof i === 'string' ? '' : ((_a = i.description) !== null && _a !== void 0 ? _a : ''); };
1174
- /** ---------------------------------------------
1175
- * Update highlight when selected changes
1176
- * ----------------------------------------------*/
1177
- React.useEffect(() => {
1745
+ const isItemDisabled = (i) => disabled || (typeof i === 'string' ? false : Boolean(i.disabled));
1746
+ const hasDescription = items.some((i) => typeof i !== 'string' && Boolean(i.description));
1747
+ // Position calculation for GPU sliding highlight
1748
+ React.useLayoutEffect(() => {
1178
1749
  if (!containerRef.current)
1179
1750
  return;
1180
- const buttons = Array.from(containerRef.current.querySelectorAll('[data-radio-item]'));
1181
- const el = buttons.find((b) => b.dataset.value === selected);
1182
- if (!el)
1751
+ const updatePosition = () => {
1752
+ if (!containerRef.current)
1753
+ return;
1754
+ const activeEl = containerRef.current.querySelector('[data-selected="true"]');
1755
+ if (!activeEl)
1756
+ return;
1757
+ const parentRect = containerRef.current.getBoundingClientRect();
1758
+ const rect = activeEl.getBoundingClientRect();
1759
+ const left = activeEl.offsetLeft || rect.left - parentRect.left;
1760
+ const top = activeEl.offsetTop || rect.top - parentRect.top;
1761
+ const width = activeEl.offsetWidth || rect.width;
1762
+ const height = activeEl.offsetHeight || rect.height;
1763
+ setStyle({
1764
+ left,
1765
+ top,
1766
+ width,
1767
+ height,
1768
+ ready: true,
1769
+ });
1770
+ };
1771
+ updatePosition();
1772
+ const resizeObserver = new ResizeObserver(updatePosition);
1773
+ resizeObserver.observe(containerRef.current);
1774
+ return () => resizeObserver.disconnect();
1775
+ }, [selected, items, size]);
1776
+ // ARIA Keyboard navigation handler
1777
+ const handleGroupKeyDown = (e) => {
1778
+ const enabledItems = items.filter((it) => !isItemDisabled(it));
1779
+ if (enabledItems.length === 0)
1183
1780
  return;
1184
- const rect = el.getBoundingClientRect();
1185
- const parentRect = containerRef.current.getBoundingClientRect();
1186
- const left = rect.left - parentRect.left;
1187
- const top = rect.top - parentRect.top;
1188
- setStyle({
1189
- left,
1190
- top,
1191
- width: rect.width,
1192
- height: rect.height,
1193
- });
1194
- }, [selected, items]);
1195
- return (jsxRuntime.jsxs(react.RadioGroup, { 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: [jsxRuntime.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: {
1781
+ const currentVal = selected;
1782
+ const currentEnabledIndex = enabledItems.findIndex((it) => getValue(it) === currentVal);
1783
+ let nextItem = null;
1784
+ if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
1785
+ e.preventDefault();
1786
+ const nextIdx = (currentEnabledIndex + 1) % enabledItems.length;
1787
+ nextItem = enabledItems[nextIdx];
1788
+ }
1789
+ else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
1790
+ e.preventDefault();
1791
+ const prevIdx = (currentEnabledIndex - 1 + enabledItems.length) % enabledItems.length;
1792
+ nextItem = enabledItems[prevIdx];
1793
+ }
1794
+ else if (e.key === 'Home') {
1795
+ e.preventDefault();
1796
+ nextItem = enabledItems[0];
1797
+ }
1798
+ else if (e.key === 'End') {
1799
+ e.preventDefault();
1800
+ nextItem = enabledItems[enabledItems.length - 1];
1801
+ }
1802
+ if (nextItem) {
1803
+ onChange(getValue(nextItem));
1804
+ }
1805
+ };
1806
+ return (jsxRuntime.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: [jsxRuntime.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: {
1196
1807
  transform: `translate(${style.left}px, ${style.top}px)`,
1197
1808
  width: style.width,
1198
1809
  height: style.height,
1199
- } }), items.map((item) => (jsxRuntime.jsx(react.Radio, { 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: jsxRuntime.jsxs("div", { className: buildClassName('flex flex-col items-center justify-center gap-1', contentClass), children: [jsxRuntime.jsx(BodyText, { className: buildClassName('font-semibold', selected === getValue(item) && 'text-white'), children: getLabel(item) }), getDescription(item) && (jsxRuntime.jsx(BodyText, { className: buildClassName('text-xs opacity-80', descriptionClass, selected === getValue(item) && 'text-white'), children: getDescription(item) }))] }) }, getValue(item))))] }));
1810
+ } }), items.map((item) => {
1811
+ const itemVal = getValue(item);
1812
+ const isSelected = selected === itemVal;
1813
+ const disabledState = isItemDisabled(item);
1814
+ return (jsxRuntime.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: () => {
1815
+ if (!disabledState) {
1816
+ onChange(itemVal);
1817
+ }
1818
+ }, 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
1819
+ ? theme === 'secondary'
1820
+ ? 'text-gray-900 dark:text-white'
1821
+ : 'text-white'
1822
+ : 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) ? (jsxRuntime.jsxs("div", { className: buildClassName('flex flex-col items-center justify-center gap-1', contentClass), children: [jsxRuntime.jsx("span", { className: "font-semibold leading-tight text-center truncate", children: getLabel(item) }), jsxRuntime.jsx("span", { className: buildClassName('text-[12px] leading-tight opacity-80 font-normal text-center truncate', descriptionClass), children: getDescription(item) })] })) : (jsxRuntime.jsx("span", { className: buildClassName('truncate leading-tight text-center', contentClass), children: getLabel(item) })) }, itemVal));
1823
+ })] }));
1200
1824
  };
1825
+ RadioSwitch.displayName = 'RadioSwitch';
1201
1826
 
1202
1827
  /**
1203
1828
  * Checks if a value is an array.
@@ -1227,30 +1852,86 @@ function isEmpty(value) {
1227
1852
  return false; // for numbers, booleans, functions, etc.
1228
1853
  }
1229
1854
 
1230
- // export interface RangeInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {
1231
- // label?: string
1232
- // labelClass?: string
1233
- // labelHint?: React.ReactNode
1234
- // containerClass?: string
1235
- // showValue?: boolean
1236
- // valueSuffix?: string
1237
- // error?: string | null
1238
- // showErrorMessage?: boolean
1239
- // }
1855
+ const sizeClasses$3 = {
1856
+ sm: {
1857
+ track: 'h-1.5',
1858
+ 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',
1859
+ text: 'text-xs',
1860
+ },
1861
+ md: {
1862
+ track: 'h-2',
1863
+ 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',
1864
+ text: 'text-sm',
1865
+ },
1866
+ lg: {
1867
+ track: 'h-3',
1868
+ thumb: '[&::-webkit-slider-thumb]:h-6 [&::-webkit-slider-thumb]:w-6 [&::-moz-range-thumb]:h-6 [&::-moz-range-thumb]:w-6',
1869
+ text: 'text-base',
1870
+ },
1871
+ };
1240
1872
  const RangeInput = React.forwardRef((props, ref) => {
1241
- const { label, labelClass = '', labelHint = null, containerClass = '', className = '', showValue = true, valueSuffix = '', error = null, showErrorMessage = true, id, min = 0, max = 100, value, ...inputProps } = props;
1873
+ 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;
1874
+ const [hovering, setHovering] = React.useState(false);
1875
+ const [internalValue, setInternalValue] = React.useState(Number(defaultValue !== null && defaultValue !== void 0 ? defaultValue : min));
1876
+ const isControlled = value !== undefined;
1877
+ const currentVal = Number(isControlled ? value : internalValue);
1878
+ const numMin = Number(min);
1879
+ const numMax = Number(max);
1880
+ const percent = numMax > numMin ? ((currentVal - numMin) / (numMax - numMin)) * 100 : 0;
1881
+ const currentSize = sizeClasses$3[size] || sizeClasses$3.md;
1242
1882
  const hasError = !isEmpty(error);
1243
- return (jsxRuntime.jsxs("div", { className: buildClassName('group space-y-2', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [jsxRuntime.jsx(Label, { htmlFor: id, className: labelClass, children: label }), labelHint] })), jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [jsxRuntime.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
1244
- '[&::-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 && (jsxRuntime.jsxs("span", { className: "text-sm text-gray-600 dark:text-gray-300 min-w-[3rem] text-right", children: [value, valueSuffix] }))] }), showErrorMessage && hasError && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) }))] }));
1883
+ const handleSliderChange = (e) => {
1884
+ if (!isControlled) {
1885
+ setInternalValue(Number(e.target.value));
1886
+ }
1887
+ onChange === null || onChange === void 0 ? void 0 : onChange(e);
1888
+ };
1889
+ // Parse custom marks if object provided
1890
+ const markEntries = typeof marks === 'object' && marks !== null
1891
+ ? Object.entries(marks).map(([k, v]) => ({ value: Number(k), label: v }))
1892
+ : typeof marks === 'boolean' && marks
1893
+ ? [
1894
+ { value: numMin, label: String(numMin) },
1895
+ { value: (numMin + numMax) / 2, label: String(Math.round((numMin + numMax) / 2)) },
1896
+ { value: numMax, label: String(numMax) },
1897
+ ]
1898
+ : null;
1899
+ return (jsxRuntime.jsxs("div", { className: buildClassName('group space-y-2', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-1", children: [jsxRuntime.jsx(Label, { htmlFor: id, className: labelClass, children: label }), labelHint] })), jsxRuntime.jsxs("div", { className: "relative flex items-center gap-3", children: [jsxRuntime.jsxs("div", { className: "relative flex-1 py-2", children: [showTooltip && hovering && (jsxRuntime.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, jsxRuntime.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" })] })), jsxRuntime.jsx("input", { ref: ref, type: "range", id: id, min: min, max: max, value: currentVal, onChange: handleSliderChange, onMouseEnter: () => setHovering(true), onMouseLeave: () => setHovering(false), ...inputProps, style: {
1900
+ 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%)`,
1901
+ }, 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
1902
+ ? '[&::-webkit-slider-thumb]:border-red-600 [&::-moz-range-thumb]:border-red-600'
1903
+ : '[&::-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 && (jsxRuntime.jsx("div", { className: "relative w-full h-4 mt-1", children: markEntries.map((m) => {
1904
+ const markPercent = ((m.value - numMin) / (numMax - numMin)) * 100;
1905
+ return (jsxRuntime.jsxs("div", { className: "absolute top-0 transform -translate-x-1/2 flex flex-col items-center", style: { left: `${markPercent}%` }, children: [jsxRuntime.jsx("span", { className: "w-1 h-1.5 bg-gray-300 dark:bg-gray-600 rounded-full" }), jsxRuntime.jsx("span", { className: "text-[10px] font-medium text-gray-400 dark:text-gray-500 mt-0.5", children: m.label })] }, m.value));
1906
+ }) }))] }), showValue && (jsxRuntime.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 ? (jsxRuntime.jsx("div", { className: "mt-1", children: showErrorMessage && hasError ? (jsxRuntime.jsx(TextContent, { error: true, small: true, children: error })) : helperText ? (jsxRuntime.jsx(TextContent, { muted: true, small: true, children: helperText })) : null })) : null] }));
1245
1907
  });
1246
-
1247
- const RangeSlider = ({ label, labelHint, labelClass = '', containerClass = '', className = '', min, max, valueMin, valueMax, onChange, valueSuffix = '', error = null, showErrorMessage = true, }) => {
1908
+ RangeInput.displayName = 'RangeInput';
1909
+
1910
+ const sizeClasses$2 = {
1911
+ sm: {
1912
+ track: 'h-1.5',
1913
+ thumb: 'h-4 w-4',
1914
+ thumbPx: 16,
1915
+ text: 'text-xs',
1916
+ },
1917
+ md: {
1918
+ track: 'h-2',
1919
+ thumb: 'h-5 w-5',
1920
+ thumbPx: 20,
1921
+ text: 'text-sm',
1922
+ },
1923
+ lg: {
1924
+ track: 'h-3',
1925
+ thumb: 'h-6 w-6',
1926
+ thumbPx: 24,
1927
+ text: 'text-base',
1928
+ },
1929
+ };
1930
+ 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, }) => {
1248
1931
  const trackRef = React.useRef(null);
1249
- // Refs for drag logic
1250
1932
  const activeThumbRef = React.useRef(null);
1251
- // State for UI styling
1252
1933
  const [activeThumbState, setActiveThumbState] = React.useState(null);
1253
- // Keep prop values in refs so drag handlers always use latest values (no stale closure)
1934
+ const [hoveringThumb, setHoveringThumb] = React.useState(null);
1254
1935
  const valueMinRef = React.useRef(valueMin);
1255
1936
  const valueMaxRef = React.useRef(valueMax);
1256
1937
  const onChangeRef = React.useRef(onChange);
@@ -1258,20 +1939,28 @@ const RangeSlider = ({ label, labelHint, labelClass = '', containerClass = '', c
1258
1939
  valueMaxRef.current = valueMax;
1259
1940
  onChangeRef.current = onChange;
1260
1941
  const hasError = !isEmpty(error);
1261
- const percent = (v) => ((v - min) / (max - min)) * 100;
1262
- // Stable handler references registered on window — these never change identity
1263
- // so removeEventListener always finds the right listener to remove.
1942
+ const currentSize = sizeClasses$2[size] || sizeClasses$2.md;
1943
+ const clampValue = (val) => Math.min(Math.max(val, min), max);
1944
+ const snapToStep = (val) => {
1945
+ const clamped = clampValue(val);
1946
+ const steps = Math.round((clamped - min) / step);
1947
+ return Math.min(Math.max(min + steps * step, min), max);
1948
+ };
1949
+ const percent = (v) => ((clampValue(v) - min) / (max - min)) * 100;
1264
1950
  const stablePointerMoveRef = React.useRef((e) => {
1265
1951
  if (!trackRef.current || !activeThumbRef.current)
1266
1952
  return;
1267
1953
  const rect = trackRef.current.getBoundingClientRect();
1268
1954
  const x = Math.min(Math.max(e.clientX - rect.left, 0), rect.width);
1269
1955
  const rawVal = min + (x / rect.width) * (max - min);
1956
+ const snappedVal = snapToStep(rawVal);
1270
1957
  if (activeThumbRef.current === 'min') {
1271
- onChangeRef.current(Math.min(rawVal, valueMaxRef.current - 1), valueMaxRef.current);
1958
+ const nextMin = Math.min(snappedVal, valueMaxRef.current - step);
1959
+ onChangeRef.current(nextMin, valueMaxRef.current);
1272
1960
  }
1273
1961
  else {
1274
- onChangeRef.current(valueMinRef.current, Math.max(rawVal, valueMinRef.current + 1));
1962
+ const nextMax = Math.max(snappedVal, valueMinRef.current + step);
1963
+ onChangeRef.current(valueMinRef.current, nextMax);
1275
1964
  }
1276
1965
  });
1277
1966
  const stablePointerUpRef = React.useRef(() => {
@@ -1280,7 +1969,6 @@ const RangeSlider = ({ label, labelHint, labelClass = '', containerClass = '', c
1280
1969
  window.removeEventListener('pointermove', stablePointerMoveRef.current);
1281
1970
  window.removeEventListener('pointerup', stablePointerUpRef.current);
1282
1971
  });
1283
- // Remove listeners on unmount in case a drag is in progress
1284
1972
  React.useEffect(() => {
1285
1973
  const move = stablePointerMoveRef.current;
1286
1974
  const up = stablePointerUpRef.current;
@@ -1289,59 +1977,91 @@ const RangeSlider = ({ label, labelHint, labelClass = '', containerClass = '', c
1289
1977
  window.removeEventListener('pointerup', up);
1290
1978
  };
1291
1979
  }, []);
1292
- // ---------------------------------------------
1293
- // Pointer Move (drag)
1294
- // ---------------------------------------------
1295
1980
  const startDrag = (thumb) => (_e) => {
1296
1981
  activeThumbRef.current = thumb;
1297
1982
  setActiveThumbState(thumb);
1298
1983
  window.addEventListener('pointermove', stablePointerMoveRef.current);
1299
1984
  window.addEventListener('pointerup', stablePointerUpRef.current);
1300
1985
  };
1301
- // ---------------------------------------------
1302
- // Click Track to move closest thumb
1303
- // ---------------------------------------------
1304
1986
  const handleTrackClick = (e) => {
1305
1987
  if (!trackRef.current)
1306
1988
  return;
1307
1989
  const rect = trackRef.current.getBoundingClientRect();
1308
1990
  const x = Math.min(Math.max(e.clientX - rect.left, 0), rect.width);
1309
1991
  const rawVal = min + (x / rect.width) * (max - min);
1992
+ const snappedVal = snapToStep(rawVal);
1310
1993
  const pxMin = (percent(valueMin) / 100) * rect.width;
1311
1994
  const pxMax = (percent(valueMax) / 100) * rect.width;
1312
- // If click is left of min thumb → move min thumb
1313
1995
  if (x <= pxMin) {
1314
- onChange(rawVal, valueMax);
1996
+ onChange(Math.min(snappedVal, valueMax - step), valueMax);
1315
1997
  return;
1316
1998
  }
1317
- // If click is right of max thumb → move max thumb
1318
1999
  if (x >= pxMax) {
1319
- onChange(valueMin, rawVal);
2000
+ onChange(valueMin, Math.max(snappedVal, valueMin + step));
1320
2001
  return;
1321
2002
  }
1322
- // Else click is between → move closest thumb
1323
- const distMin = Math.abs(rawVal - valueMin);
1324
- const distMax = Math.abs(rawVal - valueMax);
2003
+ const distMin = Math.abs(snappedVal - valueMin);
2004
+ const distMax = Math.abs(snappedVal - valueMax);
1325
2005
  if (distMin < distMax) {
1326
- onChange(rawVal, valueMax);
2006
+ onChange(Math.min(snappedVal, valueMax - step), valueMax);
1327
2007
  }
1328
2008
  else {
1329
- onChange(valueMin, rawVal);
2009
+ onChange(valueMin, Math.max(snappedVal, valueMin + step));
1330
2010
  }
1331
2011
  };
1332
- return (jsxRuntime.jsxs("div", { className: buildClassName('group space-y-2', hasError && 'has-error', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-1", children: [jsxRuntime.jsx(Label, { className: labelClass, children: label }), labelHint] })), jsxRuntime.jsxs("div", { ref: trackRef, onMouseDown: handleTrackClick, className: "relative h-8 select-none w-full", "data-testid": "range-track", children: [jsxRuntime.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" }), jsxRuntime.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: {
2012
+ // Keyboard accessibility handler for thumbs
2013
+ const handleKeyDown = (thumb) => (e) => {
2014
+ let delta = 0;
2015
+ if (e.key === 'ArrowLeft' || e.key === 'ArrowDown')
2016
+ delta = -step;
2017
+ else if (e.key === 'ArrowRight' || e.key === 'ArrowUp')
2018
+ delta = step;
2019
+ else if (e.key === 'Home') {
2020
+ if (thumb === 'min')
2021
+ onChange(min, valueMax);
2022
+ else
2023
+ onChange(valueMin, valueMin + step);
2024
+ return;
2025
+ }
2026
+ else if (e.key === 'End') {
2027
+ if (thumb === 'min')
2028
+ onChange(valueMax - step, valueMax);
2029
+ else
2030
+ onChange(valueMin, max);
2031
+ return;
2032
+ }
2033
+ if (delta !== 0) {
2034
+ e.preventDefault();
2035
+ if (thumb === 'min') {
2036
+ const next = snapToStep(valueMin + delta);
2037
+ onChange(Math.min(next, valueMax - step), valueMax);
2038
+ }
2039
+ else {
2040
+ const next = snapToStep(valueMax + delta);
2041
+ onChange(valueMin, Math.max(next, valueMin + step));
2042
+ }
2043
+ }
2044
+ };
2045
+ const markEntries = marks
2046
+ ? Object.entries(marks).map(([k, v]) => ({ value: Number(k), label: v }))
2047
+ : null;
2048
+ return (jsxRuntime.jsxs("div", { className: buildClassName('group space-y-2', hasError && 'has-error', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-1", children: [jsxRuntime.jsx(Label, { className: labelClass, children: label }), labelHint] })), jsxRuntime.jsxs("div", { ref: trackRef, onMouseDown: handleTrackClick, className: "relative h-8 select-none w-full flex items-center cursor-pointer", "data-testid": "range-track", children: [jsxRuntime.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) }), jsxRuntime.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: {
1333
2049
  left: `${percent(valueMin)}%`,
1334
2050
  width: `${percent(valueMax) - percent(valueMin)}%`,
1335
- } }), jsxRuntime.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
1336
- activeThumbState === 'min' && 'ring-4 ring-blue-200', className), style: {
1337
- left: `calc(${percent(valueMin)}% - 10px)`,
2051
+ } }), jsxRuntime.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') &&
2052
+ (hasError ? 'ring-4 ring-red-200' : 'ring-4 ring-blue-200'), className), style: {
2053
+ left: `calc(${percent(valueMin)}% - ${currentSize.thumbPx / 2}px)`,
1338
2054
  zIndex: activeThumbState === 'min' ? 50 : 30,
1339
- }, "data-testid": "thumb-min" }), jsxRuntime.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
1340
- activeThumbState === 'max' && 'ring-4 ring-blue-200', className), style: {
1341
- left: `calc(${percent(valueMax)}% - 10px)`,
2055
+ }, "data-testid": "thumb-min", children: showTooltips && (activeThumbState === 'min' || hoveringThumb === 'min') && (jsxRuntime.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, jsxRuntime.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" })] })) }), jsxRuntime.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') &&
2056
+ (hasError ? 'ring-4 ring-red-200' : 'ring-4 ring-blue-200'), className), style: {
2057
+ left: `calc(${percent(valueMax)}% - ${currentSize.thumbPx / 2}px)`,
1342
2058
  zIndex: activeThumbState === 'max' ? 50 : 40,
1343
- }, "data-testid": "thumb-max" })] }), jsxRuntime.jsxs("div", { className: "flex justify-between text-sm text-gray-700 dark:text-gray-300", children: [jsxRuntime.jsxs("span", { children: [Math.round(valueMin), valueSuffix] }), jsxRuntime.jsxs("span", { children: [Math.round(valueMax), valueSuffix] })] }), showErrorMessage && hasError && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) }))] }));
2059
+ }, "data-testid": "thumb-max", children: showTooltips && (activeThumbState === 'max' || hoveringThumb === 'max') && (jsxRuntime.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, jsxRuntime.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 && (jsxRuntime.jsx("div", { className: "relative w-full h-4 mt-1", children: markEntries.map((m) => {
2060
+ const markPercent = percent(m.value);
2061
+ return (jsxRuntime.jsxs("div", { className: "absolute top-0 transform -translate-x-1/2 flex flex-col items-center", style: { left: `${markPercent}%` }, children: [jsxRuntime.jsx("span", { className: "w-1 h-1.5 bg-gray-300 dark:bg-gray-600 rounded-full" }), jsxRuntime.jsx("span", { className: "text-[10px] font-medium text-gray-400 dark:text-gray-500 mt-0.5", children: m.label })] }, m.value));
2062
+ }) })), jsxRuntime.jsxs("div", { className: buildClassName('flex justify-between font-medium text-gray-700 dark:text-gray-300', currentSize.text), children: [jsxRuntime.jsxs("span", { children: [Math.round(valueMin), valueSuffix] }), jsxRuntime.jsxs("span", { children: [Math.round(valueMax), valueSuffix] })] }), (showErrorMessage && hasError) || helperText ? (jsxRuntime.jsx("div", { className: "mt-1", children: showErrorMessage && hasError ? (jsxRuntime.jsx(TextContent, { error: true, small: true, children: error })) : helperText ? (jsxRuntime.jsx(TextContent, { muted: true, small: true, children: helperText })) : null })) : null] }));
1344
2063
  };
2064
+ RangeSlider.displayName = 'RangeSlider';
1345
2065
 
1346
2066
  const useDebounce = (value, delay) => {
1347
2067
  const [debouncedValue, setDebouncedValue] = React.useState(value);
@@ -1409,28 +2129,69 @@ var SvgCirclePlus = function SvgCirclePlus(props) {
1409
2129
  })));
1410
2130
  };
1411
2131
 
1412
- const constructInputClass = (disabled) => buildInputClass('', { disabled });
2132
+ const sizeClasses$1 = {
2133
+ sm: {
2134
+ input: 'px-2.5 py-1 text-xs leading-5 min-h-[30px]',
2135
+ group: 'w-8 text-xs',
2136
+ icon: 'size-3.5',
2137
+ badgeSize: 'sm',
2138
+ },
2139
+ md: {
2140
+ input: 'px-3.5 py-2 text-sm leading-6 min-h-[38px]',
2141
+ group: 'w-10 text-sm',
2142
+ icon: 'size-4',
2143
+ badgeSize: 'md',
2144
+ },
2145
+ lg: {
2146
+ input: 'px-4 py-2.5 text-base leading-7 min-h-[46px]',
2147
+ group: 'w-12 text-base',
2148
+ icon: 'size-5',
2149
+ badgeSize: 'md',
2150
+ },
2151
+ };
1413
2152
  const SelectBox = React.forwardRef((props, forwardedRef) => {
1414
- 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;
2153
+ 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;
1415
2154
  const hasError = Boolean(error);
1416
2155
  const [query, setQuery] = React.useState('');
1417
2156
  const debouncedQuery = useDebounce(query, 500);
1418
2157
  const [optionsLoading, setOptionsLoading] = React.useState(false);
1419
- const shouldShowTypeToSearch = async && !query && !optionsLoading && options.length === 0;
1420
- const inputClass = React.useMemo(() => constructInputClass(disabled), [disabled]);
1421
- 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');
2158
+ // Normalize options from options prop or groups prop
2159
+ const allOptions = React.useMemo(() => {
2160
+ if (groups === null || groups === void 0 ? void 0 : groups.length) {
2161
+ return groups.flatMap((g) => g.options);
2162
+ }
2163
+ return options;
2164
+ }, [groups, options]);
2165
+ const shouldShowTypeToSearch = async && !query && !optionsLoading && allOptions.length === 0;
2166
+ const currentSize = sizeClasses$1[size] || sizeClasses$1.md;
2167
+ const baseInputClass = React.useMemo(() => buildInputClass('', { disabled, hasError, size }), [disabled, hasError, size]);
2168
+ 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');
1422
2169
  const inputRef = React.useRef(null);
1423
2170
  const containerRef = React.useRef(null);
1424
- // Expose the input element to parent
1425
2171
  React.useImperativeHandle(forwardedRef, () => inputRef.current, [inputRef]);
1426
2172
  const filteredOptions = React.useMemo(() => {
1427
2173
  if (query === '' || async)
1428
- return options;
1429
- return options.filter((opt) => {
1430
- const label = typeof opt === 'string' ? opt : opt[labelKey];
1431
- return label === null || label === void 0 ? void 0 : label.toLowerCase().includes(query.toLowerCase());
2174
+ return allOptions;
2175
+ return allOptions.filter((opt) => {
2176
+ const lbl = typeof opt === 'string' ? opt : opt[labelKey];
2177
+ return lbl === null || lbl === void 0 ? void 0 : lbl.toLowerCase().includes(query.toLowerCase());
1432
2178
  });
1433
- }, [options, query, async, labelKey]);
2179
+ }, [allOptions, query, async, labelKey]);
2180
+ const filteredGroups = React.useMemo(() => {
2181
+ if (!(groups === null || groups === void 0 ? void 0 : groups.length))
2182
+ return null;
2183
+ if (query === '' || async)
2184
+ return groups;
2185
+ return groups
2186
+ .map((g) => ({
2187
+ ...g,
2188
+ options: g.options.filter((opt) => {
2189
+ const lbl = typeof opt === 'string' ? opt : opt[labelKey];
2190
+ return lbl === null || lbl === void 0 ? void 0 : lbl.toLowerCase().includes(query.toLowerCase());
2191
+ }),
2192
+ }))
2193
+ .filter((g) => g.options.length > 0);
2194
+ }, [groups, query, async, labelKey]);
1434
2195
  const getDisplayValue = React.useCallback((opt) => {
1435
2196
  return typeof opt === 'string' ? opt : opt[labelKey];
1436
2197
  }, [labelKey]);
@@ -1446,69 +2207,48 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
1446
2207
  onChange(next);
1447
2208
  (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
1448
2209
  };
1449
- // A strongly-typed internal handler (what you expect callers to receive)
1450
2210
  const handleOnChangeInternal = React.useCallback((value) => {
1451
- // normal empty value
1452
2211
  if (value === null) {
1453
2212
  onChange(multiple ? [] : null);
1454
2213
  setQuery('');
1455
2214
  return;
1456
2215
  }
1457
- // enforce maxSelection
1458
2216
  if (multiple && Array.isArray(value) && maxSelection && value.length > maxSelection) {
1459
- // ignore the change
1460
2217
  return;
1461
2218
  }
1462
2219
  onChange(value);
1463
2220
  setQuery('');
1464
2221
  }, [onChange, multiple, maxSelection]);
1465
- /**
1466
- * Headless UI typings are overly broad (they include null[] and EnsureArray<T> etc).
1467
- * We therefore pass a wrapper to the Combobox with a cast (only at the call site),
1468
- * while keeping our internal logic strongly typed.
1469
- */
1470
2222
  const comboboxOnChangeWrapper = React.useCallback((raw) => {
1471
- // normalize actual runtime shapes Headless UI uses: T | T[] | null
1472
2223
  if (raw === null || raw === undefined) {
1473
2224
  handleOnChangeInternal(null);
1474
2225
  return;
1475
2226
  }
1476
2227
  if (Array.isArray(raw)) {
1477
- // sometimes union typing suggests arrays of nulls — guard it
1478
2228
  const allNulls = raw.length > 0 && raw.every((v) => v === null);
1479
2229
  if (allNulls) {
1480
2230
  handleOnChangeInternal([]);
1481
2231
  return;
1482
2232
  }
1483
- // cast array to T[]
1484
2233
  handleOnChangeInternal(raw);
1485
2234
  return;
1486
2235
  }
1487
- // single value
1488
2236
  handleOnChangeInternal(raw);
1489
2237
  }, [handleOnChangeInternal]);
1490
- // support create (allowAdd) as a real ComboboxOption
1491
2238
  const handleAddAndSelect = React.useCallback(async (valueToAdd) => {
1492
2239
  try {
1493
2240
  const result = await onAdd(valueToAdd);
1494
- // if the parent returned a new option (string or object), select it
1495
2241
  if (result) {
1496
- // If parent returned raw string or object, call onChange with that
1497
2242
  handleOnChangeInternal(result);
1498
2243
  }
1499
2244
  else {
1500
- // if parent didn't return anything, select the raw string value
1501
2245
  handleOnChangeInternal(valueToAdd);
1502
2246
  }
1503
2247
  }
1504
2248
  catch (err) {
1505
- // eslint-disable-next-line no-console
1506
2249
  console.error('onAdd handler error', err);
1507
2250
  }
1508
2251
  }, [onAdd, handleOnChangeInternal]);
1509
- // Kept in a ref so `handleSearch` stays referentially stable even when the
1510
- // caller passes a non-memoized `onSearch` — otherwise the effect below would
1511
- // re-fire (and re-search) on every render instead of only on query changes.
1512
2252
  const onSearchRef = React.useRef(props.onSearch);
1513
2253
  React.useEffect(() => {
1514
2254
  onSearchRef.current = props.onSearch;
@@ -1530,16 +2270,232 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
1530
2270
  if (async && debouncedQuery)
1531
2271
  handleSearch(debouncedQuery);
1532
2272
  }, [debouncedQuery, async, handleSearch]);
1533
- return (jsxRuntime.jsxs("div", { className: buildClassName('group space-y-2', hasError && 'has-error', containerClass), ref: containerRef, children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), jsxRuntime.jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxRuntime.jsxs(react.Combobox, { multiple: multiple, immediate: immediate, name: name, value: selected, onChange: comboboxOnChangeWrapper, onClose: () => setQuery(''), disabled: disabled, children: [multiple ? (jsxRuntime.jsxs("div", { className: buildClassName(inputClass, 'flex flex-wrap gap-2 peer relative'), ref: forwardedRef, children: [isArray(selected) &&
2273
+ const isAllSelected = React.useMemo(() => {
2274
+ if (!multiple || !Array.isArray(selected) || !allOptions.length)
2275
+ return false;
2276
+ return allOptions.length === selected.length;
2277
+ }, [multiple, selected, allOptions]);
2278
+ const handleToggleSelectAll = () => {
2279
+ if (isAllSelected) {
2280
+ onChange([]);
2281
+ }
2282
+ else {
2283
+ onChange(allOptions);
2284
+ }
2285
+ };
2286
+ const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
2287
+ return (jsxRuntime.jsxs("div", { className: buildClassName('group space-y-2', hasError && 'has-error', containerClass), ref: containerRef, children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), labelHint && jsxRuntime.jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxRuntime.jsxs(react.Combobox, { multiple: multiple, immediate: immediate, name: name, value: selected, onChange: comboboxOnChangeWrapper, onClose: () => setQuery(''), disabled: disabled, children: [multiple ? (jsxRuntime.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 && (jsxRuntime.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) &&
1534
2288
  (selected === null || selected === void 0 ? void 0 : selected.length) > 0 &&
1535
- selected.map((option, idx) => (jsxRuntime.jsx(Badge, { theme: "secondary", removable: !disabled, onRemove: () => removeSelection(idx), children: getDisplayValue(option) }, `${getValue(option)}-${idx}`))), jsxRuntime.jsx(react.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) => {
2289
+ selected.map((option, idx) => (jsxRuntime.jsx(Badge, { theme: "secondary", size: currentSize.badgeSize, removable: !disabled, onRemove: () => removeSelection(idx), children: getDisplayValue(option) }, `${getValue(option)}-${idx}`))), jsxRuntime.jsx(react.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) => {
1536
2290
  if (multiple && e.key === 'Backspace' && !query) {
1537
2291
  if (Array.isArray(selected) && selected.length > 0) {
1538
2292
  removeSelection(selected.length - 1);
1539
2293
  }
1540
2294
  }
1541
- }, "data-testid": "combobox-input" }), jsxRuntime.jsx(react.ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5", "data-testid": "combobox-button", children: jsxRuntime.jsx(SvgChevronDown, { className: chevronIconClass }) })] })) : (jsxRuntime.jsxs("div", { className: "relative", ref: forwardedRef, children: [jsxRuntime.jsx(react.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) && (jsxRuntime.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: jsxRuntime.jsx(SvgX, { className: "size-4 stroke-gray-500 dark:stroke-gray-300" }) })), jsxRuntime.jsx(react.ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5", "data-testid": "combobox-button", children: jsxRuntime.jsx(SvgChevronDown, { className: chevronIconClass }) })] })), jsxRuntime.jsx(react.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 ? (jsxRuntime.jsx(BodyText, { "data-testid": "options-loading", className: "flex items-center justify-center py-2 px-3 select-none", children: jsxRuntime.jsx(Loader, { size: "sm" }) })) : shouldShowTypeToSearch ? (jsxRuntime.jsx(BodyText, { className: "flex items-center justify-between py-2 px-3 select-none", children: "Type to search..." })) : (jsxRuntime.jsxs("div", { children: [filteredOptions.map((option) => (jsxRuntime.jsx(react.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: jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 dark:text-white group-data-[selected]:text-blue-600", children: [jsxRuntime.jsx("span", { className: "text-sm/6", children: getDisplayValue(option) }), jsxRuntime.jsx(SvgCheck, { className: "invisible size-5 dark:stroke-gray-300 group-data-[selected]:visible" })] }) }, getValue(option)))), filteredOptions.length === 0 && (jsxRuntime.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 ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: ["No results found for ", jsxRuntime.jsx("span", { className: "font-semibold", children: query })] })) : (noOptionsText) })), allowAdd && (jsxRuntime.jsx(react.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: jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsx(SvgCirclePlus, { className: "size-4.5" }), jsxRuntime.jsxs(TextContent, { children: [addNewText, " \"", query, "\""] })] }) }))] })) })] }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) }))] }));
2295
+ }, "data-testid": "combobox-input" }), jsxRuntime.jsx(react.ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5 flex items-center justify-center", "data-testid": "combobox-button", children: jsxRuntime.jsx(SvgChevronDown, { className: chevronIconClass }) })] })) : (jsxRuntime.jsxs("div", { className: "relative", ref: forwardedRef, children: [hasLeftGroup && (jsxRuntime.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 })), jsxRuntime.jsx(react.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) && (jsxRuntime.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: jsxRuntime.jsx(SvgX, { className: "size-4 stroke-gray-500 dark:stroke-gray-300" }) })), jsxRuntime.jsx(react.ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5 flex items-center justify-center", "data-testid": "combobox-button", children: jsxRuntime.jsx(SvgChevronDown, { className: chevronIconClass }) })] })), jsxRuntime.jsxs(react.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 && (jsxRuntime.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: [jsxRuntime.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' }), jsxRuntime.jsxs("span", { className: "text-[11px] font-medium text-gray-400", children: [Array.isArray(selected) ? selected.length : 0, " of ", allOptions.length] })] })), optionsLoading ? (jsxRuntime.jsx(BodyText, { "data-testid": "options-loading", className: "flex items-center justify-center py-2 px-3 select-none", children: jsxRuntime.jsx(Loader, { size: "sm" }) })) : shouldShowTypeToSearch ? (jsxRuntime.jsx(BodyText, { className: "flex items-center justify-between py-2 px-3 select-none", children: "Type to search..." })) : (jsxRuntime.jsxs("div", { children: [filteredGroups
2296
+ ? filteredGroups.map((grp, gIdx) => (jsxRuntime.jsxs("div", { children: [jsxRuntime.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) => {
2297
+ const optVal = getValue(option);
2298
+ const isOptSelected = multiple
2299
+ ? isArray(selected) && selected.some((s) => getValue(s) === optVal)
2300
+ : selected && getValue(selected) === optVal;
2301
+ return (jsxRuntime.jsx(react.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))) : (jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 dark:text-white group-data-[selected]:text-blue-600", children: [jsxRuntime.jsx("span", { className: "text-sm/6", children: getDisplayValue(option) }), jsxRuntime.jsx(SvgCheck, { className: "invisible size-5 dark:stroke-gray-300 group-data-[selected]:visible" })] })) }, optVal));
2302
+ })] }, gIdx)))
2303
+ : /* Render Standard Flat Options */
2304
+ filteredOptions.map((option) => {
2305
+ const optVal = getValue(option);
2306
+ const isOptSelected = multiple
2307
+ ? isArray(selected) && selected.some((s) => getValue(s) === optVal)
2308
+ : selected && getValue(selected) === optVal;
2309
+ return (jsxRuntime.jsx(react.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))) : (jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 dark:text-white group-data-[selected]:text-blue-600", children: [jsxRuntime.jsx("span", { className: "text-sm/6", children: getDisplayValue(option) }), jsxRuntime.jsx(SvgCheck, { className: "invisible size-5 dark:stroke-gray-300 group-data-[selected]:visible" })] })) }, optVal));
2310
+ }), filteredOptions.length === 0 && !(filteredGroups === null || filteredGroups === void 0 ? void 0 : filteredGroups.length) && (jsxRuntime.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 ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: ["No results found for ", jsxRuntime.jsx("span", { className: "font-semibold", children: query })] })) : (noOptionsText) })), allowAdd && (jsxRuntime.jsx(react.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: jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsx(SvgCirclePlus, { className: "size-4.5" }), jsxRuntime.jsxs(TextContent, { children: [addNewText, " \"", query, "\""] })] }) }))] }))] })] }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) })), !error && helperText && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { muted: true, small: true, children: helperText }) }))] }));
2311
+ });
2312
+ SelectBox.displayName = 'SelectBox';
2313
+
2314
+ const VARIANT_CLASS = {
2315
+ text: 'rounded-md h-4 w-full my-1',
2316
+ circular: 'rounded-full shrink-0',
2317
+ rectangular: 'rounded-none w-full h-24',
2318
+ rounded: 'rounded-xl w-full h-24',
2319
+ };
2320
+ const ANIMATION_CLASS = {
2321
+ pulse: 'animate-pulse bg-gray-200 dark:bg-gray-700',
2322
+ 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]',
2323
+ none: 'bg-gray-200 dark:bg-gray-700',
2324
+ };
2325
+ const Skeleton = React.forwardRef((props, ref) => {
2326
+ const { as, variant = 'text', animation = 'pulse', width, height, borderRadius, className, style: customStyle, ...restProps } = props;
2327
+ const Component = as || 'div';
2328
+ const inlineStyles = {
2329
+ ...(width !== undefined ? { width: typeof width === 'number' ? `${width}px` : width } : {}),
2330
+ ...(height !== undefined
2331
+ ? { height: typeof height === 'number' ? `${height}px` : height }
2332
+ : {}),
2333
+ ...(borderRadius !== undefined
2334
+ ? { borderRadius: typeof borderRadius === 'number' ? `${borderRadius}px` : borderRadius }
2335
+ : {}),
2336
+ ...customStyle,
2337
+ };
2338
+ return (jsxRuntime.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 }));
2339
+ });
2340
+ Skeleton.displayName = 'Skeleton';
2341
+
2342
+ const MinusIcon = ({ className }) => (jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", className: className, "aria-hidden": "true", children: jsxRuntime.jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" }) }));
2343
+ const PlusIcon = ({ className }) => (jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", className: className, "aria-hidden": "true", children: [jsxRuntime.jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }), jsxRuntime.jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })] }));
2344
+ const BUTTON_SIZE_CLASS = {
2345
+ sm: 'size-7',
2346
+ md: 'size-9',
2347
+ lg: 'size-10',
2348
+ };
2349
+ const ICON_SIZE_CLASS = {
2350
+ sm: 'size-3.5',
2351
+ md: 'size-4',
2352
+ lg: 'size-4.5',
2353
+ };
2354
+ const INPUT_TEXT_CLASS = {
2355
+ sm: 'w-12 text-xs py-1',
2356
+ md: 'w-16 text-sm py-1.5',
2357
+ lg: 'w-20 text-base py-2',
2358
+ };
2359
+ const QuantityStepper = React.forwardRef((props, ref) => {
2360
+ const { value, onChange, min = 0, max = 99999, step = 1, label, size = 'md', prefix, suffix, disabled = false, className, onFocus, ...restProps } = props;
2361
+ const handleDecrement = () => {
2362
+ if (disabled)
2363
+ return;
2364
+ const next = Math.max(min, Number((value - step).toFixed(4)));
2365
+ onChange(next);
2366
+ };
2367
+ const handleIncrement = () => {
2368
+ if (disabled)
2369
+ return;
2370
+ const next = Math.min(max, Number((value + step).toFixed(4)));
2371
+ onChange(next);
2372
+ };
2373
+ const handleInputChange = (e) => {
2374
+ const raw = e.target.value;
2375
+ if (raw === '') {
2376
+ onChange(0);
2377
+ return;
2378
+ }
2379
+ const parsed = parseFloat(raw);
2380
+ if (!isNaN(parsed)) {
2381
+ onChange(Math.max(min, Math.min(max, parsed)));
2382
+ }
2383
+ };
2384
+ const isAtMin = disabled || value <= min;
2385
+ const isAtMax = disabled || value >= max;
2386
+ return (jsxRuntime.jsxs("div", { className: buildClassName('flex flex-col gap-1 inline-flex', className), children: [label && (jsxRuntime.jsx("label", { className: "block text-xs font-medium text-gray-700 dark:text-gray-300", children: label })), jsxRuntime.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: [jsxRuntime.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: jsxRuntime.jsx(MinusIcon, { className: ICON_SIZE_CLASS[size] }) }), prefix && jsxRuntime.jsx("span", { className: "pl-1 text-gray-400 dark:text-gray-500", children: prefix }), jsxRuntime.jsx("input", { ref: ref, type: "number", min: min, max: max, step: step, value: value === 0 ? '' : value, placeholder: "0", disabled: disabled, onFocus: (e) => {
2387
+ e.target.select();
2388
+ onFocus === null || onFocus === void 0 ? void 0 : onFocus(e);
2389
+ }, 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 && jsxRuntime.jsx("span", { className: "pr-1 text-xs text-gray-500 dark:text-gray-400", children: suffix }), jsxRuntime.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: jsxRuntime.jsx(PlusIcon, { className: ICON_SIZE_CLASS[size] }) })] })] }));
2390
+ });
2391
+ QuantityStepper.displayName = 'QuantityStepper';
2392
+
2393
+ const SIZE_CONTAINER_CLASS = {
2394
+ sm: 'p-0.5 gap-0.5 rounded-lg text-xs',
2395
+ md: 'p-1 gap-1 rounded-xl text-sm',
2396
+ lg: 'p-1.5 gap-1.5 rounded-xl text-base',
2397
+ };
2398
+ const SIZE_OPTION_CLASS = {
2399
+ sm: 'px-2 py-1 text-xs font-medium rounded-md',
2400
+ md: 'px-3 py-1.5 text-sm font-semibold rounded-lg',
2401
+ lg: 'px-4 py-2 text-base font-semibold rounded-lg',
2402
+ };
2403
+ const SIZE_ICON_CLASS = {
2404
+ sm: 'size-3.5',
2405
+ md: 'size-4',
2406
+ lg: 'size-5',
2407
+ };
2408
+ const SegmentedControl = React.forwardRef((props, ref) => {
2409
+ const { options, value, onChange, disabled = false, size = 'md', fullWidth = false, className, ...restProps } = props;
2410
+ const handleKeyDown = (e, currentIndex) => {
2411
+ const enabledOptions = options.filter((opt) => !opt.disabled && !disabled);
2412
+ if (enabledOptions.length === 0)
2413
+ return;
2414
+ if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
2415
+ e.preventDefault();
2416
+ const currentPos = enabledOptions.findIndex((opt) => opt.value === options[currentIndex].value);
2417
+ const nextPos = (currentPos + 1) % enabledOptions.length;
2418
+ onChange(enabledOptions[nextPos].value);
2419
+ }
2420
+ else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
2421
+ e.preventDefault();
2422
+ const currentPos = enabledOptions.findIndex((opt) => opt.value === options[currentIndex].value);
2423
+ const prevPos = (currentPos - 1 + enabledOptions.length) % enabledOptions.length;
2424
+ onChange(enabledOptions[prevPos].value);
2425
+ }
2426
+ else if (e.key === 'Home') {
2427
+ e.preventDefault();
2428
+ onChange(enabledOptions[0].value);
2429
+ }
2430
+ else if (e.key === 'End') {
2431
+ e.preventDefault();
2432
+ onChange(enabledOptions[enabledOptions.length - 1].value);
2433
+ }
2434
+ };
2435
+ return (jsxRuntime.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) => {
2436
+ const isSelected = value === option.value;
2437
+ const isOptionDisabled = disabled || option.disabled;
2438
+ return (jsxRuntime.jsxs("button", { type: "button", role: "tab", "aria-selected": isSelected, "aria-disabled": isOptionDisabled, tabIndex: isSelected && !isOptionDisabled ? 0 : -1, disabled: isOptionDisabled, title: option.title, onClick: () => {
2439
+ if (!isOptionDisabled) {
2440
+ onChange(option.value);
2441
+ }
2442
+ }, 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
2443
+ ? 'bg-white text-[var(--ui-primary)] dark:bg-gray-700 dark:text-blue-400 shadow-xs font-semibold'
2444
+ : '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 && (jsxRuntime.jsx("span", { className: buildClassName('shrink-0 inline-flex items-center justify-center', SIZE_ICON_CLASS[size]), children: option.icon })), jsxRuntime.jsx("span", { children: option.label })] }, option.value));
2445
+ }) }));
1542
2446
  });
2447
+ SegmentedControl.displayName = 'SegmentedControl';
2448
+
2449
+ const THEME_PILL_CLASS = {
2450
+ success: {
2451
+ bg: 'bg-emerald-500/10 text-emerald-700 dark:text-emerald-300 border-emerald-500/20 dark:border-emerald-500/30',
2452
+ dot: 'bg-emerald-500',
2453
+ },
2454
+ warning: {
2455
+ bg: 'bg-amber-500/10 text-amber-700 dark:text-amber-300 border-amber-500/20 dark:border-amber-500/30',
2456
+ dot: 'bg-amber-500',
2457
+ },
2458
+ danger: {
2459
+ bg: 'bg-rose-500/10 text-rose-700 dark:text-rose-300 border-rose-500/20 dark:border-rose-500/30',
2460
+ dot: 'bg-rose-500',
2461
+ },
2462
+ info: {
2463
+ bg: 'bg-blue-500/10 text-blue-700 dark:text-blue-300 border-blue-500/20 dark:border-blue-500/30',
2464
+ dot: 'bg-blue-500',
2465
+ },
2466
+ secondary: {
2467
+ bg: 'bg-gray-500/10 text-gray-600 dark:text-gray-400 border-gray-500/20 dark:border-gray-500/30',
2468
+ dot: 'bg-gray-400',
2469
+ },
2470
+ };
2471
+ const SIZE_CLASS = {
2472
+ sm: 'px-2.5 py-0.5 text-xs',
2473
+ md: 'px-3 py-1 text-sm',
2474
+ };
2475
+ const DOT_SIZE_CLASS = {
2476
+ sm: 'size-1.5',
2477
+ md: 'size-2',
2478
+ };
2479
+ const StatusPill = React.forwardRef((props, ref) => {
2480
+ var _a;
2481
+ const { theme = 'secondary', size = 'sm', dot = true, pulse = false, className, children, ...restProps } = props;
2482
+ const style = (_a = THEME_PILL_CLASS[theme]) !== null && _a !== void 0 ? _a : THEME_PILL_CLASS.secondary;
2483
+ return (jsxRuntime.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 && (jsxRuntime.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" })), jsxRuntime.jsx("span", { children: children })] }));
2484
+ });
2485
+ StatusPill.displayName = 'StatusPill';
2486
+
2487
+ const ChevronUpIcon = ({ className }) => (jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: className, "aria-hidden": "true", children: jsxRuntime.jsx("polyline", { points: "18 15 12 9 6 15" }) }));
2488
+ const StickyActionBar = React.forwardRef((props, ref) => {
2489
+ const { summaryContent, actionsContent, drawerContent, drawerTitle = 'Summary Details', position = 'fixed', containerMaxWidth = 'max-w-5xl', className, ...restProps } = props;
2490
+ const [isDrawerOpen, setIsDrawerOpen] = React.useState(false);
2491
+ const positionClass = position === 'fixed'
2492
+ ? 'fixed bottom-0 left-0 right-0 z-30'
2493
+ : position === 'sticky'
2494
+ ? 'sticky bottom-0 z-30'
2495
+ : 'relative z-10';
2496
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.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: jsxRuntime.jsxs("div", { className: buildClassName('mx-auto flex items-center justify-between gap-3', containerMaxWidth), children: [summaryContent && (jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: drawerContent ? (jsxRuntime.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: [jsxRuntime.jsx("div", { children: summaryContent }), jsxRuntime.jsx(ChevronUpIcon, { className: "size-4 text-[var(--ui-primary)] dark:text-blue-400 shrink-0" })] })) : (jsxRuntime.jsx("div", { children: summaryContent })) })), actionsContent && (jsxRuntime.jsx("div", { className: "flex items-center gap-2 shrink-0 ml-auto", "data-testid": "actions-stack", children: actionsContent }))] }) }), drawerContent && (jsxRuntime.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 }))] }));
2497
+ });
2498
+ StickyActionBar.displayName = 'StickyActionBar';
1543
2499
 
1544
2500
  const Tabs = ({ tabs, defaultIndex = 0, selectedIndex, onChange, variant = 'underline', orientation = 'horizontal', stretch = false, className = '', listClass = '', panelsClass = '', }) => {
1545
2501
  const isVertical = orientation === 'vertical';
@@ -1560,7 +2516,7 @@ const Tabs = ({ tabs, defaultIndex = 0, selectedIndex, onChange, variant = 'unde
1560
2516
  }) })] }));
1561
2517
  };
1562
2518
  function buildTabClass({ selected, variant, stretch, isVertical, tabClass, }) {
1563
- 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');
2519
+ 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');
1564
2520
  let variantClass;
1565
2521
  switch (variant) {
1566
2522
  case 'underline':
@@ -1585,13 +2541,58 @@ function buildTabClass({ selected, variant, stretch, isVertical, tabClass, }) {
1585
2541
  return buildClassName(base, variantClass, tabClass);
1586
2542
  }
1587
2543
 
2544
+ const sizeClasses = {
2545
+ sm: {
2546
+ textarea: 'px-2.5 py-1.5 text-xs leading-5 min-h-[60px]',
2547
+ text: 'text-xs',
2548
+ },
2549
+ md: {
2550
+ textarea: 'px-3.5 py-2 text-sm leading-6 min-h-[80px]',
2551
+ text: 'text-sm',
2552
+ },
2553
+ lg: {
2554
+ textarea: 'px-4 py-2.5 text-base leading-7 min-h-[100px]',
2555
+ text: 'text-base',
2556
+ },
2557
+ };
2558
+ const resizeClasses = {
2559
+ none: 'resize-none',
2560
+ vertical: 'resize-y',
2561
+ horizontal: 'resize-x',
2562
+ both: 'resize',
2563
+ };
1588
2564
  const Textarea = React.forwardRef((props, ref) => {
1589
- const { name, id = name, label = null, labelHint = null, className = '', labelClass = '', labelWrapperClass = '', containerClass = '', error, showErrorMessage = true, rightGroup = null, leftGroup = null, ...inputProps } = props;
2565
+ 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;
2566
+ const innerRef = React.useRef(null);
2567
+ React.useImperativeHandle(ref, () => innerRef.current);
2568
+ const [uncontrolledValue, setUncontrolledValue] = React.useState(String(defaultValue !== null && defaultValue !== void 0 ? defaultValue : ''));
2569
+ const isControlled = value !== undefined;
2570
+ const currentVal = isControlled ? String(value !== null && value !== void 0 ? value : '') : uncontrolledValue;
2571
+ const charLength = currentVal.length;
2572
+ // Handle Auto-Resize behavior
2573
+ React.useEffect(() => {
2574
+ if (autoSize && innerRef.current) {
2575
+ innerRef.current.style.height = 'auto';
2576
+ innerRef.current.style.height = `${innerRef.current.scrollHeight}px`;
2577
+ }
2578
+ }, [autoSize, currentVal]);
2579
+ const handleTextareaChange = (e) => {
2580
+ if (!isControlled) {
2581
+ setUncontrolledValue(e.target.value);
2582
+ }
2583
+ onChange === null || onChange === void 0 ? void 0 : onChange(e);
2584
+ };
1590
2585
  const hasError = !isEmpty(error);
1591
2586
  const hasRightGroup = rightGroup != null && rightGroup !== undefined;
1592
2587
  const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
1593
- return (jsxRuntime.jsxs("div", { className: buildClassName('group', hasError && 'has-error', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between mb-2', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, htmlFor: id, children: label }), jsxRuntime.jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxRuntime.jsxs("div", { className: "relative", children: [hasLeftGroup && (jsxRuntime.jsx("span", { className: "absolute top-0 h-full flex items-center justify-center w-10", children: leftGroup })), jsxRuntime.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 && (jsxRuntime.jsx("span", { className: "absolute end-0.75 top-0 h-full flex items-center justify-center", children: rightGroup }))] }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) }))] }));
2588
+ const currentSize = sizeClasses[size] || sizeClasses.md;
2589
+ return (jsxRuntime.jsxs("div", { className: buildClassName('group', hasError && 'has-error', containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between mb-1.5', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, htmlFor: id, children: label }), labelHint && jsxRuntime.jsx(TextContent, { xsmall: true, children: labelHint })] })), jsxRuntime.jsxs("div", { className: "relative", children: [hasLeftGroup && (jsxRuntime.jsx("span", { className: "absolute top-2.5 left-3 flex items-center justify-center text-gray-400 dark:text-gray-500", children: leftGroup })), jsxRuntime.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
2590
+ ? '!outline-red-500 !ring-1 !ring-red-500/50 dark:!outline-red-500'
2591
+ : '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 && (jsxRuntime.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 ? (jsxRuntime.jsxs("div", { className: "mt-1.5 flex items-center justify-between gap-2 text-xs", children: [jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: showErrorMessage && error ? (jsxRuntime.jsx(TextContent, { error: true, small: true, children: error })) : helperText ? (jsxRuntime.jsx(TextContent, { muted: true, small: true, children: helperText })) : null }), showCount && (jsxRuntime.jsxs("span", { className: buildClassName('text-[11px] font-medium shrink-0 ml-auto', maxLength && charLength >= maxLength
2592
+ ? 'text-red-500 font-semibold'
2593
+ : 'text-gray-400 dark:text-gray-500'), children: [charLength, maxLength ? ` / ${maxLength}` : ''] }))] })) : null] }));
1594
2594
  });
2595
+ Textarea.displayName = 'Textarea';
1595
2596
 
1596
2597
  /**
1597
2598
  * Icons for each toast type
@@ -1602,23 +2603,76 @@ const iconMap = {
1602
2603
  info: jsxRuntime.jsx(SvgInfo, { className: "size-5 text-[var(--ui-info)]" }),
1603
2604
  warning: jsxRuntime.jsx(SvgTriangleAlert, { className: "size-5 text-[var(--ui-warning)]" }),
1604
2605
  };
2606
+ const filledIconMap = {
2607
+ success: jsxRuntime.jsx(SvgCircleCheck, { className: "size-5 text-white" }),
2608
+ error: jsxRuntime.jsx(SvgCircleAlert, { className: "size-5 text-white" }),
2609
+ info: jsxRuntime.jsx(SvgInfo, { className: "size-5 text-white" }),
2610
+ warning: jsxRuntime.jsx(SvgTriangleAlert, { className: "size-5 text-white" }),
2611
+ };
1605
2612
  const textColorMap = {
1606
2613
  success: 'text-[var(--ui-success-text)]',
1607
2614
  error: 'text-[var(--ui-danger-text)]',
1608
2615
  info: 'text-[var(--ui-info-text)]',
1609
2616
  warning: 'text-[var(--ui-warning-text)]',
1610
2617
  };
1611
- const borderStyle = {
1612
- success: 'border-t-4 border-[var(--ui-success)]',
1613
- error: 'border-t-4 border-[var(--ui-danger)]',
1614
- info: 'border-t-4 border-[var(--ui-info)]',
1615
- warning: 'border-t-4 border-[var(--ui-warning)]',
2618
+ const progressBarColorMap = {
2619
+ success: 'bg-[var(--ui-success)]',
2620
+ error: 'bg-[var(--ui-danger)]',
2621
+ info: 'bg-[var(--ui-info)]',
2622
+ warning: 'bg-[var(--ui-warning)]',
2623
+ };
2624
+ const variantStyleMap = {
2625
+ accent: {
2626
+ 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',
2627
+ 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',
2628
+ 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',
2629
+ 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',
2630
+ },
2631
+ filled: {
2632
+ success: 'bg-[var(--ui-success)] text-white shadow-lg',
2633
+ error: 'bg-[var(--ui-danger)] text-white shadow-lg',
2634
+ info: 'bg-[var(--ui-info)] text-white shadow-lg',
2635
+ warning: 'bg-[var(--ui-warning)] text-white shadow-lg',
2636
+ },
2637
+ outlined: {
2638
+ success: 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-success)] text-gray-900 dark:text-gray-100 shadow-md',
2639
+ error: 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-danger)] text-gray-900 dark:text-gray-100 shadow-md',
2640
+ info: 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-info)] text-gray-900 dark:text-gray-100 shadow-md',
2641
+ warning: 'bg-white dark:bg-gray-900 border-2 border-[var(--ui-warning)] text-gray-900 dark:text-gray-100 shadow-md',
2642
+ },
2643
+ glass: {
2644
+ 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',
2645
+ 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',
2646
+ 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',
2647
+ 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',
2648
+ },
1616
2649
  };
1617
- const Toast = ({ message, type, onClose, className, id, isTop = true, }) => {
2650
+ const Toast = ({ id, title, message, type, variant = 'accent', action, icon, showProgressBar = true, duration = 4000, isPaused = false, onClose, className, isTop = true, }) => {
2651
+ const [progress, setProgress] = React.useState(100);
2652
+ React.useEffect(() => {
2653
+ if (!showProgressBar || !duration || isPaused)
2654
+ return;
2655
+ const intervalTime = 50;
2656
+ const step = (intervalTime / duration) * 100;
2657
+ const timer = setInterval(() => {
2658
+ setProgress((prev) => {
2659
+ if (prev <= 0) {
2660
+ clearInterval(timer);
2661
+ return 0;
2662
+ }
2663
+ return prev - step;
2664
+ });
2665
+ }, intervalTime);
2666
+ return () => clearInterval(timer);
2667
+ }, [showProgressBar, duration, isPaused]);
1618
2668
  const enterFrom = isTop ? 'opacity-0 -translate-y-3 scale-95' : 'opacity-0 translate-y-3 scale-95';
1619
2669
  const leaveTo = isTop ? 'opacity-0 -translate-y-2 scale-95' : 'opacity-0 translate-y-2 scale-95';
1620
- return (jsxRuntime.jsx(react.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: jsxRuntime.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: [jsxRuntime.jsx(TextContent, { className: buildClassName(textColorMap[type]), children: iconMap[type] }), jsxRuntime.jsx(TextContent, { className: "flex-1 text-sm font-medium", children: message }), jsxRuntime.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: jsxRuntime.jsx(SvgX, { className: "size-4" }) })] }) }));
2670
+ const renderedIcon = icon !== null && icon !== void 0 ? icon : (variant === 'filled' ? filledIconMap[type] : iconMap[type]);
2671
+ return (jsxRuntime.jsx(react.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: jsxRuntime.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: [jsxRuntime.jsx("div", { className: "shrink-0 mt-0.5", children: variant === 'filled' ? (renderedIcon) : (jsxRuntime.jsx(TextContent, { className: buildClassName(textColorMap[type]), children: renderedIcon })) }), jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col gap-0.5", children: [title && (jsxRuntime.jsx("div", { className: buildClassName('font-semibold text-sm leading-snug', variant === 'filled' ? 'text-white' : 'text-gray-900 dark:text-gray-100'), children: title })), jsxRuntime.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 && (jsxRuntime.jsx("div", { className: "mt-1.5", children: jsxRuntime.jsx("button", { type: "button", onClick: action.onClick, className: buildClassName('text-xs font-semibold underline underline-offset-2 transition-opacity focus:outline-none', variant === 'filled'
2672
+ ? 'text-white hover:opacity-85'
2673
+ : 'text-[var(--ui-primary)] hover:opacity-80'), children: action.label }) }))] }), jsxRuntime.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: jsxRuntime.jsx(SvgX, { className: "size-4" }) }), showProgressBar && duration > 0 && (jsxRuntime.jsx("div", { className: "absolute bottom-0 left-0 right-0 h-1 bg-black/10 dark:bg-white/10 overflow-hidden", children: jsxRuntime.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)}%` } }) }))] }) }));
1621
2674
  };
2675
+ Toast.displayName = 'Toast';
1622
2676
 
1623
2677
  const ToastContext = React.createContext(undefined);
1624
2678
  let toastCounter = 0;
@@ -1628,64 +2682,138 @@ const useToast = () => {
1628
2682
  throw new Error('useToast must be used within ToastProvider');
1629
2683
  return context;
1630
2684
  };
1631
- // Maximum visible toasts at once
1632
- const MAX_TOASTS = 5;
1633
2685
  const placementClass = {
1634
- 'top-right': 'top-10 right-4 items-end',
1635
- 'top-left': 'top-10 left-4 items-start',
1636
- 'top-center': 'top-10 left-1/2 -translate-x-1/2 items-center',
1637
- 'bottom-right': 'bottom-10 right-4 items-end',
1638
- 'bottom-left': 'bottom-10 left-4 items-start',
1639
- 'bottom-center': 'bottom-10 left-1/2 -translate-x-1/2 items-center',
1640
- };
1641
- const ToastProvider = ({ children, placement = 'top-center' }) => {
2686
+ 'top-right': 'top-20 right-6 items-end',
2687
+ 'top-left': 'top-20 left-6 items-start',
2688
+ 'top-center': 'top-20 left-1/2 -translate-x-1/2 items-center',
2689
+ 'bottom-right': 'bottom-6 right-6 items-end',
2690
+ 'bottom-left': 'bottom-6 left-6 items-start',
2691
+ 'bottom-center': 'bottom-6 left-1/2 -translate-x-1/2 items-center',
2692
+ };
2693
+ const ToastProvider = ({ children, placement = 'top-right', maxToasts = 5, className, }) => {
1642
2694
  const [toasts, setToasts] = React.useState([]);
1643
- const timeoutRefs = React.useRef(new Map());
2695
+ const [pausedIds, setPausedIds] = React.useState(new Set());
2696
+ const metaRefs = React.useRef(new Map());
1644
2697
  const clearTimer = (id) => {
1645
- const timeout = timeoutRefs.current.get(id);
1646
- if (timeout) {
1647
- clearTimeout(timeout);
1648
- timeoutRefs.current.delete(id);
2698
+ const meta = metaRefs.current.get(id);
2699
+ if (meta === null || meta === void 0 ? void 0 : meta.timerId) {
2700
+ clearTimeout(meta.timerId);
2701
+ meta.timerId = null;
1649
2702
  }
1650
2703
  };
1651
2704
  const closeToast = React.useCallback((id) => {
1652
2705
  clearTimer(id);
2706
+ metaRefs.current.delete(id);
2707
+ setPausedIds((prev) => {
2708
+ const next = new Set(prev);
2709
+ next.delete(id);
2710
+ return next;
2711
+ });
1653
2712
  setToasts((prev) => prev.filter((t) => t.id !== id));
1654
2713
  }, []);
1655
- const scheduleClose = React.useCallback((toast) => {
1656
- if (!toast.autoClose)
1657
- return;
1658
- clearTimer(toast.id);
1659
- const timeout = setTimeout(() => {
1660
- closeToast(toast.id);
1661
- }, toast.duration);
1662
- timeoutRefs.current.set(toast.id, timeout);
2714
+ const dismissAll = React.useCallback(() => {
2715
+ metaRefs.current.forEach((meta) => {
2716
+ if (meta.timerId)
2717
+ clearTimeout(meta.timerId);
2718
+ });
2719
+ metaRefs.current.clear();
2720
+ setPausedIds(new Set());
2721
+ setToasts([]);
2722
+ }, []);
2723
+ const startAutoClose = React.useCallback((toastId, delay) => {
2724
+ clearTimer(toastId);
2725
+ const timerId = setTimeout(() => {
2726
+ closeToast(toastId);
2727
+ }, delay);
2728
+ const meta = metaRefs.current.get(toastId);
2729
+ if (meta) {
2730
+ meta.timerId = timerId;
2731
+ meta.startTime = Date.now();
2732
+ meta.remainingTime = delay;
2733
+ }
2734
+ else {
2735
+ metaRefs.current.set(toastId, {
2736
+ timerId,
2737
+ startTime: Date.now(),
2738
+ remainingTime: delay,
2739
+ });
2740
+ }
1663
2741
  }, [closeToast]);
1664
2742
  React.useEffect(() => {
1665
- const refs = timeoutRefs.current;
1666
- return () => refs.forEach((id) => clearTimeout(id));
2743
+ const refs = metaRefs.current;
2744
+ return () => refs.forEach((meta) => {
2745
+ if (meta.timerId)
2746
+ clearTimeout(meta.timerId);
2747
+ });
1667
2748
  }, []);
1668
2749
  const showToast = React.useCallback((message, options) => {
1669
- var _a, _b, _c, _d;
2750
+ var _a, _b, _c, _d, _e, _f;
1670
2751
  const id = ++toastCounter;
1671
2752
  const newToast = {
1672
2753
  id,
2754
+ title: options === null || options === void 0 ? void 0 : options.title,
1673
2755
  message,
1674
2756
  type: (_a = options === null || options === void 0 ? void 0 : options.type) !== null && _a !== void 0 ? _a : 'info',
1675
- duration: (_b = options === null || options === void 0 ? void 0 : options.duration) !== null && _b !== void 0 ? _b : 3000,
1676
- autoClose: (_c = options === null || options === void 0 ? void 0 : options.autoClose) !== null && _c !== void 0 ? _c : true,
1677
- pauseOnHover: (_d = options === null || options === void 0 ? void 0 : options.pauseOnHover) !== null && _d !== void 0 ? _d : true,
2757
+ variant: (_b = options === null || options === void 0 ? void 0 : options.variant) !== null && _b !== void 0 ? _b : 'accent',
2758
+ duration: (_c = options === null || options === void 0 ? void 0 : options.duration) !== null && _c !== void 0 ? _c : 4000,
2759
+ autoClose: (_d = options === null || options === void 0 ? void 0 : options.autoClose) !== null && _d !== void 0 ? _d : true,
2760
+ pauseOnHover: (_e = options === null || options === void 0 ? void 0 : options.pauseOnHover) !== null && _e !== void 0 ? _e : true,
2761
+ 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,
2762
+ action: options === null || options === void 0 ? void 0 : options.action,
2763
+ icon: options === null || options === void 0 ? void 0 : options.icon,
1678
2764
  };
1679
2765
  setToasts((prev) => {
1680
2766
  const updated = [...prev, newToast];
1681
- if (updated.length > MAX_TOASTS)
2767
+ if (updated.length > maxToasts)
1682
2768
  updated.shift();
1683
2769
  return updated;
1684
2770
  });
1685
- scheduleClose(newToast);
1686
- }, [scheduleClose]);
1687
- return (jsxRuntime.jsxs(ToastContext.Provider, { value: { showToast, closeToast }, children: [children, jsxRuntime.jsx("div", { className: buildClassName('fixed z-50 flex flex-col gap-3 pointer-events-none', placementClass[placement]), children: toasts.map((toast, index) => (jsxRuntime.jsx("div", { className: "pointer-events-auto", onMouseEnter: () => toast.pauseOnHover && clearTimer(toast.id), onMouseLeave: () => toast.pauseOnHover && scheduleClose(toast), children: jsxRuntime.jsx(Toast, { ...toast, isTop: placement.startsWith('top'), onClose: () => closeToast(toast.id) }) }, toast.id))) })] }));
1688
- };
2771
+ if (newToast.autoClose) {
2772
+ startAutoClose(id, newToast.duration);
2773
+ }
2774
+ return id;
2775
+ }, [maxToasts, startAutoClose]);
2776
+ // Hover pauses timer and tracks exact remaining milliseconds
2777
+ const handleMouseEnter = (toast) => {
2778
+ if (!toast.pauseOnHover || !toast.autoClose)
2779
+ return;
2780
+ const meta = metaRefs.current.get(toast.id);
2781
+ if (meta) {
2782
+ const elapsed = Date.now() - meta.startTime;
2783
+ meta.remainingTime = Math.max(0, meta.remainingTime - elapsed);
2784
+ clearTimer(toast.id);
2785
+ setPausedIds((prev) => new Set(prev).add(toast.id));
2786
+ }
2787
+ };
2788
+ // Mouse leave resumes timer with exact remaining milliseconds
2789
+ const handleMouseLeave = (toast) => {
2790
+ if (!toast.pauseOnHover || !toast.autoClose)
2791
+ return;
2792
+ setPausedIds((prev) => {
2793
+ const next = new Set(prev);
2794
+ next.delete(toast.id);
2795
+ return next;
2796
+ });
2797
+ const meta = metaRefs.current.get(toast.id);
2798
+ if (meta && meta.remainingTime > 0) {
2799
+ startAutoClose(toast.id, meta.remainingTime);
2800
+ }
2801
+ else if (meta && meta.remainingTime <= 0) {
2802
+ closeToast(toast.id);
2803
+ }
2804
+ };
2805
+ const toastHelpers = React.useMemo(() => ({
2806
+ show: (message, options) => showToast(message, options),
2807
+ success: (message, options) => showToast(message, { ...options, type: 'success' }),
2808
+ error: (message, options) => showToast(message, { ...options, type: 'error' }),
2809
+ info: (message, options) => showToast(message, { ...options, type: 'info' }),
2810
+ warning: (message, options) => showToast(message, { ...options, type: 'warning' }),
2811
+ dismiss: (id) => closeToast(id),
2812
+ dismissAll: () => dismissAll(),
2813
+ }), [showToast, closeToast, dismissAll]);
2814
+ return (jsxRuntime.jsxs(ToastContext.Provider, { value: { showToast, closeToast, toast: toastHelpers }, children: [children, jsxRuntime.jsx("div", { className: buildClassName('fixed z-[9999] flex flex-col gap-3 pointer-events-none', placementClass[placement], className), children: toasts.map((toastItem) => (jsxRuntime.jsx("div", { className: "pointer-events-auto", onMouseEnter: () => handleMouseEnter(toastItem), onMouseLeave: () => handleMouseLeave(toastItem), children: jsxRuntime.jsx(Toast, { ...toastItem, isPaused: pausedIds.has(toastItem.id), isTop: placement.startsWith('top'), onClose: () => closeToast(toastItem.id) }) }, toastItem.id))) })] }));
2815
+ };
2816
+ ToastProvider.displayName = 'ToastProvider';
1689
2817
 
1690
2818
  const DENSITY_CELL_CLASS$1 = {
1691
2819
  compact: 'px-2 py-1.5',
@@ -1878,6 +3006,12 @@ exports.Breadcrumb = Breadcrumb;
1878
3006
  exports.Button = Button;
1879
3007
  exports.ButtonGroup = ButtonGroup;
1880
3008
  exports.Card = Card;
3009
+ exports.CardContent = CardContent;
3010
+ exports.CardDescription = CardDescription;
3011
+ exports.CardFooter = CardFooter;
3012
+ exports.CardHeader = CardHeader;
3013
+ exports.CardMedia = CardMedia;
3014
+ exports.CardTitle = CardTitle;
1881
3015
  exports.Checkbox = Checkbox;
1882
3016
  exports.CheckboxGroup = CheckboxGroup;
1883
3017
  exports.DOTS = DOTS;
@@ -1896,13 +3030,18 @@ exports.PASSWORD_PLACEHOLDER = PASSWORD_PLACEHOLDER;
1896
3030
  exports.PASSWORD_TYPE = PASSWORD_TYPE;
1897
3031
  exports.Pagination = Pagination;
1898
3032
  exports.PasswordInput = PasswordInput;
3033
+ exports.QuantityStepper = QuantityStepper;
1899
3034
  exports.Radio = Radio;
1900
3035
  exports.RadioGroup = RadioGroup;
1901
3036
  exports.RadioSwitch = RadioSwitch;
1902
3037
  exports.RangeInput = RangeInput;
1903
3038
  exports.RangeSlider = RangeSlider;
3039
+ exports.SegmentedControl = SegmentedControl;
1904
3040
  exports.SelectBox = SelectBox;
3041
+ exports.Skeleton = Skeleton;
1905
3042
  exports.SortIconMap = SortIconMap;
3043
+ exports.StatusPill = StatusPill;
3044
+ exports.StickyActionBar = StickyActionBar;
1906
3045
  exports.Table = Table;
1907
3046
  exports.TableBody = TableBody;
1908
3047
  exports.TableCell = TableCell;
@@ -1918,6 +3057,7 @@ exports.ToastProvider = ToastProvider;
1918
3057
  exports.VirtualizedDataTable = VirtualizedDataTable;
1919
3058
  exports.buildClassName = buildClassName;
1920
3059
  exports.buildInputClass = buildInputClass;
3060
+ exports.evaluatePasswordStrength = evaluatePasswordStrength;
1921
3061
  exports.get = get;
1922
3062
  exports.getSortIcon = getSortIcon;
1923
3063
  exports.isArray = isArray;