@pk-design/react-tailwind 1.7.1 → 1.8.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 (61) hide show
  1. package/dist/cjs/components/Button/Button.js +46 -28
  2. package/dist/cjs/components/Button/Button.js.map +1 -1
  3. package/dist/cjs/components/Checkbox/Checkbox.js +10 -4
  4. package/dist/cjs/components/Checkbox/Checkbox.js.map +1 -1
  5. package/dist/cjs/components/CheckboxGroup/CheckboxGroup.js +19 -13
  6. package/dist/cjs/components/CheckboxGroup/CheckboxGroup.js.map +1 -1
  7. package/dist/cjs/components/DataList/DataList.js +2 -2
  8. package/dist/cjs/components/DataList/DataList.js.map +1 -1
  9. package/dist/cjs/components/DataTable/DataTable.js +72 -40
  10. package/dist/cjs/components/DataTable/DataTable.js.map +1 -1
  11. package/dist/cjs/components/DataTable/DataTable.utils.js +2 -1
  12. package/dist/cjs/components/DataTable/DataTable.utils.js.map +1 -1
  13. package/dist/cjs/components/Input/Input.js +11 -8
  14. package/dist/cjs/components/Input/Input.js.map +1 -1
  15. package/dist/cjs/components/Pagination/Pagination.js +27 -15
  16. package/dist/cjs/components/Pagination/Pagination.js.map +1 -1
  17. package/dist/cjs/components/Radio/Radio.js +9 -5
  18. package/dist/cjs/components/Radio/Radio.js.map +1 -1
  19. package/dist/cjs/components/RadioGroup/RadioGroup.js +8 -3
  20. package/dist/cjs/components/RadioGroup/RadioGroup.js.map +1 -1
  21. package/dist/cjs/components/SelectBox/SelectBox.js +31 -28
  22. package/dist/cjs/components/SelectBox/SelectBox.js.map +1 -1
  23. package/dist/cjs/components/Textarea/Textarea.js +8 -5
  24. package/dist/cjs/components/Textarea/Textarea.js.map +1 -1
  25. package/dist/es/components/Button/Button.js +46 -28
  26. package/dist/es/components/Button/Button.js.map +1 -1
  27. package/dist/es/components/Checkbox/Checkbox.js +10 -4
  28. package/dist/es/components/Checkbox/Checkbox.js.map +1 -1
  29. package/dist/es/components/CheckboxGroup/CheckboxGroup.js +20 -14
  30. package/dist/es/components/CheckboxGroup/CheckboxGroup.js.map +1 -1
  31. package/dist/es/components/DataList/DataList.js +2 -2
  32. package/dist/es/components/DataList/DataList.js.map +1 -1
  33. package/dist/es/components/DataTable/DataTable.js +74 -42
  34. package/dist/es/components/DataTable/DataTable.js.map +1 -1
  35. package/dist/es/components/DataTable/DataTable.utils.js +2 -1
  36. package/dist/es/components/DataTable/DataTable.utils.js.map +1 -1
  37. package/dist/es/components/Input/Input.js +12 -9
  38. package/dist/es/components/Input/Input.js.map +1 -1
  39. package/dist/es/components/Pagination/Pagination.js +27 -15
  40. package/dist/es/components/Pagination/Pagination.js.map +1 -1
  41. package/dist/es/components/Radio/Radio.js +9 -5
  42. package/dist/es/components/Radio/Radio.js.map +1 -1
  43. package/dist/es/components/RadioGroup/RadioGroup.js +9 -4
  44. package/dist/es/components/RadioGroup/RadioGroup.js.map +1 -1
  45. package/dist/es/components/SelectBox/SelectBox.js +31 -28
  46. package/dist/es/components/SelectBox/SelectBox.js.map +1 -1
  47. package/dist/es/components/Textarea/Textarea.js +9 -6
  48. package/dist/es/components/Textarea/Textarea.js.map +1 -1
  49. package/dist/react-tailwind.cjs.js +271 -180
  50. package/dist/react-tailwind.cjs.js.map +1 -1
  51. package/dist/react-tailwind.es.js +271 -180
  52. package/dist/react-tailwind.es.js.map +1 -1
  53. package/dist/types/components/Button/Button.types.d.ts +5 -0
  54. package/dist/types/components/CheckboxGroup/CheckboxGroup.types.d.ts +9 -1
  55. package/dist/types/components/DataList/DataList.d.ts +1 -1
  56. package/dist/types/components/DataList/DataList.types.d.ts +8 -0
  57. package/dist/types/components/DataTable/DataTable.d.ts +1 -1
  58. package/dist/types/components/DataTable/DataTable.types.d.ts +46 -0
  59. package/dist/types/components/Pagination/Pagination.types.d.ts +21 -24
  60. package/dist/types/components/RadioGroup/RadioGroup.types.d.ts +8 -0
  61. package/package.json +1 -1
@@ -156,17 +156,17 @@ const THEMED_BTN_CLASS = {
156
156
  primary: {
157
157
  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)]',
158
158
  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)]',
159
- 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)]',
159
+ plain: 'text-[var(--ui-primary-text)] hover:bg-blue-50/80 dark:hover:bg-blue-950/40 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-primary-ring)] disabled:text-[var(--ui-primary-disabled)]',
160
160
  },
161
161
  danger: {
162
162
  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)]',
163
163
  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)]',
164
- 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)]',
164
+ plain: 'text-[var(--ui-danger-text)] hover:bg-red-50/80 dark:hover:bg-red-950/40 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-danger-ring)] disabled:text-[var(--ui-danger-disabled)]',
165
165
  },
166
166
  secondary: {
167
167
  default: 'text-gray-700 bg-white border border-[var(--ui-border)] hover:bg-gray-100 dark:bg-gray-800 dark:text-white dark:hover:bg-gray-700 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',
168
168
  outlined: 'text-gray-700 hover:text-white border border-[var(--ui-border)] hover:bg-gray-700 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-[var(--ui-border-muted)]',
169
- 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',
169
+ plain: 'text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-800 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[var(--ui-focus-ring)] disabled:text-gray-400',
170
170
  },
171
171
  success: {
172
172
  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',
@@ -206,54 +206,72 @@ const LOADER_TEXT_CLASS = {
206
206
  plain: 'text-amber-600 dark:text-amber-400',
207
207
  },
208
208
  };
209
- /**
210
- * Generate size classes.
211
- */
212
- const getBtnSizeClasses = (size, iconOnly = false) => {
213
- switch (size) {
214
- case 'xs':
215
- return iconOnly ? 'h-7 w-7 p-1 rounded-sm text-xs' : 'h-7 px-2.5 rounded-sm gap-1 text-xs';
216
- case 'sm':
217
- return iconOnly ? 'h-8 w-8 p-1.5 rounded text-sm' : 'h-8 px-3 rounded gap-1.5 text-sm';
218
- case 'md':
219
- return iconOnly ? 'h-9 w-9 p-2 rounded-md text-sm' : 'h-9 px-4 rounded-md gap-2 text-sm';
220
- case 'lg':
221
- return iconOnly
222
- ? 'h-10 w-10 p-2.5 rounded-lg text-base'
223
- : 'h-10 px-5 rounded-lg gap-2 text-base';
224
- }
209
+ const SIZE_CLASSES = {
210
+ xs: { normal: 'h-7 px-2.5 rounded-sm gap-1 text-xs', iconOnly: 'h-7 w-7 p-1 rounded-sm text-xs' },
211
+ sm: { normal: 'h-8 px-3 rounded gap-1.5 text-sm', iconOnly: 'h-8 w-8 p-1.5 rounded text-sm' },
212
+ md: { normal: 'h-9 px-4 rounded-md gap-2 text-sm', iconOnly: 'h-9 w-9 p-2 rounded-md text-sm' },
213
+ lg: {
214
+ normal: 'h-10 px-5 rounded-lg gap-2 text-base',
215
+ iconOnly: 'h-10 w-10 p-2.5 rounded-lg text-base',
216
+ },
217
+ };
218
+ const LOADER_SIZE = {
219
+ xs: 'xs',
220
+ sm: 'xs',
221
+ md: 'sm',
222
+ lg: 'sm',
225
223
  };
226
224
  /**
227
225
  * Base shared tailwind classes.
228
226
  */
229
- const BASE_BTN_CLASS = 'font-medium flex items-center justify-center shrink-0 cursor-pointer ' +
227
+ const BASE_BTN_CLASS = 'font-medium inline-flex items-center justify-center shrink-0 cursor-pointer select-none ' +
230
228
  'disabled:cursor-not-allowed disabled:pointer-events-none disabled:opacity-75 ' +
231
229
  'active:scale-[0.98] transition-all duration-150 ease-out focus:outline-none';
232
230
  const Button = React.forwardRef(({ as, type, ...props }, ref) => {
233
- 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;
231
+ var _a, _b, _c, _d;
232
+ const { className = '', children, theme = 'primary', variant = 'default', size = 'md', leftIcon, rightIcon, fullWidth = false, loading = false, loadingText = '', rounded = false, disabled, iconOnly = false, noOutlineOnFocus = false, active, onClick, ...restProps } = props;
234
233
  const Component = as || 'button';
234
+ const isNativeButton = Component === 'button';
235
235
  // Accessibility guard for icon-only buttons
236
236
  if (iconOnly && !restProps['aria-label']) {
237
237
  console.warn('[ui] <Button iconOnly> requires aria-label for accessibility.');
238
238
  }
239
- const resolvedDisabled = disabled || loading;
239
+ const resolvedDisabled = Boolean(disabled || loading);
240
240
  // Clean invalid props when not rendering a real button
241
241
  const { type: _ignoreType, disabled: _ignoreDisabled, ...cleanRest } = restProps;
242
- const buttonSpecificProps = Component === 'button'
242
+ const handleClick = (e) => {
243
+ if (resolvedDisabled) {
244
+ e.preventDefault();
245
+ e.stopPropagation();
246
+ return;
247
+ }
248
+ onClick === null || onClick === void 0 ? void 0 : onClick(e);
249
+ };
250
+ const elementProps = isNativeButton
243
251
  ? {
244
252
  type: type !== null && type !== void 0 ? type : 'button',
245
253
  disabled: resolvedDisabled,
254
+ onClick: handleClick,
246
255
  }
247
- : {};
248
- 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]);
256
+ : {
257
+ role: (_a = cleanRest.role) !== null && _a !== void 0 ? _a : 'button',
258
+ tabIndex: resolvedDisabled ? -1 : ((_b = cleanRest.tabIndex) !== null && _b !== void 0 ? _b : 0),
259
+ 'aria-disabled': resolvedDisabled ? true : undefined,
260
+ onClick: handleClick,
261
+ };
262
+ const sizeObj = SIZE_CLASSES[size] || SIZE_CLASSES.md;
263
+ const classes = React.useMemo(() => {
264
+ var _a;
265
+ return buildClassName(BASE_BTN_CLASS, ((_a = THEMED_BTN_CLASS[theme]) === null || _a === void 0 ? void 0 : _a[variant]) || THEMED_BTN_CLASS.primary.default, iconOnly ? sizeObj.iconOnly : sizeObj.normal, !noOutlineOnFocus && 'focus-visible:ring-2 focus-visible:ring-offset-2', rounded && 'rounded-full', fullWidth && 'w-full', variant === 'default' && 'shadow-xs hover:shadow-none disabled:shadow-none', active && 'ring-2 ring-offset-2 ring-current', className);
266
+ }, [theme, variant, sizeObj, iconOnly, noOutlineOnFocus, rounded, fullWidth, active, className]);
267
+ const loaderIcon = (jsxRuntime.jsx(Loader, { "data-testid": "btn-loader-icon", size: LOADER_SIZE[size] || 'xs', className: buildClassName((_c = LOADER_TEXT_CLASS[theme]) === null || _c === void 0 ? void 0 : _c[variant]) }));
249
268
  /**
250
269
  * SPECIAL CASE: iconOnly + loading → show spinner only
251
270
  */
252
271
  if (loading && iconOnly) {
253
- 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]) }) }));
272
+ return (jsxRuntime.jsx(Component, { ref: ref, className: classes, "aria-busy": "true", "aria-pressed": active !== undefined ? active : undefined, ...elementProps, ...cleanRest, children: loaderIcon }));
254
273
  }
