@pk-design/react-tailwind 1.2.0 → 1.4.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.
- package/dist/react-tailwind.cjs.js +122 -65
- package/dist/react-tailwind.cjs.js.map +1 -1
- package/dist/react-tailwind.es.js +122 -65
- package/dist/react-tailwind.es.js.map +1 -1
- package/dist/types/components/DataTable/DataTable.d.ts +4 -1
- package/dist/types/components/DataTable/DataTable.types.d.ts +68 -16
- package/dist/types/components/Table/Table.types.d.ts +6 -4
- package/dist/types/components/Tabs/Tabs.types.d.ts +28 -22
- package/dist/types/components/VirtualizedDataTable/VirtualizedDataTable.d.ts +2 -3
- package/dist/types/components/VirtualizedDataTable/VirtualizedDataTable.types.d.ts +31 -7
- package/dist/types/components/VirtualizedDataTable/VirtualizedRow.d.ts +2 -6
- package/package.json +1 -1
|
@@ -430,9 +430,7 @@ const TableRowLoader = ({ colSize, rowSize }) => (jsx(Fragment, { children: Arra
|
|
|
430
430
|
/* ----------------------------------------------
|
|
431
431
|
* Table
|
|
432
432
|
* ---------------------------------------------- */
|
|
433
|
-
const Table = ({ children, layout = 'auto', className = '' }) => {
|
|
434
|
-
return (jsx("table", { className: buildClassName('border-collapse w-full divide-y divide-gray-200 dark:divide-gray-700', TABLE_LAYOUT[layout], className), children: children }));
|
|
435
|
-
};
|
|
433
|
+
const Table = ({ children, layout = 'auto', className = '', caption, }) => (jsxs("table", { className: buildClassName('border-collapse w-full divide-y divide-gray-200 dark:divide-gray-700', TABLE_LAYOUT[layout], className), children: [caption && (jsx("caption", { className: "mb-2 text-sm text-left text-gray-500 dark:text-gray-400", children: caption })), children] }));
|
|
436
434
|
/* ----------------------------------------------
|
|
437
435
|
* Head
|
|
438
436
|
* ---------------------------------------------- */
|
|
@@ -440,7 +438,7 @@ const TableHead = ({ children, className = '' }) => (jsx("thead", { className: b
|
|
|
440
438
|
/* ----------------------------------------------
|
|
441
439
|
* Body
|
|
442
440
|
* ---------------------------------------------- */
|
|
443
|
-
const TableBody = ({ children, loading, colSize, rowSize = 5, className, }) => (jsx("tbody", { className: buildClassName('text-sm text-gray-700 dark:text-white divide-y divide-gray-200 dark:divide-gray-700', className), children: loading ? jsx(TableRowLoader, { colSize: colSize, rowSize: rowSize }) : children }));
|
|
441
|
+
const TableBody = ({ children, loading, colSize = 1, rowSize = 5, className, }) => (jsx("tbody", { className: buildClassName('text-sm text-gray-700 dark:text-white divide-y divide-gray-200 dark:divide-gray-700', className), children: loading ? jsx(TableRowLoader, { colSize: colSize, rowSize: rowSize }) : children }));
|
|
444
442
|
/* ----------------------------------------------
|
|
445
443
|
* Foot
|
|
446
444
|
* ---------------------------------------------- */
|
|
@@ -453,7 +451,7 @@ const TableRow = ({ children, hoverable = false, className, ...props }) => (jsx(
|
|
|
453
451
|
* Cells
|
|
454
452
|
* ---------------------------------------------- */
|
|
455
453
|
const TableCell = ({ children, align = 'left', className, ...props }) => (jsx("td", { ...props, className: buildClassName('px-3 py-3 first:pl-4 last:pr-4 bg-white dark:bg-gray-900 group-hover:bg-gray-50 group-hover:dark:bg-gray-800', TEXT_ALIGN[align], className), children: jsx("div", { className: "leading-normal", children: children }) }));
|
|
456
|
-
const TableHeaderCell = ({ children, align = 'left', className, ...props }) => (jsx("th", { ...props, className: buildClassName('px-3 py-3 first:pl-4 last:pr-4 tracking-wider whitespace-nowrap text-gray-800 dark:text-gray-300', TEXT_ALIGN[align], className), children:
|
|
454
|
+
const TableHeaderCell = ({ children, align = 'left', className, ...props }) => (jsx("th", { ...props, className: buildClassName('px-3 py-3 first:pl-4 last:pr-4 tracking-wider whitespace-nowrap text-gray-800 dark:text-gray-300', TEXT_ALIGN[align], className), children: children }));
|
|
457
455
|
/* ----------------------------------------------
|
|
458
456
|
* Empty Row
|
|
459
457
|
* ---------------------------------------------- */
|
|
@@ -703,9 +701,11 @@ const SortIconMap = {
|
|
|
703
701
|
};
|
|
704
702
|
const getSortIcon = (sort) => sort ? (SortIconMap[sort]) : (jsx(SvgArrowUpDown, { className: "size-4 text-gray-400 shrink-0 group-hover:text-gray-800 dark:group-hover:text-white" }));
|
|
705
703
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
704
|
+
const DENSITY_CELL_CLASS$2 = {
|
|
705
|
+
compact: 'px-2 py-1.5',
|
|
706
|
+
default: '',
|
|
707
|
+
spacious: 'px-4 py-4',
|
|
708
|
+
};
|
|
709
709
|
const autoSortItems = (items, column, sort) => {
|
|
710
710
|
const path = column.name;
|
|
711
711
|
return [...items].sort((a, b) => {
|
|
@@ -721,7 +721,6 @@ const autoSortItems = (items, column, sort) => {
|
|
|
721
721
|
const dB = new Date(valueB).getTime();
|
|
722
722
|
return sort === 'asc' || sort === '1' ? dA - dB : dB - dA;
|
|
723
723
|
}
|
|
724
|
-
// String fallback
|
|
725
724
|
const sA = String(valueA).toLowerCase();
|
|
726
725
|
const sB = String(valueB).toLowerCase();
|
|
727
726
|
if (sA < sB)
|
|
@@ -731,7 +730,7 @@ const autoSortItems = (items, column, sort) => {
|
|
|
731
730
|
return 0;
|
|
732
731
|
});
|
|
733
732
|
};
|
|
734
|
-
|
|
733
|
+
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', }) {
|
|
735
734
|
const internalSorting = useRef({});
|
|
736
735
|
const [sortedItems, setSortedItems] = useState(items);
|
|
737
736
|
useEffect(() => {
|
|
@@ -746,17 +745,38 @@ const DataTable = ({ items, columns, sorting = {}, onSort, loading, pagination =
|
|
|
746
745
|
return;
|
|
747
746
|
onSort ? onSort(column) : doAutoSort(column);
|
|
748
747
|
};
|
|
749
|
-
|
|
748
|
+
const getKey = (item, idx) => {
|
|
749
|
+
var _a;
|
|
750
|
+
if (typeof rowKey === 'function')
|
|
751
|
+
return rowKey(item);
|
|
752
|
+
return (_a = item[rowKey]) !== null && _a !== void 0 ? _a : idx;
|
|
753
|
+
};
|
|
754
|
+
const getRowClass = (item, idx) => {
|
|
755
|
+
if (!rowClass)
|
|
756
|
+
return '';
|
|
757
|
+
if (typeof rowClass === 'function')
|
|
758
|
+
return rowClass(item, idx);
|
|
759
|
+
return rowClass;
|
|
760
|
+
};
|
|
761
|
+
const getCellClass = (column, item) => {
|
|
762
|
+
if (!column.cellClass)
|
|
763
|
+
return '';
|
|
764
|
+
if (typeof column.cellClass === 'function')
|
|
765
|
+
return column.cellClass(item);
|
|
766
|
+
return column.cellClass;
|
|
767
|
+
};
|
|
768
|
+
const densityCellClass = DENSITY_CELL_CLASS$2[density];
|
|
769
|
+
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: buildClassName('relative w-full max-w-full h-fit', tableClass), layout: layout, children: [jsx(TableHead, { className: headClass, children: columns.map((column) => (jsx(TableHeaderCell, { align: column.headerAlign, style: { width: column.width || 'auto' }, className: buildClassName('bg-gray-50 dark:bg-gray-800', densityCellClass, column.sticky === 'left' &&
|
|
750
770
|
'sticky left-0 z-10 shadow-[2px_0_6px_rgba(0,0,0,0.06)]', column.sticky === 'right' &&
|
|
751
|
-
'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:
|
|
771
|
+
'sticky right-0 z-10 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]', column.headerClass), 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, idx) => (jsx(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: columns.map((column) => {
|
|
752
772
|
var _a;
|
|
753
|
-
return (jsx(TableCell, { align: column.align, className: buildClassName(column.sticky === 'left' &&
|
|
773
|
+
return (jsx(TableCell, { align: column.align, className: buildClassName(densityCellClass, striped && idx % 2 === 1 && 'bg-gray-50 dark:bg-gray-800/40', column.sticky === 'left' &&
|
|
754
774
|
'sticky left-0 z-10 shadow-[2px_0_6px_rgba(0,0,0,0.06)]', column.sticky === 'right' &&
|
|
755
|
-
'sticky right-0 z-10 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]'), children: column.render
|
|
775
|
+
'sticky right-0 z-10 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]', getCellClass(column, item)), children: column.render
|
|
756
776
|
? column.render(item)
|
|
757
777
|
: ((_a = get(item, column.name)) !== null && _a !== void 0 ? _a : 'Not set') }, column.name));
|
|
758
|
-
}) }, item
|
|
759
|
-
}
|
|
778
|
+
}) }, getKey(item, idx))))) }), footerRow && jsx(TableFoot, { children: footerRow })] }) }), pagination && (jsx(Pagination, { ...pagination, className: "rounded-b-lg border border-t-0 border-gray-200 dark:border-gray-800", onChange: setPagination }))] }));
|
|
779
|
+
}
|
|
760
780
|
DataTable.displayName = 'DataTable';
|
|
761
781
|
|
|
762
782
|
const Title = ({ children, className }) => (jsx("h1", { className: buildClassName('text-3xl font-bold', className), children: children }));
|
|
@@ -1282,35 +1302,48 @@ const SelectBox = forwardRef((props, forwardedRef) => {
|
|
|
1282
1302
|
}, "data-testid": "combobox-input" }), jsx(ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5", "data-testid": "combobox-button", children: jsx(SvgChevronDown, { className: chevronIconClass }) })] })) : (jsxs("div", { className: "relative", ref: forwardedRef, children: [jsx(ComboboxInput, { "aria-label": name, displayValue: (option) => (option ? getDisplayValue(option) : null), autoComplete: "off", placeholder: placeholder, onChange: (event) => setQuery(event.target.value), className: inputClass, disabled: disabled, id: id, readOnly: !searchable, ref: inputRef, "data-testid": "combobox-input" }), allowClear && !isEmpty(selected) && (jsx(Button, { size: "xs", iconOnly: true, "aria-label": "Clear selection", theme: "secondary", variant: "plain", className: "group absolute inset-y-0 right-8 h-6 top-2", onClick: () => handleOnChangeInternal(multiple ? [] : null), disabled: disabled, "data-testid": "combobox-clear-button", noOutlineOnFocus: true, children: jsx(SvgX, { className: "size-4 stroke-gray-500" }) })), jsx(ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5", "data-testid": "combobox-button", children: jsx(SvgChevronDown, { className: chevronIconClass }) })] })), jsx(ComboboxOptions, { anchor: { to: 'bottom', gap: 5 }, as: "div", className: buildClassName('w-[var(--input-width)] rounded-lg border border-gray-200 dark:border-white/5 bg-white dark:bg-gray-800 p-1 [--anchor-gap:var(--spacing-1)]', '[--anchor-max-height:20rem]', 'origin-top border transition duration-200 ease-[cubic-bezier(0.4, 0.0, 0.2, 1)] data-[closed]:translate-y-[-15%] data-[closed]:opacity-0', dropdownContainerClass), transition: true, "data-testid": "combobox-options", children: optionsLoading ? (jsx("div", { "data-testid": "options-loading", className: "flex items-center justify-center py-2 px-3 select-none", children: jsx(Loader, { size: "sm" }) })) : shouldShowTypeToSearch ? (jsx("div", { className: "flex items-center justify-between py-2 px-3 select-none", children: "Type to search..." })) : (jsxs("div", { children: [filteredOptions.map((option) => (jsx(ComboboxOption, { disabled: option.disabled, value: option, className: "group rounded-lg py-1.5 px-3 select-none data-[focus]:bg-black/3 dark:data-[focus]:bg-white/10 data-disabled:opacity-50 cursor-pointer data-disabled:pointer-events-none", children: jsxs("div", { className: "flex items-center justify-between gap-2 dark:text-white group-data-[selected]:text-blue-600", children: [jsx("span", { className: "text-sm/6", children: getDisplayValue(option) }), jsx(SvgCheck, { className: "invisible size-5 dark:stroke-gray-300 group-data-[selected]:visible" })] }) }, getValue(option)))), filteredOptions.length === 0 && query && !optionsLoading && (jsxs("div", { "data-testid": "no-result-found", className: "flex items-center gap-2 py-1.5 px-3 select-none text-gray-500 dark:text-gray-400", children: ["No results found for ", jsx("span", { className: "font-semibold", children: query })] })), allowAdd && (jsx(ComboboxOption, { value: query, className: "group rounded-lg py-1.5 px-3 cursor-pointer text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700", onClick: () => void handleAddAndSelect(query), children: jsxs("div", { className: "flex items-center gap-2", children: [jsx(SvgCirclePlus, { className: "size-4.5" }), jsxs(TextContent, { children: [addNewText, " \"", query, "\""] })] }) }))] })) })] }), showErrorMessage && error && (jsx("div", { className: "mt-1", children: jsx(TextContent, { error: true, small: true, children: error }) }))] }));
|
|
1283
1303
|
});
|
|
1284
1304
|
|
|
1285
|
-
const Tabs = ({ tabs, defaultIndex = 0, variant = 'underline', className = '', listClass = '',
|
|
1286
|
-
|
|
1305
|
+
const Tabs = ({ tabs, defaultIndex = 0, selectedIndex, onChange, variant = 'underline', orientation = 'horizontal', stretch = false, className = '', listClass = '', panelsClass = '', }) => {
|
|
1306
|
+
const isVertical = orientation === 'vertical';
|
|
1307
|
+
return (jsxs(TabGroup, { defaultIndex: defaultIndex, selectedIndex: selectedIndex, onChange: onChange, vertical: isVertical, className: buildClassName(isVertical && 'flex gap-4', stretch && !isVertical && 'w-full', className), children: [jsx(TabList, { className: buildClassName(isVertical ? 'flex flex-col gap-1' : 'flex gap-2', stretch && !isVertical && 'w-full', listClass), children: tabs.map((tab, idx) => {
|
|
1308
|
+
var _a;
|
|
1309
|
+
return (jsx(Tab, { disabled: tab.disabled, as: Fragment$1, children: ({ selected }) => (jsxs("button", { disabled: tab.disabled, className: buildTabClass({
|
|
1310
|
+
selected,
|
|
1311
|
+
variant,
|
|
1312
|
+
stretch,
|
|
1313
|
+
isVertical,
|
|
1314
|
+
tabClass: tab.tabClass,
|
|
1315
|
+
}), children: [tab.icon && jsx("span", { className: "inline-flex shrink-0", children: tab.icon }), tab.label] })) }, (_a = tab.id) !== null && _a !== void 0 ? _a : idx));
|
|
1316
|
+
}) }), jsx(TabPanels, { className: buildClassName(isVertical ? 'flex-1' : 'mt-3', panelsClass), children: tabs.map((tab, idx) => {
|
|
1317
|
+
var _a;
|
|
1318
|
+
return (jsx(TabPanel, { className: tab.panelClass !== undefined
|
|
1319
|
+
? tab.panelClass
|
|
1320
|
+
: 'rounded-lg bg-white dark:bg-gray-800 p-4 shadow-sm', children: tab.content }, (_a = tab.id) !== null && _a !== void 0 ? _a : idx));
|
|
1321
|
+
}) })] }));
|
|
1287
1322
|
};
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
function buildTabClass({ selected, variant, }) {
|
|
1323
|
+
function buildTabClass({ selected, variant, stretch, isVertical, tabClass, }) {
|
|
1324
|
+
const base = buildClassName('inline-flex items-center gap-1.5 text-sm font-medium transition focus-visible:outline-none', 'disabled:opacity-40 disabled:cursor-not-allowed disabled:pointer-events-none', stretch && !isVertical && 'flex-1 justify-center');
|
|
1325
|
+
let variantClass;
|
|
1292
1326
|
switch (variant) {
|
|
1293
|
-
/**
|
|
1294
|
-
* UNDERLINE (Default)
|
|
1295
|
-
*/
|
|
1296
1327
|
case 'underline':
|
|
1297
|
-
|
|
1298
|
-
'
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1328
|
+
variantClass = isVertical
|
|
1329
|
+
? buildClassName('px-3 py-2 w-full text-left border-l-2', selected
|
|
1330
|
+
? 'border-[var(--ui-primary)] text-gray-900 dark:text-white'
|
|
1331
|
+
: 'border-transparent text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white hover:border-gray-300 dark:hover:border-gray-600')
|
|
1332
|
+
: buildClassName('px-3 py-1.5 relative text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white', selected &&
|
|
1333
|
+
'text-gray-900 dark:text-white after:absolute after:z-10 after:left-0 after:right-0 after:-bottom-1 after:h-0.5 after:bg-[var(--ui-primary)]');
|
|
1334
|
+
break;
|
|
1302
1335
|
case 'segmented':
|
|
1303
|
-
|
|
1336
|
+
variantClass = buildClassName('px-3 py-1.5 rounded-lg', selected
|
|
1304
1337
|
? 'bg-[var(--ui-primary)] text-white'
|
|
1305
1338
|
: 'text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700');
|
|
1306
|
-
|
|
1307
|
-
* SOLID
|
|
1308
|
-
*/
|
|
1339
|
+
break;
|
|
1309
1340
|
case 'solid':
|
|
1310
|
-
|
|
1341
|
+
variantClass = buildClassName('px-3 py-1.5 rounded-md', selected
|
|
1311
1342
|
? 'bg-gray-900 dark:bg-gray-700 text-white'
|
|
1312
1343
|
: 'bg-gray-200 dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-300 dark:hover:bg-gray-700');
|
|
1344
|
+
break;
|
|
1313
1345
|
}
|
|
1346
|
+
return buildClassName(base, variantClass, tabClass);
|
|
1314
1347
|
}
|
|
1315
1348
|
|
|
1316
1349
|
const Textarea = forwardRef((props, ref) => {
|
|
@@ -1415,33 +1448,61 @@ const ToastProvider = ({ children, placement = 'top-center' }) => {
|
|
|
1415
1448
|
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))) })] }));
|
|
1416
1449
|
};
|
|
1417
1450
|
|
|
1418
|
-
const
|
|
1419
|
-
|
|
1451
|
+
const DENSITY_CELL_CLASS$1 = {
|
|
1452
|
+
compact: 'px-2 py-1.5',
|
|
1453
|
+
default: 'px-3 py-3',
|
|
1454
|
+
spacious: 'px-4 py-4',
|
|
1455
|
+
};
|
|
1456
|
+
const VirtualizedRow = ({ index, style, data, }) => {
|
|
1457
|
+
const { items, columns, gridTemplate, onRowClick, rowClass, density = 'default', striped } = data;
|
|
1420
1458
|
const item = items[index];
|
|
1421
|
-
|
|
1459
|
+
const isStriped = striped && index % 2 === 1;
|
|
1460
|
+
const resolvedRowClass = !rowClass
|
|
1461
|
+
? ''
|
|
1462
|
+
: typeof rowClass === 'function'
|
|
1463
|
+
? rowClass(item, index)
|
|
1464
|
+
: rowClass;
|
|
1465
|
+
const getCellClass = (col) => {
|
|
1466
|
+
if (!col.cellClass)
|
|
1467
|
+
return '';
|
|
1468
|
+
if (typeof col.cellClass === 'function')
|
|
1469
|
+
return col.cellClass(item);
|
|
1470
|
+
return col.cellClass;
|
|
1471
|
+
};
|
|
1472
|
+
const densityCellClass = DENSITY_CELL_CLASS$1[density];
|
|
1473
|
+
return (jsx("div", { role: "row", className: buildClassName('grid border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 group', onRowClick && 'cursor-pointer', isStriped && 'bg-gray-50 dark:bg-gray-800/40', resolvedRowClass), style: {
|
|
1422
1474
|
...style,
|
|
1423
1475
|
display: 'grid',
|
|
1424
1476
|
gridTemplateColumns: gridTemplate,
|
|
1425
1477
|
minWidth: 'fit-content',
|
|
1426
|
-
}, children: columns.map((col) => {
|
|
1478
|
+
}, onClick: onRowClick ? () => onRowClick(item, index) : undefined, children: columns.map((col) => {
|
|
1427
1479
|
var _a;
|
|
1428
1480
|
const content = col.render ? col.render(item) : ((_a = get(item, col.name)) !== null && _a !== void 0 ? _a : 'Not set');
|
|
1429
|
-
return (jsx("div", { role: "cell", className: buildClassName('
|
|
1481
|
+
return (jsx("div", { role: "cell", className: buildClassName('whitespace-nowrap border-r last:border-r-0 border-gray-200 dark:border-gray-700', densityCellClass, col.align === 'center' && 'text-center', col.align === 'right' && 'text-right', col.sticky === 'left' &&
|
|
1430
1482
|
'sticky left-0 z-10 bg-white dark:bg-gray-900 shadow-[2px_0_4px_rgba(0,0,0,0.05)]', col.sticky === 'right' &&
|
|
1431
|
-
'sticky right-0 z-10 bg-white dark:bg-gray-900 shadow-[-2px_0_4px_rgba(0,0,0,0.05)]'), children: content }, col.name));
|
|
1483
|
+
'sticky right-0 z-10 bg-white dark:bg-gray-900 shadow-[-2px_0_4px_rgba(0,0,0,0.05)]', isStriped && 'bg-gray-50 dark:bg-gray-800/40', getCellClass(col)), children: content }, col.name));
|
|
1432
1484
|
}) }));
|
|
1433
1485
|
};
|
|
1434
1486
|
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1487
|
+
const DENSITY_CELL_CLASS = {
|
|
1488
|
+
compact: 'px-2 py-1.5',
|
|
1489
|
+
default: 'px-3 py-3',
|
|
1490
|
+
spacious: 'px-4 py-4',
|
|
1491
|
+
};
|
|
1492
|
+
const columnWidth = (col) => {
|
|
1493
|
+
if (!col.width)
|
|
1494
|
+
return '1fr';
|
|
1495
|
+
if (typeof col.width === 'number')
|
|
1496
|
+
return `${col.width}px`;
|
|
1497
|
+
return col.width;
|
|
1498
|
+
};
|
|
1499
|
+
function VirtualizedDataTable({ items, columns, sorting = {}, onSort, loading = false, emptyMessage = 'No data available', containerClass = '', wrapperClass = '', onRowClick, rowClass, density = 'default', striped = false, rowHeight = 48, maxHeight = 400, overscanCount = 3, }) {
|
|
1440
1500
|
const scrollRef = useRef(null);
|
|
1441
1501
|
const outerRef = useRef(null);
|
|
1442
1502
|
const headerRef = useRef(null);
|
|
1443
|
-
const gridTemplate =
|
|
1444
|
-
|
|
1503
|
+
const gridTemplate = useMemo(() => columns.map((col) => columnWidth(col)).join(' '), [columns]);
|
|
1504
|
+
const densityCellClass = DENSITY_CELL_CLASS[density];
|
|
1505
|
+
const skeletonRows = Math.max(1, Math.floor(maxHeight / rowHeight));
|
|
1445
1506
|
useEffect(() => {
|
|
1446
1507
|
const el = outerRef.current;
|
|
1447
1508
|
if (!el)
|
|
@@ -1457,35 +1518,31 @@ const VirtualizedDataTable = ({ items, columns, sorting = {}, onSort, loading =
|
|
|
1457
1518
|
if (column.sortable)
|
|
1458
1519
|
onSort === null || onSort === void 0 ? void 0 : onSort(column);
|
|
1459
1520
|
}, [onSort]);
|
|
1521
|
+
const itemData = useMemo(() => ({ items, columns, gridTemplate, onRowClick, rowClass, density, striped }), [items, columns, gridTemplate, onRowClick, rowClass, density, striped]);
|
|
1460
1522
|
/* -------------------------------------------------------
|
|
1461
|
-
*
|
|
1523
|
+
* Header
|
|
1462
1524
|
* ------------------------------------------------------- */
|
|
1463
|
-
const Header = (jsx("div", { ref: headerRef, className: "overflow-hidden w-full", children: jsx("div", { className: "grid bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700", style: {
|
|
1464
|
-
gridTemplateColumns: gridTemplate,
|
|
1465
|
-
minWidth: 'fit-content',
|
|
1466
|
-
}, children: columns.map((col) => (jsx("div", { role: "columnheader", className: buildClassName('px-3 py-3 font-semibold text-sm whitespace-nowrap flex items-center', 'border-r last:border-r-0 border-gray-200 dark:border-gray-700', col.sticky === 'left' &&
|
|
1525
|
+
const Header = (jsx("div", { ref: headerRef, className: "overflow-hidden w-full", children: jsx("div", { className: "grid bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700", style: { gridTemplateColumns: gridTemplate, minWidth: 'fit-content' }, children: columns.map((col) => (jsx("div", { role: "columnheader", className: buildClassName('font-semibold text-sm whitespace-nowrap flex items-center', densityCellClass, 'border-r last:border-r-0 border-gray-200 dark:border-gray-700', col.sticky === 'left' &&
|
|
1467
1526
|
'sticky left-0 z-20 bg-gray-50 dark:bg-gray-800 shadow-[2px_0_4px_rgba(0,0,0,0.05)]', col.sticky === 'right' &&
|
|
1468
|
-
'sticky right-0 z-20 bg-gray-50 dark:bg-gray-800 shadow-[-2px_0_4px_rgba(0,0,0,0.05)]'), children: jsxs(TextContent, { role: col.sortable ? 'button' : undefined, className: buildClassName(col.sortable && 'inline-flex items-center gap-2 cursor-pointer select-none'), onClick: () => handleSort(col), children: [col.label, col.sortable ? getSortIcon(sorting[col.name]) : null] }) }, col.name))) }) }));
|
|
1527
|
+
'sticky right-0 z-20 bg-gray-50 dark:bg-gray-800 shadow-[-2px_0_4px_rgba(0,0,0,0.05)]', col.headerClass), children: jsxs(TextContent, { role: col.sortable ? 'button' : undefined, className: buildClassName(col.sortable && 'inline-flex items-center gap-2 cursor-pointer select-none'), onClick: () => handleSort(col), children: [col.label, col.sortable ? getSortIcon(sorting[col.name]) : null] }) }, col.name))) }) }));
|
|
1528
|
+
const wrapperCls = buildClassName('rounded-lg border border-gray-200 dark:border-gray-800 overflow-hidden', wrapperClass);
|
|
1469
1529
|
/* -------------------------------------------------------
|
|
1470
|
-
*
|
|
1530
|
+
* Loading state
|
|
1471
1531
|
* ------------------------------------------------------- */
|
|
1472
1532
|
if (loading) {
|
|
1473
|
-
return (jsx("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: jsxs("div", { className:
|
|
1474
|
-
gridTemplateColumns: gridTemplate,
|
|
1475
|
-
height: rowHeight,
|
|
1476
|
-
}, children: columns.map((col) => (jsx("div", { className: "px-3 py-3", children: jsx("div", { className: "h-4 w-3/4 bg-gray-300 dark:bg-gray-700 rounded" }) }, col.name))) }, idx))) })] }) }));
|
|
1533
|
+
return (jsx("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: jsxs("div", { className: wrapperCls, children: [Header, jsx("div", { style: { height: maxHeight }, className: "flex flex-col", children: Array.from({ length: skeletonRows }).map((_, idx) => (jsx("div", { "data-testid": "skeleton-row", className: "grid animate-pulse bg-white dark:bg-gray-900 border-b border-gray-100 dark:border-gray-800", style: { gridTemplateColumns: gridTemplate, height: rowHeight }, children: columns.map((col) => (jsx("div", { className: densityCellClass, children: jsx("div", { className: "h-4 w-3/4 bg-gray-300 dark:bg-gray-700 rounded" }) }, col.name))) }, idx))) })] }) }));
|
|
1477
1534
|
}
|
|
1478
1535
|
/* -------------------------------------------------------
|
|
1479
|
-
*
|
|
1536
|
+
* Empty state
|
|
1480
1537
|
* ------------------------------------------------------- */
|
|
1481
|
-
if (
|
|
1482
|
-
return (jsx("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: jsxs("div", { className:
|
|
1538
|
+
if (items.length === 0) {
|
|
1539
|
+
return (jsx("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: jsxs("div", { className: wrapperCls, children: [Header, jsx("div", { className: "flex items-center justify-center text-gray-500 dark:text-gray-300", style: { height: maxHeight }, children: emptyMessage })] }) }));
|
|
1483
1540
|
}
|
|
1484
1541
|
/* -------------------------------------------------------
|
|
1485
|
-
*
|
|
1542
|
+
* Virtualized body
|
|
1486
1543
|
* ------------------------------------------------------- */
|
|
1487
|
-
return (jsx("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: jsxs("div", { className:
|
|
1488
|
-
}
|
|
1544
|
+
return (jsx("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: jsxs("div", { className: wrapperCls, children: [Header, jsx(FixedSizeList, { ref: scrollRef, outerRef: outerRef, height: maxHeight, width: "100%", itemCount: items.length, itemSize: rowHeight, itemData: itemData, overscanCount: overscanCount, children: VirtualizedRow })] }) }));
|
|
1545
|
+
}
|
|
1489
1546
|
|
|
1490
1547
|
const useIsMobile = () => {
|
|
1491
1548
|
const [isMobile, setIsMobile] = useState(false);
|