@pk-design/react-tailwind 1.1.4 → 1.1.6

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.
@@ -243,7 +243,7 @@ const TypeClassMap = {
243
243
  const Alert = (props) => {
244
244
  const { type = 'info', className = '', message, children, removable = false, onRemove = () => { }, ...restProps } = props;
245
245
  const Icon = AlertTypeIconMap[type] || SvgInfo;
246
- return (jsxRuntime.jsxs("div", { className: buildClassName(className, 'flex items-center justify-between rounded-md p-3 text-sm ring-1 ring-inset gap-2', TypeClassMap[type]), ...restProps, children: [jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2", children: [jsxRuntime.jsx(Icon, { className: "size-5", "data-testid": `alert-${type}-icon` }), jsxRuntime.jsx("span", { className: "pr-2 inline-flex items-center", children: message || children })] }), removable && (jsxRuntime.jsx(Button, { iconOnly: true, size: "xs", variant: "plain", theme: "secondary", className: "hover:bg-black/5 text-gray-600 dark:hover:bg-black/5 dark:text-gray-600", onClick: () => onRemove(), "data-testid": "alert-remove-btn", children: jsxRuntime.jsx(SvgX, { className: "size-4.5", "data-testid": "alert-remove-icon" }) }))] }));
246
+ return (jsxRuntime.jsxs("div", { className: buildClassName(className, 'flex items-center justify-between rounded-md p-3 text-sm ring-1 ring-inset gap-2', TypeClassMap[type]), ...restProps, children: [jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2", children: [jsxRuntime.jsx(Icon, { className: "size-5", "data-testid": `alert-${type}-icon` }), jsxRuntime.jsx("span", { className: "pr-2 inline-flex items-center", children: message || children })] }), removable && (jsxRuntime.jsx(Button, { iconOnly: true, "aria-label": "Dismiss alert", size: "xs", variant: "plain", theme: "secondary", className: "hover:bg-black/5 text-gray-600 dark:hover:bg-black/5 dark:text-gray-600", onClick: () => onRemove(), "data-testid": "alert-remove-btn", children: jsxRuntime.jsx(SvgX, { className: "size-4.5", "data-testid": "alert-remove-icon" }) }))] }));
247
247
  };
248
248
  Alert.displayName = 'Alert';
249
249
 
@@ -256,7 +256,12 @@ const ThemeClassMap = {
256
256
  };
257
257
  const Badge = React.forwardRef((props, ref) => {
258
258
  const { theme = 'info', className = '', children, removable = false, rounded = false, onRemove = () => { }, ...restProps } = props;
259
- return (jsxRuntime.jsxs(TextContent, { ref: ref, className: buildClassName(className, 'inline-flex items-center pl-3 py-1 text-xs font-medium ring-1 ring-inset', ThemeClassMap[theme], rounded ? 'rounded-full' : 'rounded-md'), "data-testid": `badge-${theme}`, ...restProps, children: [jsxRuntime.jsx(TextContent, { className: "pr-3 inline-flex items-center", children: children }), removable ? (jsxRuntime.jsx(SvgX, { className: "size-5 px-0.5 mr-1 hover:bg-slate-200 dark:hover:bg-white/10 rounded-sm cursor-pointer text-gray-600 dark:text-gray-300", role: "button", onClick: () => onRemove() })) : null] }));
259
+ return (jsxRuntime.jsxs(TextContent, { ref: ref, className: buildClassName(className, 'inline-flex items-center pl-3 py-1 text-xs font-medium ring-1 ring-inset', ThemeClassMap[theme], rounded ? 'rounded-full' : 'rounded-md'), "data-testid": `badge-${theme}`, ...restProps, children: [jsxRuntime.jsx(TextContent, { className: "pr-3 inline-flex items-center", children: children }), removable ? (jsxRuntime.jsx(SvgX, { className: "size-5 px-0.5 mr-1 hover:bg-slate-200 dark:hover:bg-white/10 rounded-sm cursor-pointer text-gray-600 dark:text-gray-300", role: "button", tabIndex: 0, "aria-label": "Remove", onClick: () => onRemove(), onKeyDown: (e) => {
260
+ if (e.key === 'Enter' || e.key === ' ') {
261
+ e.preventDefault();
262
+ onRemove();
263
+ }
264
+ } })) : null] }));
260
265
  });
261
266
  Badge.displayName = 'Badge';
262
267
 
@@ -360,7 +365,7 @@ var SvgSlash = function SvgSlash(props) {
360
365
  const Breadcrumb = ({ items, render }) => {
361
366
  const count = items.length;
362
367
  const isLast = (index) => index === count - 1;
363
- return (jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-2", "data-testid": "breadcrumb-container", children: items.map((item, index) => (jsxRuntime.jsxs("span", { className: "inline-flex gap-2 items-center dark:text-white", "data-testid": `breadcrumb-item-${item.key}`, children: [jsxRuntime.jsx("span", { className: "font-medium", children: isLast(index) ? item.text : render(item) }), !isLast(index) && (jsxRuntime.jsx(SvgSlash, { "data-testid": `breadcrumb-divider-${index}`, className: "size-4" }))] }, item.key))) }));
368
+ return (jsxRuntime.jsx("nav", { "aria-label": "Breadcrumb", className: "flex flex-wrap items-center gap-2", "data-testid": "breadcrumb-container", children: items.map((item, index) => (jsxRuntime.jsxs("span", { className: "inline-flex gap-2 items-center dark:text-white", "data-testid": `breadcrumb-item-${item.key}`, children: [jsxRuntime.jsx("span", { className: "font-medium", ...(isLast(index) ? { 'aria-current': 'page' } : {}), children: isLast(index) ? item.text : render(item) }), !isLast(index) && (jsxRuntime.jsx(SvgSlash, { "data-testid": `breadcrumb-divider-${index}`, className: "size-4" }))] }, item.key))) }));
364
369
  };
365
370
  Breadcrumb.displayName = 'Breadcrumb';
366
371
 
@@ -387,7 +392,8 @@ const Checkbox = React.forwardRef((props, ref) => {
387
392
  const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '',
388
393
  // Checkbox-only props removed from rest
389
394
  ...rest } = props;
390
- const checkboxId = (_a = id !== null && id !== void 0 ? id : name) !== null && _a !== void 0 ? _a : 'checkbox-' + Math.random();
395
+ const generatedId = React.useId();
396
+ const checkboxId = (_a = id !== null && id !== void 0 ? id : name) !== null && _a !== void 0 ? _a : generatedId;
391
397
  return (jsxRuntime.jsxs("div", { className: buildClassName(wrapperClass), children: [jsxRuntime.jsxs("div", { className: buildClassName('flex items-center', containerClass), children: [jsxRuntime.jsxs("label", { htmlFor: checkboxId, className: buildClassName('inline-flex items-center relative select-none', disabled ? 'cursor-not-allowed opacity-70' : 'cursor-pointer'), children: [jsxRuntime.jsx("input", { ref: ref, ...rest, id: checkboxId, name: name, type: "checkbox", disabled: disabled, "aria-checked": rest.checked, className: buildClassName('peer size-4.5 cursor-pointer transition-all appearance-none rounded', 'border focus:ring-2 focus:ring-offset-2', 'checked:bg-blue-600 checked:border-blue-600', 'focus:ring-blue-500 dark:focus:ring-offset-gray-900', disabled
392
398
  ? 'border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-gray-800 pointer-events-none'
393
399
  : 'bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600 hover:border-gray-400 dark:hover:border-gray-500 hover:shadow-md', className) }), jsxRuntime.jsx("span", { className: "absolute text-white opacity-0 peer-checked:opacity-100 \n top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2", children: jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-3.25 w-3.25", viewBox: "0 0 20 20", fill: "currentColor", stroke: "currentColor", strokeWidth: "1", children: jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }) })] }), label && (jsxRuntime.jsx(Label, { htmlFor: checkboxId, className: buildClassName(disabled
@@ -398,15 +404,8 @@ Checkbox.displayName = 'Checkbox';
398
404
 
399
405
  const CheckboxGroup = (props) => {
400
406
  const { name, options, label, labelClass = '', labelWrapperClass = '', labelHint, containerClass = '', value = [], onChange, inline = false, } = props;
401
- const [items, setItems] = React.useState([]);
402
- const groupId = React.useId(); // unique per group
403
- /**
404
- * Normalize options (strings → {label, value})
405
- */
406
- React.useEffect(() => {
407
- const normalized = options.map((opt) => typeof opt === 'string' ? { label: opt, value: opt } : opt);
408
- setItems(normalized);
409
- }, [options]);
407
+ const groupId = React.useId();
408
+ const items = React.useMemo(() => options.map((opt) => typeof opt === 'string' ? { label: opt, value: opt } : opt), [options]);
410
409
  /**
411
410
  * Group-level change handler. Converts raw checkbox change events
412
411
  * into a typed updated values array.
@@ -557,14 +556,14 @@ const Pagination = ({ page: currentPage, limit, total, onChange, className, elli
557
556
  onChange({ page: nextVal });
558
557
  };
559
558
  if (ellipsis) {
560
- 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-next-btn", children: "Prev" }) }), range.map((page, idx) => {
559
+ 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) => {
561
560
  if (page === DOTS) {
562
561
  return (jsxRuntime.jsx("li", { "data-testid": `page-${page}`, className: "px-2 py-1 text-gray-400", children: "\u2026" }, `${page}-${idx}`));
563
562
  }
564
563
  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}`));
565
- }), jsxRuntime.jsx("li", { children: jsxRuntime.jsx(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": "ellipsis-prev-btn", children: "Next" }) })] })] })] }));
564
+ }), 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" }) })] })] })] }));
566
565
  }
567
- 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": "next-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": "prev-btn", children: [jsxRuntime.jsx("span", { className: "sr-only", children: "Next" }), jsxRuntime.jsx(SvgChevronRight, { className: "size-4" })] })] })] })] }));
566
+ 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" })] })] })] })] }));
568
567
  };
569
568
 
570
569
  function get(obj, path, defaultValue) {
@@ -753,9 +752,13 @@ const autoSortItems = (items, column, sort) => {
753
752
  };
754
753
  const DataTable = ({ items, columns, sorting = {}, onSort, loading, pagination = null, setPagination = () => { }, emptyMessage = 'No data found', containerClass = '', wrapperClass = '', layout = 'auto', }) => {
755
754
  const internalSorting = React.useRef({});
755
+ const [sortedItems, setSortedItems] = React.useState(items);
756
+ React.useEffect(() => {
757
+ setSortedItems(items);
758
+ }, [items]);
756
759
  const doAutoSort = (column) => {
757
760
  internalSorting.current = updateSortQuery(internalSorting.current, column.name, column.type);
758
- autoSortItems(items, column, internalSorting.current[column.name]);
761
+ setSortedItems(autoSortItems(items, column, internalSorting.current[column.name]));
759
762
  };
760
763
  const handleSort = (column) => {
761
764
  if (!column.sortable)
@@ -764,7 +767,7 @@ const DataTable = ({ items, columns, sorting = {}, onSort, loading, pagination =
764
767
  };
765
768
  return (jsxRuntime.jsxs("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: [jsxRuntime.jsx("div", { className: buildClassName('overflow-x-auto overflow-y-hidden rounded-lg border border-gray-200 dark:border-gray-800', wrapperClass, pagination && 'rounded-b-none border-b-0'), children: jsxRuntime.jsxs(Table, { className: "relative w-full max-w-full h-fit", layout: layout, children: [jsxRuntime.jsx(TableHead, { children: columns.map((column) => (jsxRuntime.jsx(TableHeaderCell, { align: column.headerAlign, style: { width: column.width || 'auto' }, className: buildClassName('bg-gray-50 dark:bg-gray-800', column.sticky === 'left' &&
766
769
  'sticky left-0 z-10 shadow-[2px_0_6px_rgba(0,0,0,0.06)]', column.sticky === 'right' &&
767
- 'sticky right-0 z-10 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]'), 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: columns.length, rowSize: (pagination === null || pagination === void 0 ? void 0 : pagination.limit) || 5, children: !loading && items.length === 0 ? (jsxRuntime.jsx(EmptyTableRow, { colSpan: columns.length, children: emptyMessage })) : (items.map((item) => (jsxRuntime.jsx(TableRow, { className: "group", children: columns.map((column) => {
770
+ 'sticky right-0 z-10 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]'), 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: columns.length, rowSize: (pagination === null || pagination === void 0 ? void 0 : pagination.limit) || 5, children: !loading && sortedItems.length === 0 ? (jsxRuntime.jsx(EmptyTableRow, { colSpan: columns.length, children: emptyMessage })) : (sortedItems.map((item) => (jsxRuntime.jsx(TableRow, { className: "group", children: columns.map((column) => {
768
771
  var _a;
769
772
  return (jsxRuntime.jsx(TableCell, { align: column.align, className: buildClassName(column.sticky === 'left' &&
770
773
  'sticky left-0 z-10 shadow-[2px_0_6px_rgba(0,0,0,0.06)]', column.sticky === 'right' &&
@@ -878,7 +881,7 @@ Input.displayName = 'Input';
878
881
  const PASSWORD_TYPE = 'password';
879
882
  const PASSWORD_PLACEHOLDER = '••••••••••';
880
883
  const PasswordInput = (props) => {
881
- const { type, placeholder, ...inputProps } = props;
884
+ const { ...inputProps } = props;
882
885
  const [showPassword, setShowPassword] = React.useState(false);
883
886
  return (jsxRuntime.jsx(Input, { ...inputProps, type: showPassword ? 'text' : PASSWORD_TYPE, placeholder: PASSWORD_PLACEHOLDER, rightGroup: jsxRuntime.jsx(Button, { theme: "secondary", variant: "plain", size: "xs", className: "px-1 mr-1", type: "button", onClick: () => setShowPassword(() => !showPassword), tabIndex: -1, children: showPassword ? (jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: "1.5", stroke: "currentColor", className: "size-4", children: jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3.98 8.223A10.477 10.477 0 0 0 1.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.451 10.451 0 0 1 12 4.5c4.756 0 8.773 3.162 10.065 7.498a10.522 10.522 0 0 1-4.293 5.774M6.228 6.228 3 3m3.228 3.228 3.65 3.65m7.894 7.894L21 21m-3.228-3.228-3.65-3.65m0 0a3 3 0 1 0-4.243-4.243m4.242 4.242L9.88 9.88" }) })) : (jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: "1.5", stroke: "currentColor", className: "size-4", children: [jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" }), jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" })] })) }) }));
884
887
  };
@@ -888,8 +891,9 @@ const Radio = React.forwardRef((props, ref) => {
888
891
  const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '',
889
892
  // RadioButton-only props removed from rest
890
893
  ...rest } = props;
891
- const radioId = (_a = id !== null && id !== void 0 ? id : name) !== null && _a !== void 0 ? _a : 'checkbox-' + Math.random();
892
- return (jsxRuntime.jsxs("div", { className: buildClassName(wrapperClass), children: [jsxRuntime.jsxs("div", { className: buildClassName('flex items-center gap-2', containerClass), children: [jsxRuntime.jsx("div", { className: "grid place-items-center", children: jsxRuntime.jsx("input", { ref: ref, ...rest, id: radioId, name: name, type: "radio", disabled: disabled, "aria-selected": rest.checked, className: buildClassName('peer transition-all col-start-1 row-start-1 appearance-none shrink-0 size-4.5 rounded-full', 'border focus:ring-2 focus:ring-offset-2', 'checked:border-5 checked:bg-white', 'focus:ring-blue-500 checked:focus:ring-blue-600 dark:focus:ring-offset-gray-800', disabled
894
+ const generatedId = React.useId();
895
+ const radioId = (_a = id !== null && id !== void 0 ? id : name) !== null && _a !== void 0 ? _a : generatedId;
896
+ return (jsxRuntime.jsxs("div", { className: buildClassName(wrapperClass), children: [jsxRuntime.jsxs("div", { className: buildClassName('flex items-center gap-2', containerClass), children: [jsxRuntime.jsx("div", { className: "grid place-items-center", children: jsxRuntime.jsx("input", { ref: ref, ...rest, id: radioId, name: name, type: "radio", disabled: disabled, "aria-checked": rest.checked, className: buildClassName('peer transition-all col-start-1 row-start-1 appearance-none shrink-0 size-4.5 rounded-full', 'border focus:ring-2 focus:ring-offset-2', 'checked:border-5 checked:bg-white', 'focus:ring-blue-500 checked:focus:ring-blue-600 dark:focus:ring-offset-gray-800', disabled
893
897
  ? 'border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-gray-800 pointer-events-none'
894
898
  : buildClassName('bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600 hover:border-gray-400 dark:hover:border-gray-500', 'checked:border-blue-600 checked:hover:border-blue-600 dark:checked:border-blue-600 dark:checked:hover:border-blue-600'), className) }) }), label && (jsxRuntime.jsx(Label, { htmlFor: radioId, className: buildClassName(disabled
895
899
  ? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
@@ -898,16 +902,9 @@ const Radio = React.forwardRef((props, ref) => {
898
902
 
899
903
  const RadioGroup = (props) => {
900
904
  const { name, label, labelClass, labelWrapperClass, labelHint, containerClass, options, value, row, error, showErrorMessage, onChange, } = props;
901
- const [items, setItems] = React.useState([]);
902
- React.useEffect(() => {
903
- setItems(() => {
904
- return options === null || options === void 0 ? void 0 : options.map((option) => {
905
- if (typeof option === 'string') {
906
- return { label: option, value: option };
907
- }
908
- return option;
909
- });
910
- });
905
+ const items = React.useMemo(() => {
906
+ var _a;
907
+ return (_a = options === null || options === void 0 ? void 0 : options.map((option) => typeof option === 'string' ? { label: option, value: option } : option)) !== null && _a !== void 0 ? _a : [];
911
908
  }, [options]);
912
909
  return (jsxRuntime.jsxs("div", { className: buildClassName(containerClass), children: [label && (jsxRuntime.jsxs("div", { className: buildClassName('flex items-center justify-between mb-4', labelWrapperClass), children: [jsxRuntime.jsx(Label, { className: labelClass, children: label }), labelHint] })), jsxRuntime.jsx("div", { className: buildClassName('flex gap-4', row ? 'flex-row items-center' : 'flex-col items-start'), children: items.map((item) => (jsxRuntime.jsx(Radio, { name: name, id: `${name}-${item.value}`, label: item.label, value: item.value, checked: value === item.value, onChange: onChange }, item.value))) }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) }))] }));
913
910
  };
@@ -1001,35 +998,53 @@ const RangeSlider = ({ label, labelHint, labelClass = '', containerClass = '', c
1001
998
  const activeThumbRef = React.useRef(null);
1002
999
  // State for UI styling
1003
1000
  const [activeThumbState, setActiveThumbState] = React.useState(null);
1001
+ // Keep prop values in refs so drag handlers always use latest values (no stale closure)
1002
+ const valueMinRef = React.useRef(valueMin);
1003
+ const valueMaxRef = React.useRef(valueMax);
1004
+ const onChangeRef = React.useRef(onChange);
1005
+ valueMinRef.current = valueMin;
1006
+ valueMaxRef.current = valueMax;
1007
+ onChangeRef.current = onChange;
1004
1008
  const hasError = !isEmpty(error);
1005
1009
  const percent = (v) => ((v - min) / (max - min)) * 100;
1006
- // ---------------------------------------------
1007
- // Pointer Move (drag)
1008
- // ---------------------------------------------
1009
- const pointerMoveHandler = (e) => {
1010
+ // Stable handler references registered on window — these never change identity
1011
+ // so removeEventListener always finds the right listener to remove.
1012
+ const stablePointerMoveRef = React.useRef((e) => {
1010
1013
  if (!trackRef.current || !activeThumbRef.current)
1011
1014
  return;
1012
1015
  const rect = trackRef.current.getBoundingClientRect();
1013
1016
  const x = Math.min(Math.max(e.clientX - rect.left, 0), rect.width);
1014
1017
  const rawVal = min + (x / rect.width) * (max - min);
1015
1018
  if (activeThumbRef.current === 'min') {
1016
- onChange(Math.min(rawVal, valueMax - 1), valueMax);
1019
+ onChangeRef.current(Math.min(rawVal, valueMaxRef.current - 1), valueMaxRef.current);
1017
1020
  }
1018
1021
  else {
1019
- onChange(valueMin, Math.max(rawVal, valueMin + 1));
1022
+ onChangeRef.current(valueMinRef.current, Math.max(rawVal, valueMinRef.current + 1));
1020
1023
  }
1021
- };
1022
- const pointerUpHandler = () => {
1024
+ });
1025
+ const stablePointerUpRef = React.useRef(() => {
1023
1026
  activeThumbRef.current = null;
1024
1027
  setActiveThumbState(null);
1025
- window.removeEventListener('pointermove', pointerMoveHandler);
1026
- window.removeEventListener('pointerup', pointerUpHandler);
1027
- };
1028
- const startDrag = (thumb) => (e) => {
1028
+ window.removeEventListener('pointermove', stablePointerMoveRef.current);
1029
+ window.removeEventListener('pointerup', stablePointerUpRef.current);
1030
+ });
1031
+ // Remove listeners on unmount in case a drag is in progress
1032
+ React.useEffect(() => {
1033
+ const move = stablePointerMoveRef.current;
1034
+ const up = stablePointerUpRef.current;
1035
+ return () => {
1036
+ window.removeEventListener('pointermove', move);
1037
+ window.removeEventListener('pointerup', up);
1038
+ };
1039
+ }, []);
1040
+ // ---------------------------------------------
1041
+ // Pointer Move (drag)
1042
+ // ---------------------------------------------
1043
+ const startDrag = (thumb) => (_e) => {
1029
1044
  activeThumbRef.current = thumb;
1030
1045
  setActiveThumbState(thumb);
1031
- window.addEventListener('pointermove', pointerMoveHandler);
1032
- window.addEventListener('pointerup', pointerUpHandler);
1046
+ window.addEventListener('pointermove', stablePointerMoveRef.current);
1047
+ window.addEventListener('pointerup', stablePointerUpRef.current);
1033
1048
  };
1034
1049
  // ---------------------------------------------
1035
1050
  // Click Track to move closest thumb
@@ -1091,6 +1106,9 @@ const useDebounce = (value, delay) => {
1091
1106
  };
1092
1107
  const useDebounceFn = (fn, ms) => {
1093
1108
  const timeout = React.useRef(null);
1109
+ React.useEffect(() => {
1110
+ return () => clearTimeout(timeout.current);
1111
+ }, []);
1094
1112
  return React.useCallback((...args) => {
1095
1113
  clearTimeout(timeout.current);
1096
1114
  timeout.current = setTimeout(() => fn(...args), ms);
@@ -1255,7 +1273,7 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
1255
1273
  console.error('onAdd handler error', err);
1256
1274
  }
1257
1275
  }, [onAdd, handleOnChangeInternal]);
1258
- const handleSearch = async (searchString) => {
1276
+ const handleSearch = React.useCallback(async (searchString) => {
1259
1277
  var _a;
1260
1278
  try {
1261
1279
  setOptionsLoading(true);
@@ -1267,11 +1285,11 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
1267
1285
  finally {
1268
1286
  setOptionsLoading(false);
1269
1287
  }
1270
- };
1288
+ }, [props.onSearch]);
1271
1289
  React.useEffect(() => {
1272
1290
  if (async && debouncedQuery)
1273
1291
  handleSearch(debouncedQuery);
1274
- }, [debouncedQuery, async]);
1292
+ }, [debouncedQuery, async, handleSearch]);
1275
1293
  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.jsxs(react.Combobox, { multiple: multiple, immediate: immediate, name: name, value: selected, onChange: comboboxOnChangeWrapper, onClose: () => setQuery(''), disabled: disabled, children: [multiple ? (jsxRuntime.jsxs("div", { className: buildClassName(inputClass, 'flex flex-wrap gap-2 peer relative'), ref: forwardedRef, children: [isArray(selected) &&
1276
1294
  (selected === null || selected === void 0 ? void 0 : selected.length) > 0 &&
1277
1295
  selected.map((option, idx) => (jsxRuntime.jsx(Badge, { theme: "secondary", removable: !disabled, onRemove: () => removeSelection(idx), children: getDisplayValue(option) }, `${getValue(option)}-${idx}`))), jsxRuntime.jsx(react.ComboboxInput, { className: "outline-none flex-1 dark:placeholder-gray-400 placeholder-gray-400", "aria-label": name, value: query, autoComplete: "off", placeholder: (selected === null || selected === void 0 ? void 0 : selected.length) ? '' : placeholder, onChange: (e) => setQuery(e.target.value), id: id, ref: inputRef, onKeyDown: (e) => {
@@ -1384,7 +1402,11 @@ const ToastProvider = ({ children, placement = 'top-center' }) => {
1384
1402
  timeoutRefs.current.delete(id);
1385
1403
  }
1386
1404
  };
1387
- const scheduleClose = (toast) => {
1405
+ const closeToast = React.useCallback((id) => {
1406
+ clearTimer(id);
1407
+ setToasts((prev) => prev.filter((t) => t.id !== id));
1408
+ }, []);
1409
+ const scheduleClose = React.useCallback((toast) => {
1388
1410
  if (!toast.autoClose)
1389
1411
  return;
1390
1412
  clearTimer(toast.id);
@@ -1392,7 +1414,11 @@ const ToastProvider = ({ children, placement = 'top-center' }) => {
1392
1414
  closeToast(toast.id);
1393
1415
  }, toast.duration);
1394
1416
  timeoutRefs.current.set(toast.id, timeout);
1395
- };
1417
+ }, [closeToast]);
1418
+ React.useEffect(() => {
1419
+ const refs = timeoutRefs.current;
1420
+ return () => refs.forEach((id) => clearTimeout(id));
1421
+ }, []);
1396
1422
  const showToast = React.useCallback((message, options) => {
1397
1423
  var _a, _b, _c, _d;
1398
1424
  const id = ++toastCounter;
@@ -1411,11 +1437,7 @@ const ToastProvider = ({ children, placement = 'top-center' }) => {
1411
1437
  return updated;
1412
1438
  });
1413
1439
  scheduleClose(newToast);
1414
- }, []);
1415
- const closeToast = React.useCallback((id) => {
1416
- clearTimer(id);
1417
- setToasts((prev) => prev.filter((t) => t.id !== id));
1418
- }, []);
1440
+ }, [scheduleClose]);
1419
1441
  return (jsxRuntime.jsxs(ToastContext.Provider, { value: { showToast, closeToast }, children: [children, jsxRuntime.jsx("div", { className: buildClassName('fixed z-50 flex flex-col gap-3 pointer-events-none', placementClass[placement]), children: toasts.map((toast, index) => (jsxRuntime.jsx("div", { className: "pointer-events-auto", onMouseEnter: () => toast.pauseOnHover && clearTimer(toast.id), onMouseLeave: () => toast.pauseOnHover && scheduleClose(toast), children: jsxRuntime.jsx(Toast, { ...toast, isTop: placement.startsWith('top'), onClose: () => closeToast(toast.id) }) }, toast.id))) })] }));
1420
1442
  };
1421
1443
 
@@ -2114,6 +2136,12 @@ const useIsTablet = () => {
2114
2136
  return isTablet;
2115
2137
  };
2116
2138
 
2139
+ const useIsTabletOrMobile = () => {
2140
+ const isMobile = useIsMobile();
2141
+ const isTablet = useIsTablet();
2142
+ return isMobile || isTablet;
2143
+ };
2144
+
2117
2145
  exports.Alert = Alert;
2118
2146
  exports.Badge = Badge;
2119
2147
  exports.Banner = Banner;
@@ -2171,6 +2199,7 @@ exports.useDebounce = useDebounce;
2171
2199
  exports.useDebounceFn = useDebounceFn;
2172
2200
  exports.useIsMobile = useIsMobile;
2173
2201
  exports.useIsTablet = useIsTablet;
2202
+ exports.useIsTabletOrMobile = useIsTabletOrMobile;
2174
2203
  exports.usePagination = usePagination;
2175
2204
  exports.useToast = useToast;
2176
2205
  //# sourceMappingURL=react-tailwind.cjs.js.map