255
- const loaderIcon = (jsxRuntime.jsx(Loader, { "data-testid": "btn-loader-icon", size: "xs", className: buildClassName(LOADER_TEXT_CLASS[theme][variant]) }));
256
- 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 })] })) }));
274
+ return (jsxRuntime.jsx(Component, { ref: ref, className: classes, "aria-busy": loading ? 'true' : undefined, "aria-pressed": active !== undefined ? active : undefined, ...elementProps, ...cleanRest, children: loading ? (jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2", "aria-live": "polite", children: [loaderIcon, loadingText ? (jsxRuntime.jsx(TextContent, { className: buildClassName((_d = LOADER_TEXT_CLASS[theme]) === null || _d === void 0 ? void 0 : _d[variant]), children: loadingText })) : (children)] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [leftIcon && jsxRuntime.jsx("span", { className: "shrink-0 inline-flex items-center", children: leftIcon }), children, rightIcon && jsxRuntime.jsx("span", { className: "shrink-0 inline-flex items-center", children: rightIcon })] })) }));
257
275
  });
258
276
  Button.displayName = 'Button';
259
277
 
@@ -900,6 +918,34 @@ const Label = React.forwardRef((props, ref) => {
900
918
  });
901
919
  Label.displayName = 'Label';
902
920
 
921
+ /**
922
+ * Checks if a value is an array.
923
+ * Equivalent to lodash's _.isArray.
924
+ */
925
+ function isArray(value) {
926
+ return Array.isArray(value);
927
+ }
928
+
929
+ /**
930
+ * Checks if a value is empty.
931
+ * Works for arrays, objects, strings, Maps, and Sets.
932
+ * Equivalent to lodash's _.isEmpty.
933
+ */
934
+ function isEmpty(value) {
935
+ if (value == null)
936
+ return true; // null or undefined
937
+ if (typeof value === 'string' || isArray(value)) {
938
+ return value.length === 0;
939
+ }
940
+ if (value instanceof Map || value instanceof Set) {
941
+ return value.size === 0;
942
+ }
943
+ if (typeof value === 'object') {
944
+ return Object.keys(value).length === 0;
945
+ }
946
+ return false; // for numbers, booleans, functions, etc.
947
+ }
948
+
903
949
  const sizeStyles = {
904
950
  sm: {
905
951
  box: 'size-3.5',
@@ -922,7 +968,7 @@ const sizeStyles = {
922
968
  };
923
969
  const Checkbox = React.forwardRef((props, ref) => {
924
970
  var _a;
925
- const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, helperText, disabled = false, indeterminate = false, size = 'md', variant = 'default', description, className = '', checked, onChange, ...rest } = props;
971
+ const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, helperText, disabled = false, indeterminate = false, size = 'md', variant = 'default', description, className = '', checked, onChange, required, 'aria-describedby': ariaDescribedBy, ...rest } = props;
926
972
  const generatedId = React.useId();
927
973
  const checkboxId = (_a = id !== null && id !== void 0 ? id : name) !== null && _a !== void 0 ? _a : generatedId;
928
974
  const innerRef = React.useRef(null);
@@ -934,6 +980,11 @@ const Checkbox = React.forwardRef((props, ref) => {
934
980
  }, [indeterminate]);
935
981
  const currentSize = sizeStyles[size] || sizeStyles.md;
936
982
  const isCard = variant === 'card';
983
+ const hasError = !isEmpty(error);
984
+ const errorId = checkboxId && hasError ? `${checkboxId}-error` : undefined;
985
+ const helperId = checkboxId && helperText && !hasError ? `${checkboxId}-helper` : undefined;
986
+ const descId = checkboxId && description ? `${checkboxId}-desc` : undefined;
987
+ const describedBy = [ariaDescribedBy, errorId, helperId, descId].filter(Boolean).join(' ') || undefined;
937
988
  return (jsxRuntime.jsxs("div", { className: buildClassName(wrapperClass), children: [jsxRuntime.jsx("div", { className: buildClassName(isCard
938
989
  ? 'rounded-xl border p-3.5 transition-all duration-150 relative'
939
990
  : 'flex items-center', isCard &&
@@ -941,16 +992,16 @@ const Checkbox = React.forwardRef((props, ref) => {
941
992
  ? 'border-[var(--ui-border-muted)] bg-gray-50/50 dark:bg-gray-900/40 opacity-70'
942
993
  : checked || indeterminate
943
994
  ? 'border-blue-500/80 bg-blue-50/30 dark:bg-blue-950/20 dark:border-blue-500/60 shadow-xs'
944
- : 'border-[var(--ui-border)] 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 relative select-none w-full', description ? 'items-start' : 'items-center', disabled ? 'cursor-not-allowed opacity-70' : 'cursor-pointer'), children: [jsxRuntime.jsxs("div", { className: "relative inline-flex items-center justify-center shrink-0", 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
995
+ : 'border-[var(--ui-border)] 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 relative select-none w-full', description ? 'items-start' : 'items-center', disabled ? 'cursor-not-allowed opacity-70' : 'cursor-pointer'), children: [jsxRuntime.jsxs("div", { className: "relative inline-flex items-center justify-center shrink-0", children: [jsxRuntime.jsx("input", { ref: innerRef, ...rest, id: checkboxId, name: name, type: "checkbox", disabled: disabled, checked: checked, onChange: onChange, required: required, "aria-required": required ? true : undefined, "aria-invalid": hasError ? true : undefined, "aria-describedby": describedBy, "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
945
996
  ? 'border-[var(--ui-border-muted)] bg-gray-100 dark:bg-gray-800 pointer-events-none'
946
997
  : 'bg-white dark:bg-gray-700 border-[var(--ui-border)] hover:border-gray-400 dark:hover:border-gray-500', indeterminate &&
947
998
  '!bg-blue-600 !border-blue-600 dark:!bg-blue-600 dark:!border-blue-600', className) }), !indeterminate && (jsxRuntime.jsx("span", { className: buildClassName('absolute inset-0 flex items-center justify-center text-white pointer-events-none transition-opacity', checked !== undefined
948
999
  ? checked
949
1000
  ? 'opacity-100'
950
1001
  : 'opacity-0'
951
- : 'opacity-0 peer-checked:opacity-100'), children: jsxRuntime.jsx("svg", { className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", 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 inset-0 flex items-center justify-center text-white pointer-events-none opacity-100 z-10", children: jsxRuntime.jsx("svg", { className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", 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
1002
+ : 'opacity-0 peer-checked:opacity-100'), children: jsxRuntime.jsx("svg", { className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", 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 inset-0 flex items-center justify-center text-white pointer-events-none opacity-100 z-10", children: jsxRuntime.jsx("svg", { className: currentSize.svg, viewBox: "0 0 20 20", fill: "currentColor", 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, "aria-required": required, className: buildClassName(disabled
952
1003
  ? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
953
- : '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 })) }))] }));
1004
+ : 'cursor-pointer text-gray-800 dark:text-gray-200 font-medium', currentSize.text, labelClass), children: label })), description && (jsxRuntime.jsx("span", { id: descId, 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, id: errorId, children: error })) : (jsxRuntime.jsx(TextContent, { muted: true, small: true, id: helperId, children: helperText })) }))] }));
954
1005
  });
955
1006
  Checkbox.displayName = 'Checkbox';
956
1007
 
@@ -961,25 +1012,26 @@ const columnClasses = {
961
1012
  4: 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3',
962
1013
  };
963
1014
  const CheckboxGroup = (props) => {
964
- 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;
1015
+ 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, required, 'aria-describedby': ariaDescribedBy, } = props;
965
1016
  const groupId = React.useId();
966
1017
  const [internalValue, setInternalValue] = React.useState(defaultValue || []);
967
1018
  const isControlled = controlledValue !== undefined;
968
- const currentValue = isControlled ? controlledValue : internalValue;
1019
+ const currentValue = isControlled ? controlledValue || [] : internalValue;
969
1020
  const items = React.useMemo(() => options.map((opt) => typeof opt === 'string' ? { label: opt, value: opt } : opt), [options]);
970
1021
  const enabledItems = React.useMemo(() => items.filter((i) => !i.disabled && !disabled), [items, disabled]);
1022
+ const currentValueKeySet = React.useMemo(() => new Set(currentValue), [currentValue]);
971
1023
  // Select All State calculation
972
1024
  const allSelected = React.useMemo(() => {
973
1025
  if (!enabledItems.length)
974
1026
  return false;
975
- return enabledItems.every((item) => currentValue.includes(item.value));
976
- }, [enabledItems, currentValue]);
1027
+ return enabledItems.every((item) => currentValueKeySet.has(item.value));
1028
+ }, [enabledItems, currentValueKeySet]);
977
1029
  const isIndeterminate = React.useMemo(() => {
978
1030
  if (allSelected)
979
1031
  return false;
980
- return enabledItems.some((item) => currentValue.includes(item.value));
981
- }, [allSelected, enabledItems, currentValue]);
982
- const handleSelectAllChange = (e) => {
1032
+ return enabledItems.some((item) => currentValueKeySet.has(item.value));
1033
+ }, [allSelected, enabledItems, currentValueKeySet]);
1034
+ const handleSelectAllChange = React.useCallback((e) => {
983
1035
  let updated;
984
1036
  if (allSelected || isIndeterminate) {
985
1037
  const enabledValues = new Set(enabledItems.map((i) => i.value));
@@ -994,8 +1046,8 @@ const CheckboxGroup = (props) => {
994
1046
  setInternalValue(updated);
995
1047
  }
996
1048
  onChange === null || onChange === void 0 ? void 0 : onChange(updated, e);
997
- };
998
- const handleGroupChange = (e) => {
1049
+ }, [allSelected, isIndeterminate, enabledItems, currentValue, isControlled, onChange]);
1050
+ const handleGroupChange = React.useCallback((e) => {
999
1051
  const raw = e.target.value;
1000
1052
  const checked = e.target.checked;
1001
1053
  const matched = items.find((i) => String(i.value) === raw);
@@ -1011,16 +1063,21 @@ const CheckboxGroup = (props) => {
1011
1063
  setInternalValue(updated);
1012
1064
  }
1013
1065
  onChange === null || onChange === void 0 ? void 0 : onChange(updated, e);
1014
- };
1066
+ }, [items, currentValue, isControlled, onChange]);
1067
+ const hasError = Boolean(error);
1068
+ const labelId = label ? `${groupId}-label` : undefined;
1069
+ const errorId = groupId && hasError ? `${groupId}-error` : undefined;
1070
+ const helperId = groupId && helperText && !hasError ? `${groupId}-helper` : undefined;
1071
+ const describedBy = [ariaDescribedBy, errorId, helperId].filter(Boolean).join(' ') || undefined;
1015
1072
  const layoutClass = columns
1016
1073
  ? columnClasses[columns]
1017
1074
  : inline
1018
1075
  ? 'flex flex-wrap gap-3'
1019
1076
  : 'flex flex-col gap-3';
1020
- 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-[var(--ui-border)]", 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) => {
1077
+ return (jsxRuntime.jsxs("div", { role: "group", "aria-labelledby": labelId, "aria-describedby": describedBy, 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, { id: labelId, className: labelClass, "aria-required": required, 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-[var(--ui-border)]", 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) => {
1021
1078
  const key = typeof item.value === 'string' || typeof item.value === 'number' ? item.value : index;
1022
- 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));
1023
- }) }), (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 })) }))] }));
1079
+ 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: currentValueKeySet.has(item.value), onChange: handleGroupChange }, key));
1080
+ }) }), (error || helperText) && (jsxRuntime.jsx("div", { className: "mt-1.5 ml-0.5", children: error ? (jsxRuntime.jsx(TextContent, { error: true, small: true, id: errorId, children: error })) : (jsxRuntime.jsx(TextContent, { muted: true, small: true, id: helperId, children: helperText })) }))] }));
1024
1081
  };
