@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
|
@@ -450,9 +450,7 @@ const TableRowLoader = ({ colSize, rowSize }) => (jsxRuntime.jsx(jsxRuntime.Frag
|
|
|
450
450
|
/* ----------------------------------------------
|
|
451
451
|
* Table
|
|
452
452
|
* ---------------------------------------------- */
|
|
453
|
-
const Table = ({ children, layout = 'auto', className = '' }) => {
|
|
454
|
-
return (jsxRuntime.jsx("table", { className: buildClassName('border-collapse w-full divide-y divide-gray-200 dark:divide-gray-700', TABLE_LAYOUT[layout], className), children: children }));
|
|
455
|
-
};
|
|
453
|
+
const Table = ({ children, layout = 'auto', className = '', caption, }) => (jsxRuntime.jsxs("table", { className: buildClassName('border-collapse w-full divide-y divide-gray-200 dark:divide-gray-700', TABLE_LAYOUT[layout], className), children: [caption && (jsxRuntime.jsx("caption", { className: "mb-2 text-sm text-left text-gray-500 dark:text-gray-400", children: caption })), children] }));
|
|
456
454
|
/* ----------------------------------------------
|
|
457
455
|
* Head
|
|
458
456
|
* ---------------------------------------------- */
|
|
@@ -460,7 +458,7 @@ const TableHead = ({ children, className = '' }) => (jsxRuntime.jsx("thead", { c
|
|
|
460
458
|
/* ----------------------------------------------
|
|
461
459
|
* Body
|
|
462
460
|
* ---------------------------------------------- */
|
|
463
|
-
const TableBody = ({ children, loading, colSize, rowSize = 5, className, }) => (jsxRuntime.jsx("tbody", { className: buildClassName('text-sm text-gray-700 dark:text-white divide-y divide-gray-200 dark:divide-gray-700', className), children: loading ? jsxRuntime.jsx(TableRowLoader, { colSize: colSize, rowSize: rowSize }) : children }));
|
|
461
|
+
const TableBody = ({ children, loading, colSize = 1, rowSize = 5, className, }) => (jsxRuntime.jsx("tbody", { className: buildClassName('text-sm text-gray-700 dark:text-white divide-y divide-gray-200 dark:divide-gray-700', className), children: loading ? jsxRuntime.jsx(TableRowLoader, { colSize: colSize, rowSize: rowSize }) : children }));
|
|
464
462
|
/* ----------------------------------------------
|
|
465
463
|
* Foot
|
|
466
464
|
* ---------------------------------------------- */
|
|
@@ -473,7 +471,7 @@ const TableRow = ({ children, hoverable = false, className, ...props }) => (jsxR
|
|
|
473
471
|
* Cells
|
|
474
472
|
* ---------------------------------------------- */
|
|
475
473
|
const TableCell = ({ children, align = 'left', className, ...props }) => (jsxRuntime.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: jsxRuntime.jsx("div", { className: "leading-normal", children: children }) }));
|
|
476
|
-
const TableHeaderCell = ({ children, align = 'left', className, ...props }) => (jsxRuntime.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:
|
|
474
|
+
const TableHeaderCell = ({ children, align = 'left', className, ...props }) => (jsxRuntime.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 }));
|
|
477
475
|
/* ----------------------------------------------
|
|
478
476
|
* Empty Row
|
|
479
477
|
* ---------------------------------------------- */
|
|
@@ -723,9 +721,11 @@ const SortIconMap = {
|
|
|
723
721
|
};
|
|
724
722
|
const getSortIcon = (sort) => sort ? (SortIconMap[sort]) : (jsxRuntime.jsx(SvgArrowUpDown, { className: "size-4 text-gray-400 shrink-0 group-hover:text-gray-800 dark:group-hover:text-white" }));
|
|
725
723
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
724
|
+
const DENSITY_CELL_CLASS$2 = {
|
|
725
|
+
compact: 'px-2 py-1.5',
|
|
726
|
+
default: '',
|
|
727
|
+
spacious: 'px-4 py-4',
|
|
728
|
+
};
|
|
729
729
|
const autoSortItems = (items, column, sort) => {
|
|
730
730
|
const path = column.name;
|
|
731
731
|
return [...items].sort((a, b) => {
|
|
@@ -741,7 +741,6 @@ const autoSortItems = (items, column, sort) => {
|
|
|
741
741
|
const dB = new Date(valueB).getTime();
|
|
742
742
|
return sort === 'asc' || sort === '1' ? dA - dB : dB - dA;
|
|
743
743
|
}
|
|
744
|
-
// String fallback
|
|
745
744
|
const sA = String(valueA).toLowerCase();
|
|
746
745
|
const sB = String(valueB).toLowerCase();
|
|
747
746
|
if (sA < sB)
|
|
@@ -751,7 +750,7 @@ const autoSortItems = (items, column, sort) => {
|
|
|
751
750
|
return 0;
|
|
752
751
|
});
|
|
753
752
|
};
|
|
754
|
-
|
|
753
|
+
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', }) {
|
|
755
754
|
const internalSorting = React.useRef({});
|
|
756
755
|
const [sortedItems, setSortedItems] = React.useState(items);
|
|
757
756
|
React.useEffect(() => {
|
|
@@ -766,17 +765,38 @@ const DataTable = ({ items, columns, sorting = {}, onSort, loading, pagination =
|
|
|
766
765
|
return;
|
|
767
766
|
onSort ? onSort(column) : doAutoSort(column);
|
|
768
767
|
};
|
|
769
|
-
|
|
768
|
+
const getKey = (item, idx) => {
|
|
769
|
+
var _a;
|
|
770
|
+
if (typeof rowKey === 'function')
|
|
771
|
+
return rowKey(item);
|
|
772
|
+
return (_a = item[rowKey]) !== null && _a !== void 0 ? _a : idx;
|
|
773
|
+
};
|
|
774
|
+
const getRowClass = (item, idx) => {
|
|
775
|
+
if (!rowClass)
|
|
776
|
+
return '';
|
|
777
|
+
if (typeof rowClass === 'function')
|
|
778
|
+
return rowClass(item, idx);
|
|
779
|
+
return rowClass;
|
|
780
|
+
};
|
|
781
|
+
const getCellClass = (column, item) => {
|
|
782
|
+
if (!column.cellClass)
|
|
783
|
+
return '';
|
|
784
|
+
if (typeof column.cellClass === 'function')
|
|
785
|
+
return column.cellClass(item);
|
|
786
|
+
return column.cellClass;
|
|
787
|
+
};
|
|
788
|
+
const densityCellClass = DENSITY_CELL_CLASS$2[density];
|
|
789
|
+
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: buildClassName('relative w-full max-w-full h-fit', tableClass), layout: layout, children: [jsxRuntime.jsx(TableHead, { className: headClass, children: columns.map((column) => (jsxRuntime.jsx(TableHeaderCell, { align: column.headerAlign, style: { width: column.width || 'auto' }, className: buildClassName('bg-gray-50 dark:bg-gray-800', densityCellClass, column.sticky === 'left' &&
|
|
770
790
|
'sticky left-0 z-10 shadow-[2px_0_6px_rgba(0,0,0,0.06)]', column.sticky === 'right' &&
|
|
771
|
-
'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:
|
|
791
|
+
'sticky right-0 z-10 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]', column.headerClass), children: jsxRuntime.jsxs(TextContent, { role: column.sortable ? 'button' : undefined, className: buildClassName('group', column.sortable && 'inline-flex items-center gap-2 cursor-pointer select-none'), onClick: () => handleSort(column), children: [jsxRuntime.jsx(TextContent, { className: "shrink-0", children: column.label }), column.sortable ? getSortIcon(sorting[column.name]) : null] }) }, column.name))) }), jsxRuntime.jsx(TableBody, { loading: loading, colSize: 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, idx) => (jsxRuntime.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) => {
|
|
772
792
|
var _a;
|
|
773
|
-
return (jsxRuntime.jsx(TableCell, { align: column.align, className: buildClassName(column.sticky === 'left' &&
|
|
793
|
+
return (jsxRuntime.jsx(TableCell, { align: column.align, className: buildClassName(densityCellClass, striped && idx % 2 === 1 && 'bg-gray-50 dark:bg-gray-800/40', column.sticky === 'left' &&
|
|
774
794
|
'sticky left-0 z-10 shadow-[2px_0_6px_rgba(0,0,0,0.06)]', column.sticky === 'right' &&
|
|
775
|
-
'sticky right-0 z-10 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]'), children: column.render
|
|
795
|
+
'sticky right-0 z-10 shadow-[-2px_0_6px_rgba(0,0,0,0.06)]', getCellClass(column, item)), children: column.render
|
|
776
796
|
? column.render(item)
|
|
777
797
|
: ((_a = get(item, column.name)) !== null && _a !== void 0 ? _a : 'Not set') }, column.name));
|
|
778
|
-
}) }, item
|
|
779
|
-
}
|
|
798
|
+
}) }, getKey(item, idx))))) }), footerRow && jsxRuntime.jsx(TableFoot, { children: footerRow })] }) }), pagination && (jsxRuntime.jsx(Pagination, { ...pagination, className: "rounded-b-lg border border-t-0 border-gray-200 dark:border-gray-800", onChange: setPagination }))] }));
|
|
799
|
+
}
|
|
780
800
|
DataTable.displayName = 'DataTable';
|
|
781
801
|
|
|
782
802
|
const Title = ({ children, className }) => (jsxRuntime.jsx("h1", { className: buildClassName('text-3xl font-bold', className), children: children }));
|
|
@@ -1302,35 +1322,48 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
|
|
|
1302
1322
|
}, "data-testid": "combobox-input" }), jsxRuntime.jsx(react.ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5", "data-testid": "combobox-button", children: jsxRuntime.jsx(SvgChevronDown, { className: chevronIconClass }) })] })) : (jsxRuntime.jsxs("div", { className: "relative", ref: forwardedRef, children: [jsxRuntime.jsx(react.ComboboxInput, { "aria-label": name, displayValue: (option) => (option ? getDisplayValue(option) : null), autoComplete: "off", placeholder: placeholder, onChange: (event) => setQuery(event.target.value), className: inputClass, disabled: disabled, id: id, readOnly: !searchable, ref: inputRef, "data-testid": "combobox-input" }), allowClear && !isEmpty(selected) && (jsxRuntime.jsx(Button, { size: "xs", iconOnly: true, "aria-label": "Clear selection", theme: "secondary", variant: "plain", className: "group absolute inset-y-0 right-8 h-6 top-2", onClick: () => handleOnChangeInternal(multiple ? [] : null), disabled: disabled, "data-testid": "combobox-clear-button", noOutlineOnFocus: true, children: jsxRuntime.jsx(SvgX, { className: "size-4 stroke-gray-500" }) })), jsxRuntime.jsx(react.ComboboxButton, { className: "group absolute inset-y-0 right-0 px-2.5", "data-testid": "combobox-button", children: jsxRuntime.jsx(SvgChevronDown, { className: chevronIconClass }) })] })), jsxRuntime.jsx(react.ComboboxOptions, { anchor: { to: 'bottom', gap: 5 }, as: "div", className: buildClassName('w-[var(--input-width)] rounded-lg border border-gray-200 dark:border-white/5 bg-white dark:bg-gray-800 p-1 [--anchor-gap:var(--spacing-1)]', '[--anchor-max-height:20rem]', 'origin-top border transition duration-200 ease-[cubic-bezier(0.4, 0.0, 0.2, 1)] data-[closed]:translate-y-[-15%] data-[closed]:opacity-0', dropdownContainerClass), transition: true, "data-testid": "combobox-options", children: optionsLoading ? (jsxRuntime.jsx("div", { "data-testid": "options-loading", className: "flex items-center justify-center py-2 px-3 select-none", children: jsxRuntime.jsx(Loader, { size: "sm" }) })) : shouldShowTypeToSearch ? (jsxRuntime.jsx("div", { className: "flex items-center justify-between py-2 px-3 select-none", children: "Type to search..." })) : (jsxRuntime.jsxs("div", { children: [filteredOptions.map((option) => (jsxRuntime.jsx(react.ComboboxOption, { disabled: option.disabled, value: option, className: "group rounded-lg py-1.5 px-3 select-none data-[focus]:bg-black/3 dark:data-[focus]:bg-white/10 data-disabled:opacity-50 cursor-pointer data-disabled:pointer-events-none", children: jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 dark:text-white group-data-[selected]:text-blue-600", children: [jsxRuntime.jsx("span", { className: "text-sm/6", children: getDisplayValue(option) }), jsxRuntime.jsx(SvgCheck, { className: "invisible size-5 dark:stroke-gray-300 group-data-[selected]:visible" })] }) }, getValue(option)))), filteredOptions.length === 0 && query && !optionsLoading && (jsxRuntime.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 ", jsxRuntime.jsx("span", { className: "font-semibold", children: query })] })), allowAdd && (jsxRuntime.jsx(react.ComboboxOption, { value: query, className: "group rounded-lg py-1.5 px-3 cursor-pointer text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700", onClick: () => void handleAddAndSelect(query), children: jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsx(SvgCirclePlus, { className: "size-4.5" }), jsxRuntime.jsxs(TextContent, { children: [addNewText, " \"", query, "\""] })] }) }))] })) })] }), showErrorMessage && error && (jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx(TextContent, { error: true, small: true, children: error }) }))] }));
|
|
1303
1323
|
});
|
|
1304
1324
|
|
|
1305
|
-
const Tabs = ({ tabs, defaultIndex = 0, variant = 'underline', className = '', listClass = '',
|
|
1306
|
-
|
|
1325
|
+
const Tabs = ({ tabs, defaultIndex = 0, selectedIndex, onChange, variant = 'underline', orientation = 'horizontal', stretch = false, className = '', listClass = '', panelsClass = '', }) => {
|
|
1326
|
+
const isVertical = orientation === 'vertical';
|
|
1327
|
+
return (jsxRuntime.jsxs(react.TabGroup, { defaultIndex: defaultIndex, selectedIndex: selectedIndex, onChange: onChange, vertical: isVertical, className: buildClassName(isVertical && 'flex gap-4', stretch && !isVertical && 'w-full', className), children: [jsxRuntime.jsx(react.TabList, { className: buildClassName(isVertical ? 'flex flex-col gap-1' : 'flex gap-2', stretch && !isVertical && 'w-full', listClass), children: tabs.map((tab, idx) => {
|
|
1328
|
+
var _a;
|
|
1329
|
+
return (jsxRuntime.jsx(react.Tab, { disabled: tab.disabled, as: React.Fragment, children: ({ selected }) => (jsxRuntime.jsxs("button", { disabled: tab.disabled, className: buildTabClass({
|
|
1330
|
+
selected,
|
|
1331
|
+
variant,
|
|
1332
|
+
stretch,
|
|
1333
|
+
isVertical,
|
|
1334
|
+
tabClass: tab.tabClass,
|
|
1335
|
+
}), children: [tab.icon && jsxRuntime.jsx("span", { className: "inline-flex shrink-0", children: tab.icon }), tab.label] })) }, (_a = tab.id) !== null && _a !== void 0 ? _a : idx));
|
|
1336
|
+
}) }), jsxRuntime.jsx(react.TabPanels, { className: buildClassName(isVertical ? 'flex-1' : 'mt-3', panelsClass), children: tabs.map((tab, idx) => {
|
|
1337
|
+
var _a;
|
|
1338
|
+
return (jsxRuntime.jsx(react.TabPanel, { className: tab.panelClass !== undefined
|
|
1339
|
+
? tab.panelClass
|
|
1340
|
+
: 'rounded-lg bg-white dark:bg-gray-800 p-4 shadow-sm', children: tab.content }, (_a = tab.id) !== null && _a !== void 0 ? _a : idx));
|
|
1341
|
+
}) })] }));
|
|
1307
1342
|
};
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
function buildTabClass({ selected, variant, }) {
|
|
1343
|
+
function buildTabClass({ selected, variant, stretch, isVertical, tabClass, }) {
|
|
1344
|
+
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');
|
|
1345
|
+
let variantClass;
|
|
1312
1346
|
switch (variant) {
|
|
1313
|
-
/**
|
|
1314
|
-
* UNDERLINE (Default)
|
|
1315
|
-
*/
|
|
1316
1347
|
case 'underline':
|
|
1317
|
-
|
|
1318
|
-
'
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1348
|
+
variantClass = isVertical
|
|
1349
|
+
? buildClassName('px-3 py-2 w-full text-left border-l-2', selected
|
|
1350
|
+
? 'border-[var(--ui-primary)] text-gray-900 dark:text-white'
|
|
1351
|
+
: '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')
|
|
1352
|
+
: buildClassName('px-3 py-1.5 relative text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white', selected &&
|
|
1353
|
+
'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)]');
|
|
1354
|
+
break;
|
|
1322
1355
|
case 'segmented':
|
|
1323
|
-
|
|
1356
|
+
variantClass = buildClassName('px-3 py-1.5 rounded-lg', selected
|
|
1324
1357
|
? 'bg-[var(--ui-primary)] text-white'
|
|
1325
1358
|
: 'text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700');
|
|
1326
|
-
|
|
1327
|
-
* SOLID
|
|
1328
|
-
*/
|
|
1359
|
+
break;
|
|
1329
1360
|
case 'solid':
|
|
1330
|
-
|
|
1361
|
+
variantClass = buildClassName('px-3 py-1.5 rounded-md', selected
|
|
1331
1362
|
? 'bg-gray-900 dark:bg-gray-700 text-white'
|
|
1332
1363
|
: 'bg-gray-200 dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-300 dark:hover:bg-gray-700');
|
|
1364
|
+
break;
|
|
1333
1365
|
}
|
|
1366
|
+
return buildClassName(base, variantClass, tabClass);
|
|
1334
1367
|
}
|
|
1335
1368
|
|
|
1336
1369
|
const Textarea = React.forwardRef((props, ref) => {
|
|
@@ -1435,33 +1468,61 @@ const ToastProvider = ({ children, placement = 'top-center' }) => {
|
|
|
1435
1468
|
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))) })] }));
|
|
1436
1469
|
};
|
|
1437
1470
|
|
|
1438
|
-
const
|
|
1439
|
-
|
|
1471
|
+
const DENSITY_CELL_CLASS$1 = {
|
|
1472
|
+
compact: 'px-2 py-1.5',
|
|
1473
|
+
default: 'px-3 py-3',
|
|
1474
|
+
spacious: 'px-4 py-4',
|
|
1475
|
+
};
|
|
1476
|
+
const VirtualizedRow = ({ index, style, data, }) => {
|
|
1477
|
+
const { items, columns, gridTemplate, onRowClick, rowClass, density = 'default', striped } = data;
|
|
1440
1478
|
const item = items[index];
|
|
1441
|
-
|
|
1479
|
+
const isStriped = striped && index % 2 === 1;
|
|
1480
|
+
const resolvedRowClass = !rowClass
|
|
1481
|
+
? ''
|
|
1482
|
+
: typeof rowClass === 'function'
|
|
1483
|
+
? rowClass(item, index)
|
|
1484
|
+
: rowClass;
|
|
1485
|
+
const getCellClass = (col) => {
|
|
1486
|
+
if (!col.cellClass)
|
|
1487
|
+
return '';
|
|
1488
|
+
if (typeof col.cellClass === 'function')
|
|
1489
|
+
return col.cellClass(item);
|
|
1490
|
+
return col.cellClass;
|
|
1491
|
+
};
|
|
1492
|
+
const densityCellClass = DENSITY_CELL_CLASS$1[density];
|
|
1493
|
+
return (jsxRuntime.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: {
|
|
1442
1494
|
...style,
|
|
1443
1495
|
display: 'grid',
|
|
1444
1496
|
gridTemplateColumns: gridTemplate,
|
|
1445
1497
|
minWidth: 'fit-content',
|
|
1446
|
-
}, children: columns.map((col) => {
|
|
1498
|
+
}, onClick: onRowClick ? () => onRowClick(item, index) : undefined, children: columns.map((col) => {
|
|
1447
1499
|
var _a;
|
|
1448
1500
|
const content = col.render ? col.render(item) : ((_a = get(item, col.name)) !== null && _a !== void 0 ? _a : 'Not set');
|
|
1449
|
-
return (jsxRuntime.jsx("div", { role: "cell", className: buildClassName('
|
|
1501
|
+
return (jsxRuntime.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' &&
|
|
1450
1502
|
'sticky left-0 z-10 bg-white dark:bg-gray-900 shadow-[2px_0_4px_rgba(0,0,0,0.05)]', col.sticky === 'right' &&
|
|
1451
|
-
'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));
|
|
1503
|
+
'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));
|
|
1452
1504
|
}) }));
|
|
1453
1505
|
};
|
|
1454
1506
|
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1507
|
+
const DENSITY_CELL_CLASS = {
|
|
1508
|
+
compact: 'px-2 py-1.5',
|
|
1509
|
+
default: 'px-3 py-3',
|
|
1510
|
+
spacious: 'px-4 py-4',
|
|
1511
|
+
};
|
|
1512
|
+
const columnWidth = (col) => {
|
|
1513
|
+
if (!col.width)
|
|
1514
|
+
return '1fr';
|
|
1515
|
+
if (typeof col.width === 'number')
|
|
1516
|
+
return `${col.width}px`;
|
|
1517
|
+
return col.width;
|
|
1518
|
+
};
|
|
1519
|
+
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, }) {
|
|
1460
1520
|
const scrollRef = React.useRef(null);
|
|
1461
1521
|
const outerRef = React.useRef(null);
|
|
1462
1522
|
const headerRef = React.useRef(null);
|
|
1463
|
-
const gridTemplate =
|
|
1464
|
-
|
|
1523
|
+
const gridTemplate = React.useMemo(() => columns.map((col) => columnWidth(col)).join(' '), [columns]);
|
|
1524
|
+
const densityCellClass = DENSITY_CELL_CLASS[density];
|
|
1525
|
+
const skeletonRows = Math.max(1, Math.floor(maxHeight / rowHeight));
|
|
1465
1526
|
React.useEffect(() => {
|
|
1466
1527
|
const el = outerRef.current;
|
|
1467
1528
|
if (!el)
|
|
@@ -1477,35 +1538,31 @@ const VirtualizedDataTable = ({ items, columns, sorting = {}, onSort, loading =
|
|
|
1477
1538
|
if (column.sortable)
|
|
1478
1539
|
onSort === null || onSort === void 0 ? void 0 : onSort(column);
|
|
1479
1540
|
}, [onSort]);
|
|
1541
|
+
const itemData = React.useMemo(() => ({ items, columns, gridTemplate, onRowClick, rowClass, density, striped }), [items, columns, gridTemplate, onRowClick, rowClass, density, striped]);
|
|
1480
1542
|
/* -------------------------------------------------------
|
|
1481
|
-
*
|
|
1543
|
+
* Header
|
|
1482
1544
|
* ------------------------------------------------------- */
|
|
1483
|
-
const Header = (jsxRuntime.jsx("div", { ref: headerRef, className: "overflow-hidden w-full", children: jsxRuntime.jsx("div", { className: "grid bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700", style: {
|
|
1484
|
-
gridTemplateColumns: gridTemplate,
|
|
1485
|
-
minWidth: 'fit-content',
|
|
1486
|
-
}, children: columns.map((col) => (jsxRuntime.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' &&
|
|
1545
|
+
const Header = (jsxRuntime.jsx("div", { ref: headerRef, className: "overflow-hidden w-full", children: jsxRuntime.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) => (jsxRuntime.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' &&
|
|
1487
1546
|
'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' &&
|
|
1488
|
-
'sticky right-0 z-20 bg-gray-50 dark:bg-gray-800 shadow-[-2px_0_4px_rgba(0,0,0,0.05)]'), children: jsxRuntime.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))) }) }));
|
|
1547
|
+
'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: jsxRuntime.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))) }) }));
|
|
1548
|
+
const wrapperCls = buildClassName('rounded-lg border border-gray-200 dark:border-gray-800 overflow-hidden', wrapperClass);
|
|
1489
1549
|
/* -------------------------------------------------------
|
|
1490
|
-
*
|
|
1550
|
+
* Loading state
|
|
1491
1551
|
* ------------------------------------------------------- */
|
|
1492
1552
|
if (loading) {
|
|
1493
|
-
return (jsxRuntime.jsx("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: jsxRuntime.jsxs("div", { className:
|
|
1494
|
-
gridTemplateColumns: gridTemplate,
|
|
1495
|
-
height: rowHeight,
|
|
1496
|
-
}, children: columns.map((col) => (jsxRuntime.jsx("div", { className: "px-3 py-3", children: jsxRuntime.jsx("div", { className: "h-4 w-3/4 bg-gray-300 dark:bg-gray-700 rounded" }) }, col.name))) }, idx))) })] }) }));
|
|
1553
|
+
return (jsxRuntime.jsx("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: jsxRuntime.jsxs("div", { className: wrapperCls, children: [Header, jsxRuntime.jsx("div", { style: { height: maxHeight }, className: "flex flex-col", children: Array.from({ length: skeletonRows }).map((_, idx) => (jsxRuntime.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) => (jsxRuntime.jsx("div", { className: densityCellClass, children: jsxRuntime.jsx("div", { className: "h-4 w-3/4 bg-gray-300 dark:bg-gray-700 rounded" }) }, col.name))) }, idx))) })] }) }));
|
|
1497
1554
|
}
|
|
1498
1555
|
/* -------------------------------------------------------
|
|
1499
|
-
*
|
|
1556
|
+
* Empty state
|
|
1500
1557
|
* ------------------------------------------------------- */
|
|
1501
|
-
if (
|
|
1502
|
-
return (jsxRuntime.jsx("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: jsxRuntime.jsxs("div", { className:
|
|
1558
|
+
if (items.length === 0) {
|
|
1559
|
+
return (jsxRuntime.jsx("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: jsxRuntime.jsxs("div", { className: wrapperCls, children: [Header, jsxRuntime.jsx("div", { className: "flex items-center justify-center text-gray-500 dark:text-gray-300", style: { height: maxHeight }, children: emptyMessage })] }) }));
|
|
1503
1560
|
}
|
|
1504
1561
|
/* -------------------------------------------------------
|
|
1505
|
-
*
|
|
1562
|
+
* Virtualized body
|
|
1506
1563
|
* ------------------------------------------------------- */
|
|
1507
|
-
return (jsxRuntime.jsx("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: jsxRuntime.jsxs("div", { className:
|
|
1508
|
-
}
|
|
1564
|
+
return (jsxRuntime.jsx("div", { className: buildClassName('w-full rounded-lg relative', containerClass), children: jsxRuntime.jsxs("div", { className: wrapperCls, children: [Header, jsxRuntime.jsx(reactWindow.FixedSizeList, { ref: scrollRef, outerRef: outerRef, height: maxHeight, width: "100%", itemCount: items.length, itemSize: rowHeight, itemData: itemData, overscanCount: overscanCount, children: VirtualizedRow })] }) }));
|
|
1565
|
+
}
|
|
1509
1566
|
|
|
1510
1567
|
const useIsMobile = () => {
|
|
1511
1568
|
const [isMobile, setIsMobile] = React.useState(false);
|