@pk-design/react-tailwind 1.1.5 → 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.
- package/dist/react-tailwind.cjs.js +77 -55
- package/dist/react-tailwind.cjs.js.map +1 -1
- package/dist/react-tailwind.es.js +78 -56
- package/dist/react-tailwind.es.js.map +1 -1
- package/dist/types/components/PasswordInput/PasswordInput.d.ts +1 -1
- package/package.json +1 -1
- package/dist/types/components/RangeInput/-backup.d.ts +0 -10
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { twMerge } from 'tailwind-merge';
|
|
3
3
|
import * as React from 'react';
|
|
4
|
-
import React__default, { forwardRef, useMemo,
|
|
4
|
+
import React__default, { forwardRef, useMemo, useId, useRef, useState, useEffect, Fragment as Fragment$1, useCallback, useImperativeHandle, createContext, useContext, memo, useLayoutEffect, createElement } from 'react';
|
|
5
5
|
import { Dialog, DialogBackdrop, DialogPanel, DialogTitle, Menu, MenuButton, MenuItems, MenuItem, RadioGroup as RadioGroup$1, Radio as Radio$1, Combobox, ComboboxInput, ComboboxButton, ComboboxOptions, ComboboxOption, TabGroup, TabList, Tab, TabPanels, TabPanel, Transition } from '@headlessui/react';
|
|
6
6
|
|
|
7
7
|
const buildClassName = (...classes) => twMerge(classes.filter(Boolean));
|
|
@@ -223,7 +223,7 @@ const TypeClassMap = {
|
|
|
223
223
|
const Alert = (props) => {
|
|
224
224
|
const { type = 'info', className = '', message, children, removable = false, onRemove = () => { }, ...restProps } = props;
|
|
225
225
|
const Icon = AlertTypeIconMap[type] || SvgInfo;
|
|
226
|
-
return (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: [jsxs("span", { className: "inline-flex items-center gap-2", children: [jsx(Icon, { className: "size-5", "data-testid": `alert-${type}-icon` }), jsx("span", { className: "pr-2 inline-flex items-center", children: message || children })] }), removable && (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: jsx(SvgX, { className: "size-4.5", "data-testid": "alert-remove-icon" }) }))] }));
|
|
226
|
+
return (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: [jsxs("span", { className: "inline-flex items-center gap-2", children: [jsx(Icon, { className: "size-5", "data-testid": `alert-${type}-icon` }), jsx("span", { className: "pr-2 inline-flex items-center", children: message || children })] }), removable && (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: jsx(SvgX, { className: "size-4.5", "data-testid": "alert-remove-icon" }) }))] }));
|
|
227
227
|
};
|
|
228
228
|
Alert.displayName = 'Alert';
|
|
229
229
|
|
|
@@ -236,7 +236,12 @@ const ThemeClassMap = {
|
|
|
236
236
|
};
|
|
237
237
|
const Badge = forwardRef((props, ref) => {
|
|
238
238
|
const { theme = 'info', className = '', children, removable = false, rounded = false, onRemove = () => { }, ...restProps } = props;
|
|
239
|
-
return (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: [jsx(TextContent, { className: "pr-3 inline-flex items-center", children: children }), removable ? (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()
|
|
239
|
+
return (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: [jsx(TextContent, { className: "pr-3 inline-flex items-center", children: children }), removable ? (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) => {
|
|
240
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
241
|
+
e.preventDefault();
|
|
242
|
+
onRemove();
|
|
243
|
+
}
|
|
244
|
+
} })) : null] }));
|
|
240
245
|
});
|
|
241
246
|
Badge.displayName = 'Badge';
|
|
242
247
|
|
|
@@ -340,7 +345,7 @@ var SvgSlash = function SvgSlash(props) {
|
|
|
340
345
|
const Breadcrumb = ({ items, render }) => {
|
|
341
346
|
const count = items.length;
|
|
342
347
|
const isLast = (index) => index === count - 1;
|
|
343
|
-
return (jsx("
|
|
348
|
+
return (jsx("nav", { "aria-label": "Breadcrumb", className: "flex flex-wrap items-center gap-2", "data-testid": "breadcrumb-container", children: items.map((item, index) => (jsxs("span", { className: "inline-flex gap-2 items-center dark:text-white", "data-testid": `breadcrumb-item-${item.key}`, children: [jsx("span", { className: "font-medium", ...(isLast(index) ? { 'aria-current': 'page' } : {}), children: isLast(index) ? item.text : render(item) }), !isLast(index) && (jsx(SvgSlash, { "data-testid": `breadcrumb-divider-${index}`, className: "size-4" }))] }, item.key))) }));
|
|
344
349
|
};
|
|
345
350
|
Breadcrumb.displayName = 'Breadcrumb';
|
|
346
351
|
|
|
@@ -367,7 +372,8 @@ const Checkbox = forwardRef((props, ref) => {
|
|
|
367
372
|
const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '',
|
|
368
373
|
// Checkbox-only props removed from rest
|
|
369
374
|
...rest } = props;
|
|
370
|
-
const
|
|
375
|
+
const generatedId = useId();
|
|
376
|
+
const checkboxId = (_a = id !== null && id !== void 0 ? id : name) !== null && _a !== void 0 ? _a : generatedId;
|
|
371
377
|
return (jsxs("div", { className: buildClassName(wrapperClass), children: [jsxs("div", { className: buildClassName('flex items-center', containerClass), children: [jsxs("label", { htmlFor: checkboxId, className: buildClassName('inline-flex items-center relative select-none', disabled ? 'cursor-not-allowed opacity-70' : 'cursor-pointer'), children: [jsx("input", { ref: ref, ...rest, id: checkboxId, name: name, type: "checkbox", disabled: disabled, "aria-checked": rest.checked, className: buildClassName('peer size-4.5 cursor-pointer transition-all appearance-none rounded', 'border focus:ring-2 focus:ring-offset-2', 'checked:bg-blue-600 checked:border-blue-600', 'focus:ring-blue-500 dark:focus:ring-offset-gray-900', disabled
|
|
372
378
|
? 'border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-gray-800 pointer-events-none'
|
|
373
379
|
: 'bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600 hover:border-gray-400 dark:hover:border-gray-500 hover:shadow-md', className) }), jsx("span", { className: "absolute text-white opacity-0 peer-checked:opacity-100 \n top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2", children: jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-3.25 w-3.25", viewBox: "0 0 20 20", fill: "currentColor", stroke: "currentColor", strokeWidth: "1", children: jsx("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }) })] }), label && (jsx(Label, { htmlFor: checkboxId, className: buildClassName(disabled
|
|
@@ -378,15 +384,8 @@ Checkbox.displayName = 'Checkbox';
|
|
|
378
384
|
|
|
379
385
|
const CheckboxGroup = (props) => {
|
|
380
386
|
const { name, options, label, labelClass = '', labelWrapperClass = '', labelHint, containerClass = '', value = [], onChange, inline = false, } = props;
|
|
381
|
-
const
|
|
382
|
-
const
|
|
383
|
-
/**
|
|
384
|
-
* Normalize options (strings → {label, value})
|
|
385
|
-
*/
|
|
386
|
-
useEffect(() => {
|
|
387
|
-
const normalized = options.map((opt) => typeof opt === 'string' ? { label: opt, value: opt } : opt);
|
|
388
|
-
setItems(normalized);
|
|
389
|
-
}, [options]);
|
|
387
|
+
const groupId = useId();
|
|
388
|
+
const items = useMemo(() => options.map((opt) => typeof opt === 'string' ? { label: opt, value: opt } : opt), [options]);
|
|
390
389
|
/**
|
|
391
390
|
* Group-level change handler. Converts raw checkbox change events
|
|
392
391
|
* into a typed updated values array.
|
|
@@ -537,14 +536,14 @@ const Pagination = ({ page: currentPage, limit, total, onChange, className, elli
|
|
|
537
536
|
onChange({ page: nextVal });
|
|
538
537
|
};
|
|
539
538
|
if (ellipsis) {
|
|
540
|
-
return (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: [jsxs("div", { className: "flex flex-1 justify-between sm:hidden", children: [jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": "ellipsis-mobile-prev-btn", children: [jsx("span", { children: "Previous" }), jsx(SvgChevronLeft, { className: "size-4" })] }), jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": "ellipsis-mobile-next-btn", children: [jsx("span", { children: "Next" }), jsx(SvgChevronRight, { className: "size-4" })] })] }), jsxs("div", { className: "hidden sm:flex sm:flex-1 sm:items-center sm:justify-between", children: [jsxs("p", { "data-testid": "pagination-summary", className: "text-sm text-gray-600 dark:text-gray-400 flex items-center gap-1", children: ["Showing", jsx("span", { className: "font-medium", children: total === 0 ? 0 : Math.max(currentPage * limit - limit + 1, 1) }), "to", jsx("span", { className: "font-medium", children: Math.min(currentPage * limit, total) }), "of", jsx("span", { className: "font-medium", children: total }), "results"] }), jsxs("ul", { className: "flex items-center space-x-2 text-sm", children: [jsx("li", { children: jsx(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": "ellipsis-
|
|
539
|
+
return (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: [jsxs("div", { className: "flex flex-1 justify-between sm:hidden", children: [jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": "ellipsis-mobile-prev-btn", children: [jsx("span", { children: "Previous" }), jsx(SvgChevronLeft, { className: "size-4" })] }), jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": "ellipsis-mobile-next-btn", children: [jsx("span", { children: "Next" }), jsx(SvgChevronRight, { className: "size-4" })] })] }), jsxs("div", { className: "hidden sm:flex sm:flex-1 sm:items-center sm:justify-between", children: [jsxs("p", { "data-testid": "pagination-summary", className: "text-sm text-gray-600 dark:text-gray-400 flex items-center gap-1", children: ["Showing", jsx("span", { className: "font-medium", children: total === 0 ? 0 : Math.max(currentPage * limit - limit + 1, 1) }), "to", jsx("span", { className: "font-medium", children: Math.min(currentPage * limit, total) }), "of", jsx("span", { className: "font-medium", children: total }), "results"] }), jsxs("ul", { className: "flex items-center space-x-2 text-sm", children: [jsx("li", { children: 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) => {
|
|
541
540
|
if (page === DOTS) {
|
|
542
541
|
return (jsx("li", { "data-testid": `page-${page}`, className: "px-2 py-1 text-gray-400", children: "\u2026" }, `${page}-${idx}`));
|
|
543
542
|
}
|
|
544
543
|
return (jsx("li", { children: 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}`));
|
|
545
|
-
}), jsx("li", { children: jsx(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": "ellipsis-
|
|
544
|
+
}), jsx("li", { children: jsx(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": "ellipsis-next-btn", children: "Next" }) })] })] })] }));
|
|
546
545
|
}
|
|
547
|
-
return (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: [jsxs("div", { className: "flex flex-1 justify-between sm:hidden", children: [jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": "mobile-prev-btn", children: [jsx("span", { children: "Previous" }), jsx(SvgChevronLeft, { className: "size-4" })] }), jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": "mobile-next-btn", children: [jsx("span", { children: "Next" }), jsx(SvgChevronRight, { className: "size-4" })] })] }), jsxs("div", { className: "hidden sm:flex sm:flex-1 sm:items-center sm:justify-between", children: [jsxs("p", { "data-testid": "pagination-summary", className: "text-sm text-gray-600 dark:text-gray-400 flex items-center gap-1", children: ["Showing", jsx("span", { className: "font-medium", children: total === 0 ? 0 : Math.max(currentPage * limit - limit + 1, 1) }), "to", jsx("span", { className: "font-medium", children: Math.min(currentPage * limit, total) }), "of", jsx("span", { className: "font-medium", children: total }), "results"] }), jsxs("nav", { className: "isolate inline-flex items-center rounded-md", "aria-label": "Pagination", children: [jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": "
|
|
546
|
+
return (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: [jsxs("div", { className: "flex flex-1 justify-between sm:hidden", children: [jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": "mobile-prev-btn", children: [jsx("span", { children: "Previous" }), jsx(SvgChevronLeft, { className: "size-4" })] }), jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": "mobile-next-btn", children: [jsx("span", { children: "Next" }), jsx(SvgChevronRight, { className: "size-4" })] })] }), jsxs("div", { className: "hidden sm:flex sm:flex-1 sm:items-center sm:justify-between", children: [jsxs("p", { "data-testid": "pagination-summary", className: "text-sm text-gray-600 dark:text-gray-400 flex items-center gap-1", children: ["Showing", jsx("span", { className: "font-medium", children: total === 0 ? 0 : Math.max(currentPage * limit - limit + 1, 1) }), "to", jsx("span", { className: "font-medium", children: Math.min(currentPage * limit, total) }), "of", jsx("span", { className: "font-medium", children: total }), "results"] }), jsxs("nav", { className: "isolate inline-flex items-center rounded-md", "aria-label": "Pagination", children: [jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isFirstPage, onClick: () => handlePageChange(currentPage - 1), "data-testid": "prev-btn", children: [jsx("span", { className: "sr-only", children: "Previous" }), jsx(SvgChevronLeft, { className: "size-4" })] }), pages.map((page) => (jsx(Button, { size: "xs", theme: page === currentPage ? 'primary' : 'secondary', variant: page === currentPage ? 'default' : 'plain', "aria-current": page === currentPage ? 'page' : undefined, onClick: () => handlePageChange(page), children: jsx("span", { className: "size-4 block", children: page }) }, page))), jsxs(Button, { size: "xs", theme: "secondary", variant: "plain", disabled: isLastPage, onClick: () => handlePageChange(currentPage + 1), "data-testid": "next-btn", children: [jsx("span", { className: "sr-only", children: "Next" }), jsx(SvgChevronRight, { className: "size-4" })] })] })] })] }));
|
|
548
547
|
};
|
|
549
548
|
|
|
550
549
|
function get(obj, path, defaultValue) {
|
|
@@ -733,9 +732,13 @@ const autoSortItems = (items, column, sort) => {
|
|
|
733
732
|
};
|
|
734
733
|
const DataTable = ({ items, columns, sorting = {}, onSort, loading, pagination = null, setPagination = () => { }, emptyMessage = 'No data found', containerClass = '', wrapperClass = '', layout = 'auto', }) => {
|
|
735
734
|
const internalSorting = useRef({});
|
|
735
|
+
const [sortedItems, setSortedItems] = useState(items);
|
|
736
|
+
useEffect(() => {
|
|
737
|
+
setSortedItems(items);
|
|
738
|
+
}, [items]);
|
|
736
739
|
const doAutoSort = (column) => {
|
|
737
740
|
internalSorting.current = updateSortQuery(internalSorting.current, column.name, column.type);
|
|
738
|
-
autoSortItems(items, column, internalSorting.current[column.name]);
|
|
741
|
+
setSortedItems(autoSortItems(items, column, internalSorting.current[column.name]));
|
|
739
742
|
};
|
|
740
743
|
const handleSort = (column) => {
|
|
741
744
|
if (!column.sortable)
|
|
@@ -744,7 +747,7 @@ const DataTable = ({ items, columns, sorting = {}, onSort, loading, pagination =
|
|
|
744
747
|
};
|
|
745
748
|
return (jsxs("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: [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: jsxs(Table, { className: "relative w-full max-w-full h-fit", layout: layout, children: [jsx(TableHead, { children: columns.map((column) => (jsx(TableHeaderCell, { align: column.headerAlign, style: { width: column.width || 'auto' }, className: buildClassName('bg-gray-50 dark:bg-gray-800', column.sticky === 'left' &&
|
|
746
749
|
'sticky left-0 z-10 shadow-[2px_0_6px_rgba(0,0,0,0.06)]', column.sticky === 'right' &&
|
|
747
|
-
'sticky right-0 z-10 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]'), children: 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: [jsx(TextContent, { className: "shrink-0", children: column.label }), column.sortable ? getSortIcon(sorting[column.name]) : null] }) }, column.name))) }), jsx(TableBody, { loading: loading, colSize: columns.length, rowSize: (pagination === null || pagination === void 0 ? void 0 : pagination.limit) || 5, children: !loading &&
|
|
750
|
+
'sticky right-0 z-10 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]'), children: 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: [jsx(TextContent, { className: "shrink-0", children: column.label }), column.sortable ? getSortIcon(sorting[column.name]) : null] }) }, column.name))) }), jsx(TableBody, { loading: loading, colSize: columns.length, rowSize: (pagination === null || pagination === void 0 ? void 0 : pagination.limit) || 5, children: !loading && sortedItems.length === 0 ? (jsx(EmptyTableRow, { colSpan: columns.length, children: emptyMessage })) : (sortedItems.map((item) => (jsx(TableRow, { className: "group", children: columns.map((column) => {
|
|
748
751
|
var _a;
|
|
749
752
|
return (jsx(TableCell, { align: column.align, className: buildClassName(column.sticky === 'left' &&
|
|
750
753
|
'sticky left-0 z-10 shadow-[2px_0_6px_rgba(0,0,0,0.06)]', column.sticky === 'right' &&
|
|
@@ -858,7 +861,7 @@ Input.displayName = 'Input';
|
|
|
858
861
|
const PASSWORD_TYPE = 'password';
|
|
859
862
|
const PASSWORD_PLACEHOLDER = '••••••••••';
|
|
860
863
|
const PasswordInput = (props) => {
|
|
861
|
-
const {
|
|
864
|
+
const { ...inputProps } = props;
|
|
862
865
|
const [showPassword, setShowPassword] = useState(false);
|
|
863
866
|
return (jsx(Input, { ...inputProps, type: showPassword ? 'text' : PASSWORD_TYPE, placeholder: PASSWORD_PLACEHOLDER, rightGroup: jsx(Button, { theme: "secondary", variant: "plain", size: "xs", className: "px-1 mr-1", type: "button", onClick: () => setShowPassword(() => !showPassword), tabIndex: -1, children: showPassword ? (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: "1.5", stroke: "currentColor", className: "size-4", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3.98 8.223A10.477 10.477 0 0 0 1.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.451 10.451 0 0 1 12 4.5c4.756 0 8.773 3.162 10.065 7.498a10.522 10.522 0 0 1-4.293 5.774M6.228 6.228 3 3m3.228 3.228 3.65 3.65m7.894 7.894L21 21m-3.228-3.228-3.65-3.65m0 0a3 3 0 1 0-4.243-4.243m4.242 4.242L9.88 9.88" }) })) : (jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: "1.5", stroke: "currentColor", className: "size-4", children: [jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" }), jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" })] })) }) }));
|
|
864
867
|
};
|
|
@@ -868,8 +871,9 @@ const Radio = forwardRef((props, ref) => {
|
|
|
868
871
|
const { id, name, label, labelClass = '', wrapperClass = '', containerClass = '', error, disabled = false, className = '',
|
|
869
872
|
// RadioButton-only props removed from rest
|
|
870
873
|
...rest } = props;
|
|
871
|
-
const
|
|
872
|
-
|
|
874
|
+
const generatedId = useId();
|
|
875
|
+
const radioId = (_a = id !== null && id !== void 0 ? id : name) !== null && _a !== void 0 ? _a : generatedId;
|
|
876
|
+
return (jsxs("div", { className: buildClassName(wrapperClass), children: [jsxs("div", { className: buildClassName('flex items-center gap-2', containerClass), children: [jsx("div", { className: "grid place-items-center", children: jsx("input", { ref: ref, ...rest, id: radioId, name: name, type: "radio", disabled: disabled, "aria-checked": rest.checked, className: buildClassName('peer transition-all col-start-1 row-start-1 appearance-none shrink-0 size-4.5 rounded-full', 'border focus:ring-2 focus:ring-offset-2', 'checked:border-5 checked:bg-white', 'focus:ring-blue-500 checked:focus:ring-blue-600 dark:focus:ring-offset-gray-800', disabled
|
|
873
877
|
? 'border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-gray-800 pointer-events-none'
|
|
874
878
|
: 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 && (jsx(Label, { htmlFor: radioId, className: buildClassName(disabled
|
|
875
879
|
? 'cursor-not-allowed text-gray-400 dark:text-gray-500'
|
|
@@ -878,16 +882,9 @@ const Radio = forwardRef((props, ref) => {
|
|
|
878
882
|
|
|
879
883
|
const RadioGroup = (props) => {
|
|
880
884
|
const { name, label, labelClass, labelWrapperClass, labelHint, containerClass, options, value, row, error, showErrorMessage, onChange, } = props;
|
|
881
|
-
const
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
return options === null || options === void 0 ? void 0 : options.map((option) => {
|
|
885
|
-
if (typeof option === 'string') {
|
|
886
|
-
return { label: option, value: option };
|
|
887
|
-
}
|
|
888
|
-
return option;
|
|
889
|
-
});
|
|
890
|
-
});
|
|
885
|
+
const items = useMemo(() => {
|
|
886
|
+
var _a;
|
|
887
|
+
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 : [];
|
|
891
888
|
}, [options]);
|
|
892
889
|
return (jsxs("div", { className: buildClassName(containerClass), children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between mb-4', labelWrapperClass), children: [jsx(Label, { className: labelClass, children: label }), labelHint] })), jsx("div", { className: buildClassName('flex gap-4', row ? 'flex-row items-center' : 'flex-col items-start'), children: items.map((item) => (jsx(Radio, { name: name, id: `${name}-${item.value}`, label: item.label, value: item.value, checked: value === item.value, onChange: onChange }, item.value))) }), showErrorMessage && error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
|
|
893
890
|
};
|
|
@@ -981,35 +978,53 @@ const RangeSlider = ({ label, labelHint, labelClass = '', containerClass = '', c
|
|
|
981
978
|
const activeThumbRef = useRef(null);
|
|
982
979
|
// State for UI styling
|
|
983
980
|
const [activeThumbState, setActiveThumbState] = useState(null);
|
|
981
|
+
// Keep prop values in refs so drag handlers always use latest values (no stale closure)
|
|
982
|
+
const valueMinRef = useRef(valueMin);
|
|
983
|
+
const valueMaxRef = useRef(valueMax);
|
|
984
|
+
const onChangeRef = useRef(onChange);
|
|
985
|
+
valueMinRef.current = valueMin;
|
|
986
|
+
valueMaxRef.current = valueMax;
|
|
987
|
+
onChangeRef.current = onChange;
|
|
984
988
|
const hasError = !isEmpty(error);
|
|
985
989
|
const percent = (v) => ((v - min) / (max - min)) * 100;
|
|
986
|
-
//
|
|
987
|
-
//
|
|
988
|
-
|
|
989
|
-
const pointerMoveHandler = (e) => {
|
|
990
|
+
// Stable handler references registered on window — these never change identity
|
|
991
|
+
// so removeEventListener always finds the right listener to remove.
|
|
992
|
+
const stablePointerMoveRef = useRef((e) => {
|
|
990
993
|
if (!trackRef.current || !activeThumbRef.current)
|
|
991
994
|
return;
|
|
992
995
|
const rect = trackRef.current.getBoundingClientRect();
|
|
993
996
|
const x = Math.min(Math.max(e.clientX - rect.left, 0), rect.width);
|
|
994
997
|
const rawVal = min + (x / rect.width) * (max - min);
|
|
995
998
|
if (activeThumbRef.current === 'min') {
|
|
996
|
-
|
|
999
|
+
onChangeRef.current(Math.min(rawVal, valueMaxRef.current - 1), valueMaxRef.current);
|
|
997
1000
|
}
|
|
998
1001
|
else {
|
|
999
|
-
|
|
1002
|
+
onChangeRef.current(valueMinRef.current, Math.max(rawVal, valueMinRef.current + 1));
|
|
1000
1003
|
}
|
|
1001
|
-
};
|
|
1002
|
-
const
|
|
1004
|
+
});
|
|
1005
|
+
const stablePointerUpRef = useRef(() => {
|
|
1003
1006
|
activeThumbRef.current = null;
|
|
1004
1007
|
setActiveThumbState(null);
|
|
1005
|
-
window.removeEventListener('pointermove',
|
|
1006
|
-
window.removeEventListener('pointerup',
|
|
1007
|
-
};
|
|
1008
|
-
|
|
1008
|
+
window.removeEventListener('pointermove', stablePointerMoveRef.current);
|
|
1009
|
+
window.removeEventListener('pointerup', stablePointerUpRef.current);
|
|
1010
|
+
});
|
|
1011
|
+
// Remove listeners on unmount in case a drag is in progress
|
|
1012
|
+
useEffect(() => {
|
|
1013
|
+
const move = stablePointerMoveRef.current;
|
|
1014
|
+
const up = stablePointerUpRef.current;
|
|
1015
|
+
return () => {
|
|
1016
|
+
window.removeEventListener('pointermove', move);
|
|
1017
|
+
window.removeEventListener('pointerup', up);
|
|
1018
|
+
};
|
|
1019
|
+
}, []);
|
|
1020
|
+
// ---------------------------------------------
|
|
1021
|
+
// Pointer Move (drag)
|
|
1022
|
+
// ---------------------------------------------
|
|
1023
|
+
const startDrag = (thumb) => (_e) => {
|
|
1009
1024
|
activeThumbRef.current = thumb;
|
|
1010
1025
|
setActiveThumbState(thumb);
|
|
1011
|
-
window.addEventListener('pointermove',
|
|
1012
|
-
window.addEventListener('pointerup',
|
|
1026
|
+
window.addEventListener('pointermove', stablePointerMoveRef.current);
|
|
1027
|
+
window.addEventListener('pointerup', stablePointerUpRef.current);
|
|
1013
1028
|
};
|
|
1014
1029
|
// ---------------------------------------------
|
|
1015
1030
|
// Click Track to move closest thumb
|
|
@@ -1071,6 +1086,9 @@ const useDebounce = (value, delay) => {
|
|
|
1071
1086
|
};
|
|
1072
1087
|
const useDebounceFn = (fn, ms) => {
|
|
1073
1088
|
const timeout = useRef(null);
|
|
1089
|
+
useEffect(() => {
|
|
1090
|
+
return () => clearTimeout(timeout.current);
|
|
1091
|
+
}, []);
|
|
1074
1092
|
return useCallback((...args) => {
|
|
1075
1093
|
clearTimeout(timeout.current);
|
|
1076
1094
|
timeout.current = setTimeout(() => fn(...args), ms);
|
|
@@ -1235,7 +1253,7 @@ const SelectBox = forwardRef((props, forwardedRef) => {
|
|
|
1235
1253
|
console.error('onAdd handler error', err);
|
|
1236
1254
|
}
|
|
1237
1255
|
}, [onAdd, handleOnChangeInternal]);
|
|
1238
|
-
const handleSearch = async (searchString) => {
|
|
1256
|
+
const handleSearch = useCallback(async (searchString) => {
|
|
1239
1257
|
var _a;
|
|
1240
1258
|
try {
|
|
1241
1259
|
setOptionsLoading(true);
|
|
@@ -1247,11 +1265,11 @@ const SelectBox = forwardRef((props, forwardedRef) => {
|
|
|
1247
1265
|
finally {
|
|
1248
1266
|
setOptionsLoading(false);
|
|
1249
1267
|
}
|
|
1250
|
-
};
|
|
1268
|
+
}, [props.onSearch]);
|
|
1251
1269
|
useEffect(() => {
|
|
1252
1270
|
if (async && debouncedQuery)
|
|
1253
1271
|
handleSearch(debouncedQuery);
|
|
1254
|
-
}, [debouncedQuery, async]);
|
|
1272
|
+
}, [debouncedQuery, async, handleSearch]);
|
|
1255
1273
|
return (jsxs("div", { className: buildClassName('group space-y-2', hasError && 'has-error', containerClass), ref: containerRef, children: [label && (jsxs("div", { className: buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsx(Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), labelHint] })), jsxs(Combobox, { multiple: multiple, immediate: immediate, name: name, value: selected, onChange: comboboxOnChangeWrapper, onClose: () => setQuery(''), disabled: disabled, children: [multiple ? (jsxs("div", { className: buildClassName(inputClass, 'flex flex-wrap gap-2 peer relative'), ref: forwardedRef, children: [isArray(selected) &&
|
|
1256
1274
|
(selected === null || selected === void 0 ? void 0 : selected.length) > 0 &&
|
|
1257
1275
|
selected.map((option, idx) => (jsx(Badge, { theme: "secondary", removable: !disabled, onRemove: () => removeSelection(idx), children: getDisplayValue(option) }, `${getValue(option)}-${idx}`))), jsx(ComboboxInput, { className: "outline-none flex-1 dark:placeholder-gray-400 placeholder-gray-400", "aria-label": name, value: query, autoComplete: "off", placeholder: (selected === null || selected === void 0 ? void 0 : selected.length) ? '' : placeholder, onChange: (e) => setQuery(e.target.value), id: id, ref: inputRef, onKeyDown: (e) => {
|
|
@@ -1364,7 +1382,11 @@ const ToastProvider = ({ children, placement = 'top-center' }) => {
|
|
|
1364
1382
|
timeoutRefs.current.delete(id);
|
|
1365
1383
|
}
|
|
1366
1384
|
};
|
|
1367
|
-
const
|
|
1385
|
+
const closeToast = useCallback((id) => {
|
|
1386
|
+
clearTimer(id);
|
|
1387
|
+
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
1388
|
+
}, []);
|
|
1389
|
+
const scheduleClose = useCallback((toast) => {
|
|
1368
1390
|
if (!toast.autoClose)
|
|
1369
1391
|
return;
|
|
1370
1392
|
clearTimer(toast.id);
|
|
@@ -1372,7 +1394,11 @@ const ToastProvider = ({ children, placement = 'top-center' }) => {
|
|
|
1372
1394
|
closeToast(toast.id);
|
|
1373
1395
|
}, toast.duration);
|
|
1374
1396
|
timeoutRefs.current.set(toast.id, timeout);
|
|
1375
|
-
};
|
|
1397
|
+
}, [closeToast]);
|
|
1398
|
+
useEffect(() => {
|
|
1399
|
+
const refs = timeoutRefs.current;
|
|
1400
|
+
return () => refs.forEach((id) => clearTimeout(id));
|
|
1401
|
+
}, []);
|
|
1376
1402
|
const showToast = useCallback((message, options) => {
|
|
1377
1403
|
var _a, _b, _c, _d;
|
|
1378
1404
|
const id = ++toastCounter;
|
|
@@ -1391,11 +1417,7 @@ const ToastProvider = ({ children, placement = 'top-center' }) => {
|
|
|
1391
1417
|
return updated;
|
|
1392
1418
|
});
|
|
1393
1419
|
scheduleClose(newToast);
|
|
1394
|
-
}, []);
|
|
1395
|
-
const closeToast = useCallback((id) => {
|
|
1396
|
-
clearTimer(id);
|
|
1397
|
-
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
1398
|
-
}, []);
|
|
1420
|
+
}, [scheduleClose]);
|
|
1399
1421
|
return (jsxs(ToastContext.Provider, { value: { showToast, closeToast }, children: [children, jsx("div", { className: buildClassName('fixed z-50 flex flex-col gap-3 pointer-events-none', placementClass[placement]), children: toasts.map((toast, index) => (jsx("div", { className: "pointer-events-auto", onMouseEnter: () => toast.pauseOnHover && clearTimer(toast.id), onMouseLeave: () => toast.pauseOnHover && scheduleClose(toast), children: jsx(Toast, { ...toast, isTop: placement.startsWith('top'), onClose: () => closeToast(toast.id) }) }, toast.id))) })] }));
|
|
1400
1422
|
};
|
|
1401
1423
|
|