1025
1082
  CheckboxGroup.displayName = 'CheckboxGroup';
1026
1083
 
@@ -1114,27 +1171,39 @@ var SvgChevronRight = function SvgChevronRight(props) {
1114
1171
  })));
1115
1172
  };
1116
1173
 
1117
- const Pagination = ({ page: currentPage, limit, total, onChange, className, ellipsis = true, // Default to showing ellipsis
1118
- }) => {
1119
- const pages = new Array(Math.ceil(total / limit)).fill(null).map((_, i) => i + 1);
1120
- const isFirstPage = currentPage === 1;
1121
- const isLastPage = total === 0 || pages.length === currentPage;
1122
- const range = usePagination({ totalPages: pages.length, currentPage });
1174
+ const Pagination = (props) => {
1175
+ var _a, _b, _c, _d, _e, _f;
1176
+ const { className, ellipsis = true, onChange, isMobile, showSummary = true, showPageNumbers = true, } = props;
1177
+ const currentPage = (_c = (_b = (_a = props.page) !== null && _a !== void 0 ? _a : props.current) !== null && _b !== void 0 ? _b : props.currentPage) !== null && _c !== void 0 ? _c : 1;
1178
+ const limit = (_e = (_d = props.limit) !== null && _d !== void 0 ? _d : props.pageSize) !== null && _e !== void 0 ? _e : 10;
1179
+ const total = (_f = props.total) !== null && _f !== void 0 ? _f : 0;
1180
+ const totalPages = Math.max(1, Math.ceil(total / limit));
1181
+ const isFirstPage = currentPage <= 1;
1182
+ const isLastPage = total === 0 || currentPage >= totalPages;
1183
+ const range = usePagination({ totalPages, currentPage });
1123
1184
  const handlePageChange = (nextVal) => {
1124
- if (nextVal === currentPage)
1185
+ if (nextVal === currentPage || nextVal < 1 || nextVal > totalPages)
1125
1186
  return;
1126
1187
  onChange({ page: nextVal });
1127
1188
  };
1128
- if (ellipsis) {
1129
- return (jsxRuntime.jsxs("div", { "data-testid": "pagination-container", className: buildClassName('flex items-center justify-between bg-white dark:bg-gray-800 px-4 py-3 sm:px-6', className), children: [jsxRuntime.jsxs("div", { className: "flex flex-1 justify-between sm:hidden", children: [jsxRuntime.jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": "ellipsis-mobile-prev-btn", children: [jsxRuntime.jsx("span", { children: "Previous" }), jsxRuntime.jsx(SvgChevronLeft, { className: "size-4" })] }), jsxRuntime.jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": "ellipsis-mobile-next-btn", children: [jsxRuntime.jsx("span", { children: "Next" }), jsxRuntime.jsx(SvgChevronRight, { className: "size-4" })] })] }), jsxRuntime.jsxs("div", { className: "hidden sm:flex sm:flex-1 sm:items-center sm:justify-between", children: [jsxRuntime.jsxs("p", { "data-testid": "pagination-summary", className: "text-sm text-gray-600 dark:text-gray-400 flex items-center gap-1", children: ["Showing", jsxRuntime.jsx("span", { className: "font-medium", children: total === 0 ? 0 : Math.max(currentPage * limit - limit + 1, 1) }), "to", jsxRuntime.jsx("span", { className: "font-medium", children: Math.min(currentPage * limit, total) }), "of", jsxRuntime.jsx("span", { className: "font-medium", children: total }), "results"] }), jsxRuntime.jsxs("ul", { className: "flex items-center space-x-2 text-sm", children: [jsxRuntime.jsx("li", { children: jsxRuntime.jsx(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": "ellipsis-prev-btn", children: "Prev" }) }), range.map((page, idx) => {
1130
- if (page === DOTS) {
1131
- return (jsxRuntime.jsx("li", { "data-testid": `page-${page}`, className: "px-2 py-1 text-gray-400", children: "\u2026" }, `${page}-${idx}`));
1132
- }
1133
- return (jsxRuntime.jsx("li", { children: jsxRuntime.jsx(Button, { size: "xs", theme: page === currentPage ? 'primary' : 'secondary', variant: page === currentPage ? 'default' : 'plain', "aria-current": page === currentPage ? 'page' : undefined, onClick: () => handlePageChange(page), children: page }) }, `${page}-${idx}`));
1134
- }), jsxRuntime.jsx("li", { children: jsxRuntime.jsx(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": "ellipsis-next-btn", children: "Next" }) })] })] })] }));
1189
+ const startResult = total === 0 ? 0 : Math.max((currentPage - 1) * limit + 1, 1);
1190
+ const endResult = Math.min(currentPage * limit, total);
1191
+ // Explicit Mobile UX (when isMobile=true)
1192
+ if (isMobile) {
1193
+ return (jsxRuntime.jsxs("div", { "data-testid": "pagination-container", className: buildClassName('flex items-center justify-between bg-white dark:bg-gray-800 px-4 py-3 sm:px-6 rounded-xl border border-[var(--ui-border-muted)] w-full', className), children: [jsxRuntime.jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": ellipsis ? 'ellipsis-mobile-prev-btn' : 'mobile-prev-btn', className: "flex items-center gap-1 font-medium", children: [jsxRuntime.jsx(SvgChevronLeft, { className: "size-4" }), jsxRuntime.jsx("span", { children: "Previous" })] }), jsxRuntime.jsxs("span", { className: "text-xs font-semibold text-gray-700 dark:text-gray-300", children: ["Page ", currentPage, " of ", totalPages] }), jsxRuntime.jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": ellipsis ? 'ellipsis-mobile-next-btn' : 'mobile-next-btn', className: "flex items-center gap-1 font-medium", children: [jsxRuntime.jsx("span", { children: "Next" }), jsxRuntime.jsx(SvgChevronRight, { className: "size-4" })] })] }));
1135
1194
  }
1136
- return (jsxRuntime.jsxs("div", { "data-testid": "pagination-container", className: buildClassName('flex items-center justify-between bg-white dark:bg-gray-800 px-4 py-3 sm:px-6', className), children: [jsxRuntime.jsxs("div", { className: "flex flex-1 justify-between sm:hidden", children: [jsxRuntime.jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": "mobile-prev-btn", children: [jsxRuntime.jsx("span", { children: "Previous" }), jsxRuntime.jsx(SvgChevronLeft, { className: "size-4" })] }), jsxRuntime.jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": "mobile-next-btn", children: [jsxRuntime.jsx("span", { children: "Next" }), jsxRuntime.jsx(SvgChevronRight, { className: "size-4" })] })] }), jsxRuntime.jsxs("div", { className: "hidden sm:flex sm:flex-1 sm:items-center sm:justify-between", children: [jsxRuntime.jsxs("p", { "data-testid": "pagination-summary", className: "text-sm text-gray-600 dark:text-gray-400 flex items-center gap-1", children: ["Showing", jsxRuntime.jsx("span", { className: "font-medium", children: total === 0 ? 0 : Math.max(currentPage * limit - limit + 1, 1) }), "to", jsxRuntime.jsx("span", { className: "font-medium", children: Math.min(currentPage * limit, total) }), "of", jsxRuntime.jsx("span", { className: "font-medium", children: total }), "results"] }), jsxRuntime.jsxs("nav", { className: "isolate inline-flex items-center rounded-md", "aria-label": "Pagination", children: [jsxRuntime.jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": "prev-btn", children: [jsxRuntime.jsx("span", { className: "sr-only", children: "Previous" }), jsxRuntime.jsx(SvgChevronLeft, { className: "size-4" })] }), pages.map((page) => (jsxRuntime.jsx(Button, { size: "xs", theme: page === currentPage ? 'primary' : 'secondary', variant: page === currentPage ? 'default' : 'plain', "aria-current": page === currentPage ? 'page' : undefined, onClick: () => handlePageChange(page), children: jsxRuntime.jsx("span", { className: "size-4 block", children: page }) }, page))), jsxRuntime.jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": "next-btn", children: [jsxRuntime.jsx("span", { className: "sr-only", children: "Next" }), jsxRuntime.jsx(SvgChevronRight, { className: "size-4" })] })] })] })] }));
1195
+ // Standard Responsive View
1196
+ return (jsxRuntime.jsxs("div", { "data-testid": "pagination-container", className: buildClassName('flex items-center justify-between bg-white dark:bg-gray-800 px-4 py-3 sm:px-6 rounded-xl border border-[var(--ui-border-muted)] w-full', className), children: [jsxRuntime.jsxs("div", { className: "flex flex-1 items-center justify-between sm:hidden", children: [jsxRuntime.jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": ellipsis ? 'ellipsis-mobile-prev-btn' : 'mobile-prev-btn', className: "flex items-center gap-1", children: [jsxRuntime.jsx(SvgChevronLeft, { className: "size-4" }), jsxRuntime.jsx("span", { children: "Previous" })] }), jsxRuntime.jsxs("span", { className: "text-xs font-semibold text-gray-700 dark:text-gray-300", children: ["Page ", currentPage, " of ", totalPages] }), jsxRuntime.jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": ellipsis ? 'ellipsis-mobile-next-btn' : 'mobile-next-btn', className: "flex items-center gap-1", children: [jsxRuntime.jsx("span", { children: "Next" }), jsxRuntime.jsx(SvgChevronRight, { className: "size-4" })] })] }), jsxRuntime.jsxs("div", { className: "hidden sm:flex sm:flex-1 sm:items-center sm:justify-between", children: [showSummary && (jsxRuntime.jsxs("p", { "data-testid": "pagination-summary", className: "text-sm text-gray-600 dark:text-gray-400 flex items-center gap-1", children: ["Showing", jsxRuntime.jsx("span", { className: "font-medium", children: startResult }), "to", jsxRuntime.jsx("span", { className: "font-medium", children: endResult }), "of", jsxRuntime.jsx("span", { className: "font-medium", children: total }), "results"] })), showPageNumbers && (jsxRuntime.jsxs("ul", { className: "flex items-center space-x-2 text-sm", children: [jsxRuntime.jsx("li", { children: jsxRuntime.jsx(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": ellipsis ? 'ellipsis-prev-btn' : 'prev-btn', children: ellipsis ? 'Prev' : 'Previous' }) }), ellipsis
1197
+ ? range.map((pageItem, idx) => {
1198
+ if (pageItem === DOTS) {
1199
+ return (jsxRuntime.jsx("li", { "data-testid": `page-${pageItem}`, className: "px-2 py-1 text-gray-400", children: "\u2026" }, `${pageItem}-${idx}`));
1200
+ }
1201
+ const pageNum = Number(pageItem);
1202
+ return (jsxRuntime.jsx("li", { children: jsxRuntime.jsx(Button, { size: "xs", theme: pageNum === currentPage ? 'primary' : 'secondary', variant: pageNum === currentPage ? 'default' : 'plain', "aria-current": pageNum === currentPage ? 'page' : undefined, onClick: () => handlePageChange(pageNum), children: pageNum }) }, `${pageItem}-${idx}`));
1203
+ })
1204
+ : Array.from({ length: totalPages }, (_, i) => i + 1).map((pageNum) => (jsxRuntime.jsx("li", { children: jsxRuntime.jsx(Button, { size: "xs", theme: pageNum === currentPage ? 'primary' : 'secondary', variant: pageNum === currentPage ? 'default' : 'plain', "aria-current": pageNum === currentPage ? 'page' : undefined, onClick: () => handlePageChange(pageNum), children: jsxRuntime.jsx("span", { className: "size-4 block", children: pageNum }) }) }, pageNum))), jsxRuntime.jsx("li", { children: jsxRuntime.jsx(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": ellipsis ? 'ellipsis-next-btn' : 'next-btn', children: ellipsis ? 'Next' : 'Next' }) })] }))] })] }));
1137
1205
  };
1206
+ Pagination.displayName = 'Pagination';
1138
1207
 
1139
1208
  function get(obj, path, defaultValue) {
1140
1209
  var _a;
@@ -1221,9 +1290,10 @@ const useRowSelection = ({ items, getKey, selectedRowKeys, defaultSelectedRowKey
1221
1290
  emit(Array.from(new Set([...keys, ...selectableKeySet])));
1222
1291
  }
1223
1292
  }, [allSelected, selectableEntries, keys, emit]);
1293
+ const isSelected = React.useCallback((key) => keySet.has(key), [keySet]);
1224
1294
  return {
1225
1295
  selectedKeys: keys,
1226
- isSelected: (key) => keySet.has(key),
1296
+ isSelected,
1227
1297
  toggleRow,
1228
1298
  toggleAll,
1229
1299
  allSelected,
@@ -1334,6 +1404,8 @@ const SortIconMap = {
1334
1404
  };
1335
1405
  const getSortIcon = (sort) => sort ? (SortIconMap[sort]) : (jsxRuntime.jsx(SvgArrowUpDown, { className: "size-4 text-gray-400 shrink-0 group-hover:text-gray-800 dark:group-hover:text-white" }));
1336
1406
 
1407
+ const OVERFLOW_REGEX = /\boverflow-(x-|y-)?(auto|hidden|scroll|visible|clip)\b/;
1408
+ const NOOP$1 = () => { };
1337
1409
  const DENSITY_CELL_CLASS$2 = {
1338
1410
  compact: 'px-2 py-1.5',
1339
1411
  default: '',
@@ -1363,41 +1435,33 @@ const autoSortItems = (items, column, sort) => {
1363
1435
  return 0;
1364
1436
  });
1365
1437
  };
1366
- function DataTable({ items, columns, sorting = {}, onSort, loading, pagination, setPagination = () => { }, emptyMessage = 'No data found', containerClass = '', wrapperClass = '', tableClass = '', headClass = '', footerRow, rowKey = 'id', onRowClick, rowClass, density = 'default', striped = false, layout = 'auto', selectable = false, selectedRowKeys, defaultSelectedRowKeys, onSelectionChange, isRowSelectable, stickyHeader = true, stickyPagination = true, paginationContainerClass, }) {
1367
- const internalSorting = React.useRef({});
1368
- const [sortedItems, setSortedItems] = React.useState(items);
1369
- React.useEffect(() => {
1370
- setSortedItems(items);
1371
- }, [items]);
1372
- const doAutoSort = (column) => {
1373
- internalSorting.current = updateSortQuery(internalSorting.current, column.name, column.type);
1374
- setSortedItems(autoSortItems(items, column, internalSorting.current[column.name]));
1375
- };
1376
- const handleSort = (column) => {
1438
+ function DataTable({ items, columns, sorting = {}, onSort, loading, pagination, setPagination = NOOP$1, emptyMessage = 'No data found', containerClass = '', wrapperClass = '', tableClass = '', headClass = '', footerRow, rowKey = 'id', onRowClick, rowClass, density = 'default', striped = false, layout = 'auto', selectable = false, selectedRowKeys, defaultSelectedRowKeys, onSelectionChange, isRowSelectable, stickyHeader = true, stickyPagination = true, rowHoverActions, renderRowHoverActions, rowHoverActionHeader, rowHoverActionMode = 'overlay', rowHoverActionClass, paginationClass, paginationContainerClass, paginationProps, renderPagination, }) {
1439
+ const [internalSortingState, setInternalSortingState] = React.useState({});
1440
+ const handleSort = React.useCallback((column) => {
1377
1441
  if (!column.sortable)
1378
1442
  return;
1379
- onSort ? onSort(column) : doAutoSort(column);
1380
- };
1381
- const getKey = (item, idx) => {
1382
- var _a;
1383
- if (typeof rowKey === 'function')
1384
- return rowKey(item);
1385
- return (_a = item[rowKey]) !== null && _a !== void 0 ? _a : idx;
1386
- };
1387
- const getRowClass = (item, idx) => {
1388
- if (!rowClass)
1389
- return '';
1390
- if (typeof rowClass === 'function')
1391
- return rowClass(item, idx);
1392
- return rowClass;
1393
- };
1394
- const getCellClass = (column, item) => {
1395
- if (!column.cellClass)
1396
- return '';
1397
- if (typeof column.cellClass === 'function')
1398
- return column.cellClass(item);
1399
- return column.cellClass;
1400
- };
1443
+ if (onSort) {
1444
+ onSort(column);
1445
+ }
1446
+ else {
1447
+ setInternalSortingState((prev) => updateSortQuery(prev, column.name, column.type));
1448
+ }
1449
+ }, [onSort]);
1450
+ const activeSorting = React.useMemo(() => ({ ...sorting, ...internalSortingState }), [sorting, internalSortingState]);
1451
+ const sortedItems = React.useMemo(() => {
1452
+ if (onSort)
1453
+ return items;
1454
+ const activeSortKey = Object.keys(activeSorting)[0];
1455
+ if (!activeSortKey)
1456
+ return items;
1457
+ const column = columns.find((c) => c.name === activeSortKey);
1458
+ if (!column)
1459
+ return items;
1460
+ return autoSortItems(items, column, activeSorting[activeSortKey]);
1461
+ }, [items, columns, onSort, activeSorting]);
1462
+ const getKey = React.useCallback((item, idx) => { var _a; return typeof rowKey === 'function' ? rowKey(item) : ((_a = item[rowKey]) !== null && _a !== void 0 ? _a : idx); }, [rowKey]);
1463
+ const getRowClass = React.useCallback((item, idx) => typeof rowClass === 'function' ? rowClass(item, idx) : rowClass || '', [rowClass]);
1464
+ const getCellClass = React.useCallback((column, item) => typeof column.cellClass === 'function' ? column.cellClass(item) : column.cellClass || '', []);
1401
1465
  const { isSelected, toggleRow, toggleAll, allSelected, someSelected } = useRowSelection({
1402
1466
  items: sortedItems,
1403
1467
  getKey,
@@ -1406,15 +1470,50 @@ function DataTable({ items, columns, sorting = {}, onSort, loading, pagination,
1406
1470
  onSelectionChange,
1407
1471
  isRowSelectable,
1408
1472
  });
1473
+ const hasRowHoverActions = Boolean(rowHoverActions || renderRowHoverActions);
1474
+ const isInlineMode = hasRowHoverActions && rowHoverActionMode === 'inline';
1475
+ const isOverlayMode = hasRowHoverActions && rowHoverActionMode !== 'inline';
1409
1476
  const densityCellClass = DENSITY_CELL_CLASS$2[density];
1410
- const colSize = columns.length + (selectable ? 1 : 0);
1411
- const wrapperHasOverflow = /\boverflow-(x-|y-)?(auto|hidden|scroll|visible|clip)\b/.test(wrapperClass);
1477
+ const colSize = columns.length + (selectable ? 1 : 0) + (isInlineMode ? 1 : 0);
1478
+ const wrapperHasOverflow = OVERFLOW_REGEX.test(wrapperClass);
1479
+ const renderActionsContent = React.useCallback((item, idx) => {
1480
+ if (renderRowHoverActions)
1481
+ return renderRowHoverActions(item, idx);
1482
+ if (!rowHoverActions)
1483
+ return null;
1484
+ return rowHoverActions(item, idx).map((act) => {
1485
+ const isDisabled = typeof act.disabled === 'function' ? act.disabled(item) : act.disabled;
1486
+ const isIconOnly = Boolean(act.icon && !act.label);
1487
+ return (jsxRuntime.jsxs(Button, { size: "xs", theme: act.theme || 'secondary', variant: act.variant || 'plain', disabled: isDisabled, title: act.title || (typeof act.label === 'string' ? act.label : undefined), className: buildClassName(isIconOnly
1488
+ ? 'p-1.5 rounded-full hover:bg-black/5 dark:hover:bg-white/10'
1489
+ : 'px-2 py-1', act.className), onClick: (e) => {
1490
+ e.stopPropagation();
1491
+ act.onClick(item, idx, e);
1492
+ }, children: [act.icon && jsxRuntime.jsx("span", { className: "shrink-0", children: act.icon }), act.label && jsxRuntime.jsx("span", { children: act.label })] }, act.id));
1493
+ });
1494
+ }, [renderRowHoverActions, rowHoverActions]);
1412
1495
  return (jsxRuntime.jsxs("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: [jsxRuntime.jsx("div", { className: buildClassName(!wrapperHasOverflow && 'overflow-x-auto', 'rounded-lg border border-[var(--ui-border)]', wrapperClass, pagination && 'rounded-b-none border-b-0'), children: jsxRuntime.jsxs(Table, { className: buildClassName('relative w-full max-w-full h-fit', tableClass), layout: layout, children: [jsxRuntime.jsxs(TableHead, { className: headClass, children: [selectable && (jsxRuntime.jsx(TableHeaderCell, { className: buildClassName('bg-gray-50 dark:bg-gray-800 w-px', densityCellClass, stickyHeader && 'sticky top-0 z-20 shadow-[0_1px_2px_rgba(0,0,0,0.06)]'), children: jsxRuntime.jsx(Checkbox, { "aria-label": "Select all rows", checked: allSelected, ref: (el) => {
1413
1496
  if (el)
1414
1497
  el.indeterminate = someSelected;
1415
- }, onChange: toggleAll }) })), columns.map((column) => (jsxRuntime.jsx(TableHeaderCell, { align: column.headerAlign, style: { width: column.width || 'auto' }, className: buildClassName('bg-gray-50 dark:bg-gray-800', densityCellClass, stickyHeader && 'sticky top-0 z-20 shadow-[0_1px_2px_rgba(0,0,0,0.06)]', column.sticky === 'left' &&
1416
- 'sticky left-0 z-30 shadow-[2px_0_6px_rgba(0,0,0,0.06)]', column.sticky === 'right' &&
1417
- 'sticky right-0 z-30 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]', column.headerClass), children: jsxRuntime.jsxs(TextContent, { role: column.sortable ? 'button' : undefined, className: buildClassName('group', column.sortable && 'inline-flex items-center gap-2 cursor-pointer select-none'), onClick: () => handleSort(column), children: [jsxRuntime.jsx(TextContent, { className: "shrink-0", children: column.label }), column.sortable ? getSortIcon(sorting[column.name]) : null] }) }, column.name)))] }), jsxRuntime.jsx(TableBody, { loading: loading, colSize: colSize, rowSize: (pagination === null || pagination === void 0 ? void 0 : pagination.limit) || 5, children: !loading && sortedItems.length === 0 ? (jsxRuntime.jsx(EmptyTableRow, { colSpan: colSize, children: emptyMessage })) : (sortedItems.map((item, idx) => {
1498
+ }, onChange: toggleAll }) })), columns.map((column) => {
1499
+ const sortState = activeSorting[column.name];
1500
+ const ariaSortValue = column.sortable
1501
+ ? sortState === 'asc' || sortState === '1'
1502
+ ? 'ascending'
1503
+ : sortState === 'desc' || sortState === '-1'
1504
+ ? 'descending'
1505
+ : 'none'
1506
+ : undefined;
1507
+ return (jsxRuntime.jsx(TableHeaderCell, { align: column.headerAlign, style: { width: column.width || 'auto' }, "aria-sort": ariaSortValue, className: buildClassName('bg-gray-50 dark:bg-gray-800', densityCellClass, stickyHeader && 'sticky top-0 z-20 shadow-[0_1px_2px_rgba(0,0,0,0.06)]', column.sticky === 'left' &&
1508
+ 'sticky left-0 z-30 shadow-[2px_0_6px_rgba(0,0,0,0.06)]', column.sticky === 'right' &&
1509
+ 'sticky right-0 z-30 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]', column.headerClass), children: jsxRuntime.jsxs(TextContent, { role: column.sortable ? 'button' : undefined, tabIndex: column.sortable ? 0 : undefined, "aria-label": column.sortable ? `Sort by ${column.label}` : undefined, className: buildClassName('group', column.sortable &&
1510
+ 'inline-flex items-center gap-2 cursor-pointer select-none focus:outline-none focus-visible:ring-1 focus-visible:ring-blue-500 rounded'), onClick: () => handleSort(column), onKeyDown: (e) => {
1511
+ if (column.sortable && (e.key === 'Enter' || e.key === ' ')) {
1512
+ e.preventDefault();
1513
+ handleSort(column);
1514
+ }
1515
+ }, children: [jsxRuntime.jsx(TextContent, { className: "shrink-0", children: column.label }), column.sortable ? getSortIcon(activeSorting[column.name]) : null] }) }, column.name));
1516
+ }), isInlineMode && (jsxRuntime.jsx(TableHeaderCell, { align: "right", className: buildClassName('bg-gray-50 dark:bg-gray-800 text-right w-px', densityCellClass, stickyHeader && 'sticky top-0 z-20 shadow-[0_1px_2px_rgba(0,0,0,0.06)]'), children: rowHoverActionHeader !== null && rowHoverActionHeader !== void 0 ? rowHoverActionHeader : '' })), isOverlayMode && (jsxRuntime.jsx(TableHeaderCell, { align: "right", style: { padding: 0, width: 0, minWidth: 0, maxWidth: 0 }, className: buildClassName('sticky right-0 z-20 !p-0 !pr-0 !pl-0 !py-0 w-0 max-w-0 min-w-0 border-0 bg-gray-50 dark:bg-gray-800', stickyHeader && 'top-0'), children: null }))] }), jsxRuntime.jsx(TableBody, { loading: loading, colSize: colSize, rowSize: (pagination === null || pagination === void 0 ? void 0 : pagination.limit) || 5, children: !loading && sortedItems.length === 0 ? (jsxRuntime.jsx(EmptyTableRow, { colSpan: colSize, children: emptyMessage })) : (sortedItems.map((item, idx) => {
1418
1517
  const key = getKey(item, idx);
1419
1518
  const rowSelectable = !isRowSelectable || isRowSelectable(item);
1420
1519
  return (jsxRuntime.jsxs(TableRow, { className: buildClassName('group', onRowClick && 'cursor-pointer', striped && idx % 2 === 1 && 'bg-gray-50 dark:bg-gray-800/40', getRowClass(item, idx)), onClick: onRowClick ? () => onRowClick(item, idx) : undefined, children: [selectable && (jsxRuntime.jsx(TableCell, { className: buildClassName(densityCellClass, striped && idx % 2 === 1 && 'bg-gray-50 dark:bg-gray-800/40'), children: jsxRuntime.jsx("span", { onClick: (e) => e.stopPropagation(), children: jsxRuntime.jsx(Checkbox, { "aria-label": "Select row", checked: isSelected(key), disabled: !rowSelectable, onChange: () => toggleRow(key) }) }) })), columns.map((column) => {
@@ -1424,8 +1523,10 @@ function DataTable({ items, columns, sorting = {}, onSort, loading, pagination,
1424
1523
  'sticky right-0 z-10 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]', getCellClass(column, item)), children: column.render
1425
1524
  ? column.render(item)
1426
1525
  : ((_a = get(item, column.name)) !== null && _a !== void 0 ? _a : 'Not set') }, column.name));
1427
- })] }, key));
1428
- })) }), footerRow && jsxRuntime.jsx(TableFoot, { children: footerRow })] }) }), pagination && (jsxRuntime.jsx(Pagination, { ...pagination, className: buildClassName('rounded-b-lg border border-[var(--ui-border)]', stickyPagination && 'sticky bottom-0 z-20 bg-white dark:bg-gray-800', paginationContainerClass), onChange: setPagination }))] }));
1526
+ }), isInlineMode && (jsxRuntime.jsx(TableCell, { align: "right", className: buildClassName(densityCellClass, striped && idx % 2 === 1 && 'bg-gray-50 dark:bg-gray-800/40'), onClick: (e) => e.stopPropagation(), children: jsxRuntime.jsx("div", { className: buildClassName('opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 transition-opacity duration-150 flex items-center justify-end gap-1.5', rowHoverActionClass), "data-testid": `row-hover-actions-${key}`, children: renderActionsContent(item, idx) }) })), isOverlayMode && (jsxRuntime.jsx(TableCell, { align: "right", style: { padding: 0, width: 0, minWidth: 0, maxWidth: 0 }, className: "sticky right-0 z-20 !p-0 !pr-0 !pl-0 !py-0 w-0 max-w-0 min-w-0 border-0 overflow-visible text-right", onClick: (e) => e.stopPropagation(), children: jsxRuntime.jsx("div", { className: buildClassName('absolute right-0 top-0 bottom-0 h-full opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 transition-opacity duration-150 flex items-center justify-end gap-1 px-3 z-30 pointer-events-none group-hover:pointer-events-auto whitespace-nowrap', striped && idx % 2 === 1
1527
+ ? 'bg-gray-100 dark:bg-gray-800'
1528
+ : 'bg-white group-hover:bg-gray-50 dark:bg-gray-900 dark:group-hover:bg-gray-800', rowHoverActionClass), "data-testid": `row-hover-actions-${key}`, children: renderActionsContent(item, idx) }) }))] }, key));
1529
+ })) }), footerRow && jsxRuntime.jsx(TableFoot, { children: footerRow })] }) }), pagination && (jsxRuntime.jsx("div", { className: buildClassName(stickyPagination && 'sticky bottom-0 z-20 bg-white dark:bg-gray-800', paginationClass, paginationContainerClass), children: renderPagination ? (renderPagination(pagination, setPagination)) : (jsxRuntime.jsx(Pagination, { ...pagination, ...paginationProps, className: buildClassName('rounded-t-none rounded-b-lg border border-[var(--ui-border)]', stickyPagination && 'sticky bottom-0 z-20 bg-white dark:bg-gray-800', paginationProps === null || paginationProps === void 0 ? void 0 : paginationProps.className), onChange: setPagination })) }))] }));
1429
1530
  }
1430
1531
  DataTable.displayName = 'DataTable';
1431
1532
 
@@ -2054,20 +2155,20 @@ function buildInputClass(className, { isFocusWithin = false, disabled = false, h
2054
2155
  : 'focus:outline-[var(--ui-focus-ring)] focus:outline-2 focus:-outline-offset-2', className);
2055
2156
  }
2056
2157
  const Input = React.forwardRef((props, ref) => {
2057
- 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;
2158
+ 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, required, 'aria-describedby': ariaDescribedBy, ...inputProps } = props;
2058
2159
  const innerRef = React.useRef(null);
2059
2160
  React.useImperativeHandle(ref, () => innerRef.current);
2060
2161
  const [uncontrolledValue, setUncontrolledValue] = React.useState(String(defaultValue !== null && defaultValue !== void 0 ? defaultValue : ''));
2061
2162
  const isControlled = value !== undefined;
2062
2163
  const currentVal = isControlled ? String(value !== null && value !== void 0 ? value : '') : uncontrolledValue;
2063
2164
  const charLength = currentVal.length;
2064
- const handleInputChange = (e) => {
2165
+ const handleInputChange = React.useCallback((e) => {
2065
2166
  if (!isControlled) {
2066
2167
  setUncontrolledValue(e.target.value);
2067
2168
  }
2068
2169
  onChange === null || onChange === void 0 ? void 0 : onChange(e);
2069
- };
2070
- const handleClear = () => {
2170
+ }, [isControlled, onChange]);
2171
+ const handleClear = React.useCallback(() => {
2071
2172
  if (innerRef.current) {
2072
2173
  innerRef.current.value = '';
2073
2174
  const event = new Event('input', {
@@ -2080,18 +2181,21 @@ const Input = React.forwardRef((props, ref) => {
2080
2181
  onChange === null || onChange === void 0 ? void 0 : onChange(event);
2081
2182
  }
2082
2183
  onClear === null || onClear === void 0 ? void 0 : onClear();
2083
- };
2184
+ }, [isControlled, onChange, onClear]);
2084
2185
  const hasError = Boolean(error);
2085
2186
  const currentSize = sizeClasses$4[size] || sizeClasses$4.md;
2187
+ const errorId = id && hasError && showErrorMessage ? `${id}-error` : undefined;
2188
+ const helperId = id && helperText && !hasError ? `${id}-helper` : undefined;
2189
+ const describedBy = [ariaDescribedBy, errorId, helperId].filter(Boolean).join(' ') || undefined;
2086
2190
  const showClearButton = clearable && charLength > 0 && !inputProps.disabled;
2087
- 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);
2191
+ 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", "aria-label": "Clear input text", 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);
2088
2192
  const hasRightGroup = actualRightGroup !== null && actualRightGroup !== undefined;
2089
2193
  const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
2090
- 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-[var(--ui-border)] 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, {
2194
+ 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, "aria-required": required, 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-[var(--ui-border)] 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, required: required, "aria-invalid": hasError ? true : undefined, "aria-describedby": describedBy, className: buildClassName(buildInputClass(className, {
2091
2195
  disabled: inputProps.disabled,
2092
2196
  hasError,
2093
2197
  size,
2094
- }), 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-[var(--ui-border)] 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
2198
+ }), 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-[var(--ui-border)] 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, id: errorId, children: error })) : helperText ? (jsxRuntime.jsx(TextContent, { muted: true, small: true, id: helperId, children: helperText })) : null }), showCount && (jsxRuntime.jsxs("span", { className: buildClassName('text-[11px] font-medium shrink-0 ml-auto', maxLength && charLength >= maxLength
2095
2199
  ? 'text-red-500 font-semibold'
2096
2200
  : 'text-gray-400 dark:text-gray-500'), children: [charLength, maxLength ? ` / ${maxLength}` : ''] }))] })) : null] }));
2097
2201
  });
@@ -2316,16 +2420,20 @@ const THEME_MAP = {
2316
2420
  danger: 'checked:border-[var(--ui-danger)] checked:hover:border-[var(--ui-danger)] focus:ring-[var(--ui-danger-ring)]',
2317
2421
  };
2318
2422
  const Radio = React.forwardRef((props, ref) => {
2319
- const { id, name, label, description, size = 'md', theme = 'primary', labelClass = '', descriptionClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '', ...rest } = props;
2423
+ const { id, name, label, description, size = 'md', theme = 'primary', labelClass = '', descriptionClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '', required, 'aria-describedby': ariaDescribedBy, ...rest } = props;
2320
2424
  const generatedId = React.useId();
2321
2425
  const radioId = id !== null && id !== void 0 ? id : (name && rest.value ? `${name}-${rest.value}` : generatedId);
2322
- 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
2426
+ const hasError = Boolean(error);
2427
+ const errorId = radioId && hasError ? `${radioId}-error` : undefined;
2428
+ const descId = radioId && description ? `${radioId}-desc` : undefined;
2429
+ const describedBy = [ariaDescribedBy, errorId, descId].filter(Boolean).join(' ') || undefined;
2430
+ 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, required: required, "aria-required": required ? true : undefined, "aria-invalid": hasError ? true : undefined, "aria-describedby": describedBy, "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
2323
2431
  ? 'border-[var(--ui-border-muted)] bg-gray-100 dark:bg-gray-800 pointer-events-none'
2324
- : 'bg-white dark:bg-gray-700 border-[var(--ui-border)] 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
2432
+ : 'bg-white dark:bg-gray-700 border-[var(--ui-border)] 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, "aria-required": required, className: buildClassName(disabled
2325
2433
  ? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
2326
- : '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
2434
+ : 'cursor-pointer text-gray-700 dark:text-gray-200 font-medium', labelClass), children: label })), description && (jsxRuntime.jsx("p", { id: descId, className: buildClassName('text-xs leading-normal', disabled
2327
2435
  ? 'text-gray-300 dark:text-gray-600'
2328
- : '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 }) }))] }));
2436
+ : '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, id: errorId, children: error }) }))] }));
2329
2437
  });
2330
2438
  Radio.displayName = 'Radio';
2331
2439
 
@@ -2336,7 +2444,8 @@ const COLUMN_GRID_MAP = {
2336
2444
  4: 'grid-cols-1 md:grid-cols-4',
2337
2445
  };
2338
2446
  const RadioGroup = (props) => {
2339
- const { name, variant = 'default', size = 'md', theme = 'primary', label, labelClass, labelWrapperClass, labelHint, containerClass, options, value, columns, disabled = false, row, error, showErrorMessage = true, onChange, } = props;
2447
+ const { name, variant = 'default', size = 'md', theme = 'primary', label, labelClass, labelWrapperClass, labelHint, containerClass, options, value, columns, disabled = false, row, error, showErrorMessage = true, onChange, required, 'aria-describedby': ariaDescribedBy, } = props;
2448
+ const groupId = React.useId();
2340
2449
  const items = React.useMemo(() => {
2341
2450
  var _a;
2342
2451
  return (_a = options === null || options === void 0 ? void 0 : options.map((option) => typeof option === 'string'
@@ -2350,7 +2459,11 @@ const RadioGroup = (props) => {
2350
2459
  : option)) !== null && _a !== void 0 ? _a : [];
2351
2460
  }, [options]);
2352
2461
  const isCards = variant === 'cards';
2353
- 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
2462
+ const hasError = Boolean(error);
2463
+ const labelId = label ? `${groupId}-label` : undefined;
2464
+ const errorId = groupId && hasError && showErrorMessage ? `${groupId}-error` : undefined;
2465
+ const describedBy = [ariaDescribedBy, errorId].filter(Boolean).join(' ') || undefined;
2466
+ return (jsxRuntime.jsxs("div", { role: "radiogroup", "aria-labelledby": labelId, "aria-describedby": describedBy, className: buildClassName(containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between mb-3', labelWrapperClass), children: [jsxRuntime.jsx(Label, { id: labelId, className: labelClass, "aria-required": required, children: label }), labelHint] })), jsxRuntime.jsx("div", { className: buildClassName(columns
2354
2467
  ? buildClassName('grid gap-3', COLUMN_GRID_MAP[columns])
2355
2468
  : row || isCards
2356
2469
  ? 'flex flex-wrap gap-3'
@@ -2364,7 +2477,7 @@ const RadioGroup = (props) => {
2364
2477
  '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));
2365
2478
  }
2366
2479
  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));
2367
- }) }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1.5", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) }))] }));
2480
+ }) }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1.5", children: jsxRuntime.jsx(TextContent, { error: true, small: true, id: errorId, children: error }) }))] }));
2368
2481
  };
2369
2482
  RadioGroup.displayName = 'RadioGroup';
2370
2483
 
@@ -2476,34 +2589,6 @@ const RadioSwitch = ({ label, items, selected, size = 'md', theme = 'primary', d
2476
2589
  };
2477
2590
  RadioSwitch.displayName = 'RadioSwitch';
2478
2591
 
2479
- /**
2480
- * Checks if a value is an array.
2481
- * Equivalent to lodash's _.isArray.
2482
- */
2483
- function isArray(value) {
2484
- return Array.isArray(value);
2485
- }
2486
-
2487
- /**
2488
- * Checks if a value is empty.
2489
- * Works for arrays, objects, strings, Maps, and Sets.
2490
- * Equivalent to lodash's _.isEmpty.
2491
- */
2492
- function isEmpty(value) {
2493
- if (value == null)
2494
- return true; // null or undefined
2495
- if (typeof value === 'string' || isArray(value)) {
2496
- return value.length === 0;
2497
- }
2498
- if (value instanceof Map || value instanceof Set) {
2499
- return value.size === 0;
2500
- }
2501
- if (typeof value === 'object') {
2502
- return Object.keys(value).length === 0;
2503
- }
2504
- return false; // for numbers, booleans, functions, etc.
2505
- }
2506
-
2507
2592
  const sizeClasses$3 = {
2508
2593
  sm: {
2509
2594
  track: 'h-1.5',
@@ -3486,7 +3571,7 @@ const parseNumericValue = (val) => {
3486
3571
  const cleaned = String(val).replace(/[^0-9.-]/g, '');
3487
3572
  return cleaned !== '' ? Number(cleaned) : NaN;
3488
3573
  };
3489
- function DataList({ items, columns, renderItem, searchable = false, searchQuery: externalSearchQuery, onSearchChange, searchFields, filterFields, selectedFilters: externalFilters, onFilterChange, sorting: externalSorting, onSortChange, swipeableActions, rowKey = 'id', selectable = false, selectedKeys = [], onSelectionChange, onItemClick, loading = false, emptyMessage = 'No items found', pagination, setPagination, containerClass = '', itemClass = '', }) {
3574
+ function DataList({ items, columns, renderItem, searchable = false, searchQuery: externalSearchQuery, onSearchChange, searchFields, filterFields, selectedFilters: externalFilters, onFilterChange, sorting: externalSorting, onSortChange, swipeableActions, rowKey = 'id', selectable = false, selectedKeys = [], onSelectionChange, onItemClick, loading = false, emptyMessage = 'No items found', pagination, setPagination, paginationClass, paginationContainerClass, paginationProps, renderPagination, containerClass = '', itemClass = '', }) {
3490
3575
  // Internal Search State
3491
3576
  const [internalSearchQuery, setInternalSearchQuery] = React.useState('');
3492
3577
  const searchQuery = externalSearchQuery !== undefined ? externalSearchQuery : internalSearchQuery;
@@ -3734,10 +3819,12 @@ function DataList({ items, columns, renderItem, searchable = false, searchQuery:
3734
3819
  return (jsxRuntime.jsx(SwipeableRow, { leftActions: actions.leftActions, rightActions: actions.rightActions, children: contentNode }, key));
3735
3820
  }
3736
3821
  return jsxRuntime.jsx("div", { children: contentNode }, key);
3737
- })) }), pagination && pagination.total > 0 && (jsxRuntime.jsx("div", { className: "pt-2 flex justify-center", children: jsxRuntime.jsx(Pagination, { ...pagination, onChange: (newPag) => setPagination === null || setPagination === void 0 ? void 0 : setPagination(newPag) }) }))] }));
3822
+ })) }), pagination && pagination.total > 0 && (jsxRuntime.jsx("div", { className: buildClassName('pt-2 flex justify-center w-full', paginationClass, paginationContainerClass), children: renderPagination ? (renderPagination(pagination, setPagination)) : (jsxRuntime.jsx(Pagination, { ...pagination, ...paginationProps, onChange: (newPag) => setPagination === null || setPagination === void 0 ? void 0 : setPagination(newPag) })) }))] }));
3738
3823
  }
3739
3824
  DataList.displayName = 'DataList';
3740
3825
 
3826
+ const NOOP = () => { };
3827
+ const EMPTY_ARRAY = [];
3741
3828
  const sizeClasses$1 = {
3742
3829
  sm: {
3743
3830
  input: 'px-2.5 py-1.5 text-xs leading-5 min-h-[32px]',
@@ -3759,7 +3846,7 @@ const sizeClasses$1 = {
3759
3846
  },
3760
3847
  };
3761
3848
  const SelectBox = React.forwardRef((props, forwardedRef) => {
3762
- 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, allowFreeText = false, modalDropdown = false, noOptionsText = 'No options available', } = props;
3849
+ const { labelKey = 'label', valueKey = 'value', name, id = name, options = EMPTY_ARRAY, groups, selected, size = 'md', label = null, labelWrapperClass = '', labelClass = '', labelHint = null, disabled = false, containerClass = '', onChange = NOOP, 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 = NOOP, allowClear = false, allowFreeText = false, modalDropdown = false, noOptionsText = 'No options available', } = props;
3763
3850
  if (props.asBottomSheet || props.mobileMode === 'sheet') {
3764
3851
  return jsxRuntime.jsx(MobilePicker, { ...props });
3765
3852
  }
@@ -3777,7 +3864,7 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
3777
3864
  const shouldShowTypeToSearch = async && !query && !optionsLoading && allOptions.length === 0;
3778
3865
  const currentSize = sizeClasses$1[size] || sizeClasses$1.md;
3779
3866
  const baseInputClass = React.useMemo(() => buildInputClass('', { disabled, hasError, size }), [disabled, hasError, size]);
3780
- 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');
3867
+ const chevronIconClass = React.useMemo(() => 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'), [currentSize.icon, disabled]);
3781
3868
  const inputRef = React.useRef(null);
3782
3869
  const containerRef = React.useRef(null);
3783
3870
  React.useImperativeHandle(forwardedRef, () => inputRef.current, [inputRef]);
@@ -3853,7 +3940,7 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
3853
3940
  maxSelection,
3854
3941
  onChange,
3855
3942
  ]);
3856
- const removeSelection = (idx) => {
3943
+ const removeSelection = React.useCallback((idx) => {
3857
3944
  var _a;
3858
3945
  if (!isArray(selected))
3859
3946
  return;
@@ -3861,7 +3948,7 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
3861
3948
  next.splice(idx, 1);
3862
3949
  onChange(next);
3863
3950
  (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
3864
- };
3951
+ }, [selected, onChange]);
3865
3952
  const handleOnChangeInternal = React.useCallback((value) => {
3866
3953
  if (value === null) {
3867
3954
  onChange(multiple ? [] : null);
@@ -3905,9 +3992,7 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
3905
3992
  }
3906
3993
  }, [onAdd, handleOnChangeInternal]);
3907
3994
  const onSearchRef = React.useRef(props.onSearch);
3908
- React.useEffect(() => {
3909
- onSearchRef.current = props.onSearch;
3910
- }, [props.onSearch]);
3995
+ onSearchRef.current = props.onSearch;
3911
3996
  const handleSearch = React.useCallback(async (searchString) => {
3912
3997
  var _a;
3913
3998
  try {
@@ -3939,20 +4024,40 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
3939
4024
  return selected !== null && selected !== void 0 ? selected : null;
3940
4025
  }
3941
4026
  }, [multiple, selected]);
4027
+ const selectedValueSet = React.useMemo(() => {
4028
+ if (!normalizedSelected)
4029
+ return new Set();
4030
+ if (Array.isArray(normalizedSelected)) {
4031
+ return new Set(normalizedSelected.map((s) => getValue(s)));
4032
+ }
4033
+ return new Set([getValue(normalizedSelected)]);
4034
+ }, [normalizedSelected, getValue]);
3942
4035
  const isAllSelected = React.useMemo(() => {
3943
4036
  if (!multiple || !Array.isArray(normalizedSelected) || !allOptions.length)
3944
4037
  return false;
3945
4038
  return allOptions.length === normalizedSelected.length;
3946
4039
  }, [multiple, normalizedSelected, allOptions]);
3947
- const handleToggleSelectAll = () => {
4040
+ const handleToggleSelectAll = React.useCallback(() => {
3948
4041
  if (isAllSelected) {
3949
4042
  onChange([]);
3950
4043
  }
3951
4044
  else {
3952
4045
  onChange(allOptions);
3953
4046
  }
3954
- };
4047
+ }, [isAllSelected, onChange, allOptions]);
3955
4048
  const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
4049
+ const hasFreeTextOption = Boolean(allowFreeText &&
4050
+ query.trim() &&
4051
+ !allOptions.some((opt) => { var _a; return ((_a = getDisplayValue(opt)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === query.trim().toLowerCase(); }));
4052
+ const hasAddOption = Boolean(!allowFreeText && allowAdd && query.trim());
4053
+ const hasGroupOptions = Boolean(filteredGroups && filteredGroups.length > 0);
4054
+ const hasFlatOptions = filteredOptions.length > 0;
4055
+ const hasAnyOptionsToDisplay = hasFlatOptions || hasGroupOptions || hasFreeTextOption || hasAddOption;
4056
+ const renderOptionItem = (option) => {
4057
+ const optVal = getValue(option);
4058
+ const isOptSelected = selectedValueSet.has(optVal);
4059
+ return (jsxRuntime.jsx(react.ComboboxOption, { disabled: option.disabled, value: option, className: "group rounded-lg py-1.5 px-3 select-none data-[focus]:bg-gray-100 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 text-gray-900 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));
4060
+ };
3956
4061
  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: normalizedSelected, onChange: comboboxOnChangeWrapper, onClose: () => setQuery(''), disabled: disabled, by: (a, b) => (a && b ? getValue(a) === getValue(b) : a === b), 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(normalizedSelected) &&
3957
4062
  (normalizedSelected === null || normalizedSelected === void 0 ? void 0 : normalizedSelected.length) > 0 &&
3958
4063
  normalizedSelected.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: (normalizedSelected === null || normalizedSelected === void 0 ? void 0 : normalizedSelected.length) ? '' : placeholder, onChange: (e) => {
@@ -3996,26 +4101,9 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
3996
4101
  }, 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(normalizedSelected) && (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-[var(--ui-border)] bg-white dark:bg-gray-800 p-1 shadow-lg max-h-80 overflow-y-auto z-50 [--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-[var(--ui-border-muted)] 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(normalizedSelected) ? normalizedSelected.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: [allowFreeText &&
3997
4102
  query.trim() &&
3998
4103
  !allOptions.some((opt) => { var _a; return ((_a = getDisplayValue(opt)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === query.trim().toLowerCase(); }) && (jsxRuntime.jsx(react.ComboboxOption, { value: createFreeTextOption(query.trim()), className: "group flex items-center gap-2 rounded-lg py-1.5 px-3 select-none text-sm data-[focus]:bg-blue-50 dark:data-[focus]:bg-blue-950/40 text-blue-600 dark:text-blue-400 font-medium cursor-pointer w-full", "data-testid": "freetext-option", children: jsxRuntime.jsxs("span", { children: ["Use \"", query.trim(), "\""] }) }, `freetext-${query.trim()}`)), filteredGroups
3999
- ? 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) => {
4000
- const optVal = getValue(option);
4001
- const isOptSelected = multiple
4002
- ? isArray(normalizedSelected) &&
4003
- normalizedSelected.some((s) => getValue(s) === optVal)
4004
- : normalizedSelected && getValue(normalizedSelected) === optVal;
4005
- return (jsxRuntime.jsx(react.ComboboxOption, { disabled: option.disabled, value: option, className: "group rounded-lg py-1.5 px-3 select-none data-[focus]:bg-gray-100 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 text-gray-900 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));
4006
- })] }, gIdx)))
4104
+ ? 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(renderOptionItem)] }, gIdx)))
4007
4105
  : /* Render Standard Flat Options */
4008
- filteredOptions.map((option) => {
4009
- const optVal = getValue(option);
4010
- const isOptSelected = multiple
4011
- ? isArray(normalizedSelected) &&
4012
- normalizedSelected.some((s) => getValue(s) === optVal)
4013
- : normalizedSelected && getValue(normalizedSelected) === optVal;
4014
- return (jsxRuntime.jsx(react.ComboboxOption, { disabled: option.disabled, value: option, className: "group rounded-lg py-1.5 px-3 select-none data-[focus]:bg-gray-100 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 text-gray-900 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));
4015
- }), filteredOptions.length === 0 &&
4016
- !(filteredGroups === null || filteredGroups === void 0 ? void 0 : filteredGroups.length) &&
4017
- !allowFreeText &&
4018
- !(allowAdd && query.trim()) && (jsxRuntime.jsxs("div", { "data-testid": "no-result-found", className: "flex items-center gap-2 py-1.5 px-3 select-none", children: [jsxRuntime.jsx(TextContent, { muted: true, small: true, children: noOptionsText }), jsxRuntime.jsx(TextContent, { strong: true, small: true, children: query })] })), !allowFreeText && allowAdd && Boolean(query.trim()) && (jsxRuntime.jsx(react.ComboboxOption, { value: query, className: "group rounded-lg py-1.5 px-3 cursor-pointer text-gray-700 dark:text-gray-300 data-[focus]:bg-gray-100 dark:data-[focus]: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, "\""] })] }) }, `add-${query.trim()}`))] }))] })] }), 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 }) }))] }));
4106
+ filteredOptions.map(renderOptionItem), !hasAnyOptionsToDisplay && Boolean(noOptionsText) && (jsxRuntime.jsxs("div", { "data-testid": "no-result-found", className: "flex items-center gap-2 py-1.5 px-3 select-none", children: [jsxRuntime.jsx(TextContent, { muted: true, small: true, children: noOptionsText }), Boolean(query.trim()) && (jsxRuntime.jsxs(TextContent, { strong: true, small: true, children: ["\"", query.trim(), "\""] }))] })), !allowFreeText && allowAdd && Boolean(query.trim()) && (jsxRuntime.jsx(react.ComboboxOption, { value: query, className: "group rounded-lg py-1.5 px-3 cursor-pointer text-gray-700 dark:text-gray-300 data-[focus]:bg-gray-100 dark:data-[focus]: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, "\""] })] }) }, `add-${query.trim()}`))] }))] })] }), 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 }) }))] }));
4019
4107
  });
4020
4108
  SelectBox.displayName = 'SelectBox';
4021
4109
 
@@ -4369,7 +4457,7 @@ const resizeClasses = {
4369
4457
  both: 'resize',
4370
4458
  };
4371
4459
  const Textarea = React.forwardRef((props, ref) => {
4372
- 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;
4460
+ 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, required, 'aria-describedby': ariaDescribedBy, ...inputProps } = props;
4373
4461
  const innerRef = React.useRef(null);
4374
4462
  React.useImperativeHandle(ref, () => innerRef.current);
4375
4463
  const [uncontrolledValue, setUncontrolledValue] = React.useState(String(defaultValue !== null && defaultValue !== void 0 ? defaultValue : ''));
@@ -4383,19 +4471,22 @@ const Textarea = React.forwardRef((props, ref) => {
4383
4471
  innerRef.current.style.height = `${innerRef.current.scrollHeight}px`;
4384
4472
  }
4385
4473
  }, [autoSize, currentVal]);
4386
- const handleTextareaChange = (e) => {
4474
+ const handleTextareaChange = React.useCallback((e) => {
4387
4475
  if (!isControlled) {
4388
4476
  setUncontrolledValue(e.target.value);
4389
4477
  }
4390
4478
  onChange === null || onChange === void 0 ? void 0 : onChange(e);
4391
- };
4479
+ }, [isControlled, onChange]);
4392
4480
  const hasError = !isEmpty(error);
4393
4481
  const hasRightGroup = rightGroup != null && rightGroup !== undefined;
4394
4482
  const hasLeftGroup = leftGroup != null && leftGroup !== undefined;
4395
4483
  const currentSize = sizeClasses[size] || sizeClasses.md;
4396
- 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
4484
+ const errorId = id && hasError && showErrorMessage ? `${id}-error` : undefined;
4485
+ const helperId = id && helperText && !hasError ? `${id}-helper` : undefined;
4486
+ const describedBy = [ariaDescribedBy, errorId, helperId].filter(Boolean).join(' ') || undefined;
4487
+ 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, "aria-required": required, 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, required: required, "aria-invalid": hasError ? true : undefined, "aria-describedby": describedBy, 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
4397
4488
  ? '!outline-red-500 !ring-1 !ring-red-500/50 dark:!outline-red-500'
4398
- : 'outline-[var(--ui-border)]', 'focus:outline-[var(--ui-focus-ring)] focus:outline-2 focus:-outline-offset-2', 'dark:bg-gray-700 dark:placeholder-gray-400 placeholder:text-gray-400 dark:text-white', 'disabled:pointer-events-none disabled:bg-gray-100 disabled:outline-[var(--ui-border-muted)]', 'dark:disabled:bg-gray-800 dark:disabled:text-gray-600 dark:disabled:placeholder-gray-600 dark:disabled:outline-[var(--ui-border-muted)]', 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
4489
+ : 'outline-[var(--ui-border)]', 'focus:outline-[var(--ui-focus-ring)] focus:outline-2 focus:-outline-offset-2', 'dark:bg-gray-700 dark:placeholder-gray-400 placeholder:text-gray-400 dark:text-white', 'disabled:pointer-events-none disabled:bg-gray-100 disabled:outline-[var(--ui-border-muted)]', 'dark:disabled:bg-gray-800 dark:disabled:text-gray-600 dark:disabled:placeholder-gray-600 dark:disabled:outline-[var(--ui-border-muted)]', 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, id: errorId, children: error })) : helperText ? (jsxRuntime.jsx(TextContent, { muted: true, small: true, id: helperId, children: helperText })) : null }), showCount && (jsxRuntime.jsxs("span", { className: buildClassName('text-[11px] font-medium shrink-0 ml-auto', maxLength && charLength >= maxLength
4399
4490
  ? 'text-red-500 font-semibold'
4400
4491
  : 'text-gray-400 dark:text-gray-500'), children: [charLength, maxLength ? ` / ${maxLength}` : ''] }))] })) : null] }));
4401
4492
  });