@papernote/ui 2.0.3 → 2.0.5
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/components/DataTable.d.ts +29 -11
- package/dist/components/DataTable.d.ts.map +1 -1
- package/dist/components/FilterBar.d.ts +1 -1
- package/dist/components/FilterBar.d.ts.map +1 -1
- package/dist/index.d.ts +28 -10
- package/dist/index.esm.js +433 -270
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +433 -270
- package/dist/index.js.map +1 -1
- package/dist/styles.css +8 -0
- package/package.json +1 -1
- package/src/components/DataTable.tsx +993 -647
- package/src/components/FilterBar.tsx +130 -53
package/dist/index.js
CHANGED
|
@@ -2765,7 +2765,7 @@ function FormControl({ label, required = false, error, helperText, children, cla
|
|
|
2765
2765
|
return (jsxRuntime.jsxs("div", { className: `${className}`, children: [label && (jsxRuntime.jsxs("label", { htmlFor: htmlFor, className: "block text-sm font-medium text-ink-700 mb-1", children: [label, required && jsxRuntime.jsx("span", { className: "text-error-500 ml-1", children: "*" })] })), jsxRuntime.jsx("div", { children: children }), (error || helperText) && (jsxRuntime.jsx("p", { className: `mt-1 text-xs ${error ? 'text-error-600' : 'text-ink-500'}`, children: error || helperText }))] }));
|
|
2766
2766
|
}
|
|
2767
2767
|
|
|
2768
|
-
function FilterBar({ filters, values, onChange, className =
|
|
2768
|
+
function FilterBar({ filters, values, onChange, className = "", onClear, showClearButton = false, }) {
|
|
2769
2769
|
const handleFilterChange = (key, value) => {
|
|
2770
2770
|
onChange({
|
|
2771
2771
|
...values,
|
|
@@ -2779,16 +2779,19 @@ function FilterBar({ filters, values, onChange, className = '', onClear, showCle
|
|
|
2779
2779
|
else {
|
|
2780
2780
|
// Default clear: set all values to null/empty
|
|
2781
2781
|
const clearedValues = {};
|
|
2782
|
-
filters.forEach(filter => {
|
|
2783
|
-
if (filter.type ===
|
|
2784
|
-
clearedValues[filter.key] =
|
|
2782
|
+
filters.forEach((filter) => {
|
|
2783
|
+
if (filter.type === "text" || filter.type === "search") {
|
|
2784
|
+
clearedValues[filter.key] = "";
|
|
2785
2785
|
}
|
|
2786
|
-
else if (filter.type ===
|
|
2786
|
+
else if (filter.type === "dateRange") {
|
|
2787
2787
|
clearedValues[filter.key] = { from: undefined, to: undefined };
|
|
2788
2788
|
}
|
|
2789
|
-
else if (filter.type ===
|
|
2789
|
+
else if (filter.type === "multiSelect") {
|
|
2790
2790
|
clearedValues[filter.key] = [];
|
|
2791
2791
|
}
|
|
2792
|
+
else if (filter.type === "switch") {
|
|
2793
|
+
clearedValues[filter.key] = false;
|
|
2794
|
+
}
|
|
2792
2795
|
else {
|
|
2793
2796
|
clearedValues[filter.key] = null;
|
|
2794
2797
|
}
|
|
@@ -2799,51 +2802,70 @@ function FilterBar({ filters, values, onChange, className = '', onClear, showCle
|
|
|
2799
2802
|
const renderFilter = (filter) => {
|
|
2800
2803
|
const value = values[filter.key];
|
|
2801
2804
|
switch (filter.type) {
|
|
2802
|
-
case
|
|
2803
|
-
return (jsxRuntime.jsx(Input, { type: "text", placeholder: filter.placeholder || `Filter by ${filter.label}`, value: value ||
|
|
2804
|
-
case
|
|
2805
|
+
case "text":
|
|
2806
|
+
return (jsxRuntime.jsx(Input, { type: "text", placeholder: filter.placeholder || `Filter by ${filter.label}`, value: value || "", onChange: (e) => handleFilterChange(filter.key, e.target.value) }));
|
|
2807
|
+
case "select": {
|
|
2805
2808
|
const selectOptions = [
|
|
2806
|
-
{ value:
|
|
2807
|
-
...(filter.options?.map(opt => ({
|
|
2809
|
+
{ value: "", label: `All ${filter.label}` },
|
|
2810
|
+
...(filter.options?.map((opt) => ({
|
|
2808
2811
|
value: String(opt.value),
|
|
2809
2812
|
label: opt.label,
|
|
2810
2813
|
})) || []),
|
|
2811
2814
|
];
|
|
2812
|
-
return (jsxRuntime.jsx(Select, { options: selectOptions, value: String(value ||
|
|
2815
|
+
return (jsxRuntime.jsx(Select, { options: selectOptions, value: String(value || ""), onChange: (newValue) => handleFilterChange(filter.key, newValue || null) }));
|
|
2813
2816
|
}
|
|
2814
|
-
case
|
|
2815
|
-
return (jsxRuntime.jsx("input", { type: "date", value: value ||
|
|
2816
|
-
case
|
|
2817
|
-
return (jsxRuntime.jsx("input", { type: "number", placeholder: filter.placeholder || `Filter by ${filter.label}`, value: value !== null && value !== undefined ? String(value) :
|
|
2818
|
-
case
|
|
2817
|
+
case "date":
|
|
2818
|
+
return (jsxRuntime.jsx("input", { type: "date", value: value || "", onChange: (e) => handleFilterChange(filter.key, e.target.value), className: "input" }));
|
|
2819
|
+
case "number":
|
|
2820
|
+
return (jsxRuntime.jsx("input", { type: "number", placeholder: filter.placeholder || `Filter by ${filter.label}`, value: value !== null && value !== undefined ? String(value) : "", onChange: (e) => handleFilterChange(filter.key, e.target.value ? Number(e.target.value) : null), className: "input" }));
|
|
2821
|
+
case "boolean": {
|
|
2819
2822
|
const boolOptions = [
|
|
2820
|
-
{ value:
|
|
2821
|
-
{ value:
|
|
2822
|
-
{ value:
|
|
2823
|
+
{ value: "", label: "All" },
|
|
2824
|
+
{ value: "true", label: "Yes" },
|
|
2825
|
+
{ value: "false", label: "No" },
|
|
2823
2826
|
];
|
|
2824
|
-
return (jsxRuntime.jsx(Select, { options: boolOptions, value: value === null || value === undefined ?
|
|
2827
|
+
return (jsxRuntime.jsx(Select, { options: boolOptions, value: value === null || value === undefined ? "" : String(value), onChange: (newValue) => handleFilterChange(filter.key, newValue === "" ? null : newValue === "true") }));
|
|
2825
2828
|
}
|
|
2826
|
-
case
|
|
2827
|
-
return (jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none", children: jsxRuntime.jsx(lucideReact.Search, { className: "h-4 w-4 text-ink-400" }) }), jsxRuntime.jsx("input", { type: "text", placeholder: filter.placeholder || `Search ${filter.label}...`, value: value ||
|
|
2828
|
-
case
|
|
2829
|
+
case "search":
|
|
2830
|
+
return (jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none", children: jsxRuntime.jsx(lucideReact.Search, { className: "h-4 w-4 text-ink-400" }) }), jsxRuntime.jsx("input", { type: "text", placeholder: filter.placeholder || `Search ${filter.label}...`, value: value || "", onChange: (e) => handleFilterChange(filter.key, e.target.value), className: "input pl-9" })] }));
|
|
2831
|
+
case "dateRange": {
|
|
2829
2832
|
const rangeValue = value || {};
|
|
2830
|
-
return (jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsx("input", { type: "date", value: rangeValue.from ||
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
+
return (jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsx("input", { type: "date", value: rangeValue.from || "", onChange: (e) => handleFilterChange(filter.key, {
|
|
2834
|
+
...rangeValue,
|
|
2835
|
+
from: e.target.value || undefined,
|
|
2836
|
+
}), className: "input text-sm", "aria-label": `${filter.label} from` }), jsxRuntime.jsx("span", { className: "text-ink-400 text-xs", children: "to" }), jsxRuntime.jsx("input", { type: "date", value: rangeValue.to || "", onChange: (e) => handleFilterChange(filter.key, {
|
|
2837
|
+
...rangeValue,
|
|
2838
|
+
to: e.target.value || undefined,
|
|
2839
|
+
}), className: "input text-sm", "aria-label": `${filter.label} to` })] }));
|
|
2840
|
+
}
|
|
2841
|
+
case "toggle": {
|
|
2833
2842
|
const toggleOptions = [
|
|
2834
|
-
{ value:
|
|
2835
|
-
{ value:
|
|
2836
|
-
{ value:
|
|
2843
|
+
{ value: "", label: "All" },
|
|
2844
|
+
{ value: "true", label: "Yes" },
|
|
2845
|
+
{ value: "false", label: "No" },
|
|
2837
2846
|
];
|
|
2838
|
-
const currentVal = value === null || value === undefined ?
|
|
2839
|
-
return (jsxRuntime.jsx("div", { className: "flex rounded-lg border border-paper-300 overflow-hidden", role: "group", children: toggleOptions.map((opt) => (jsxRuntime.jsx("button", { type: "button", onClick: () => handleFilterChange(filter.key, opt.value ===
|
|
2840
|
-
?
|
|
2841
|
-
:
|
|
2842
|
-
}
|
|
2843
|
-
case
|
|
2847
|
+
const currentVal = value === null || value === undefined ? "" : String(value);
|
|
2848
|
+
return (jsxRuntime.jsx("div", { className: "flex rounded-lg border border-paper-300 overflow-hidden", role: "group", children: toggleOptions.map((opt) => (jsxRuntime.jsx("button", { type: "button", onClick: () => handleFilterChange(filter.key, opt.value === "" ? null : opt.value === "true"), className: `px-3 py-1.5 text-xs font-medium transition-colors ${currentVal === opt.value
|
|
2849
|
+
? "bg-accent-500 text-white"
|
|
2850
|
+
: "bg-white text-ink-600 hover:bg-paper-50"} ${opt.value !== "" ? "border-l border-paper-300" : ""}`, children: opt.label }, opt.value))) }));
|
|
2851
|
+
}
|
|
2852
|
+
case "switch": {
|
|
2853
|
+
// Single binary toggle — use when the filter is naturally on/off
|
|
2854
|
+
// (e.g. "Mine only", "Archived"), unlike `boolean` / `toggle` which
|
|
2855
|
+
// present an All/Yes/No tri-state. Stored value is a plain boolean.
|
|
2856
|
+
const checked = value === true;
|
|
2857
|
+
return (jsxRuntime.jsxs("button", { type: "button", role: "switch", "aria-checked": checked, onClick: () => handleFilterChange(filter.key, !checked), className: `relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-accent-400 focus:ring-offset-2 ${checked ? "bg-accent-500" : "bg-paper-300"}`, children: [jsxRuntime.jsx("span", { className: `inline-block h-4 w-4 transform rounded-full bg-white shadow transition-transform ${checked ? "translate-x-6" : "translate-x-1"}` }), jsxRuntime.jsx("span", { className: "sr-only", children: filter.label })] }));
|
|
2858
|
+
}
|
|
2859
|
+
case "multiSelect": {
|
|
2844
2860
|
const selectedValues = Array.isArray(value) ? value : [];
|
|
2845
2861
|
const msOptions = filter.options || [];
|
|
2846
|
-
return (jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsx(Select, { options: [
|
|
2862
|
+
return (jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsx(Select, { options: [
|
|
2863
|
+
{ value: "", label: `All ${filter.label}` },
|
|
2864
|
+
...msOptions.map((o) => ({
|
|
2865
|
+
value: String(o.value),
|
|
2866
|
+
label: o.label,
|
|
2867
|
+
})),
|
|
2868
|
+
], value: "", onChange: (newValue) => {
|
|
2847
2869
|
if (!newValue) {
|
|
2848
2870
|
handleFilterChange(filter.key, []);
|
|
2849
2871
|
}
|
|
@@ -2851,8 +2873,8 @@ function FilterBar({ filters, values, onChange, className = '', onClear, showCle
|
|
|
2851
2873
|
handleFilterChange(filter.key, [...selectedValues, newValue]);
|
|
2852
2874
|
}
|
|
2853
2875
|
} }), selectedValues.length > 0 && (jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1 mt-1", children: selectedValues.map((sv) => {
|
|
2854
|
-
const opt = msOptions.find(o => String(o.value) === sv);
|
|
2855
|
-
return (jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs bg-accent-100 text-accent-700 rounded-full", children: [opt?.label || sv, jsxRuntime.jsx("button", { type: "button", onClick: () => handleFilterChange(filter.key, selectedValues.filter(v => v !== sv)), className: "hover:text-accent-900", children: jsxRuntime.jsx(lucideReact.X, { className: "h-3 w-3" }) })] }, sv));
|
|
2876
|
+
const opt = msOptions.find((o) => String(o.value) === sv);
|
|
2877
|
+
return (jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs bg-accent-100 text-accent-700 rounded-full", children: [opt?.label || sv, jsxRuntime.jsx("button", { type: "button", onClick: () => handleFilterChange(filter.key, selectedValues.filter((v) => v !== sv)), className: "hover:text-accent-900", children: jsxRuntime.jsx(lucideReact.X, { className: "h-3 w-3" }) })] }, sv));
|
|
2856
2878
|
}) }))] }));
|
|
2857
2879
|
}
|
|
2858
2880
|
default:
|
|
@@ -14804,25 +14826,27 @@ function ActionMenu({ actions, item, }) {
|
|
|
14804
14826
|
// Click outside handler
|
|
14805
14827
|
React.useEffect(() => {
|
|
14806
14828
|
const handleClickOutside = (event) => {
|
|
14807
|
-
if (menuRef.current &&
|
|
14808
|
-
|
|
14829
|
+
if (menuRef.current &&
|
|
14830
|
+
!menuRef.current.contains(event.target) &&
|
|
14831
|
+
buttonRef.current &&
|
|
14832
|
+
!buttonRef.current.contains(event.target)) {
|
|
14809
14833
|
setIsOpen(false);
|
|
14810
14834
|
}
|
|
14811
14835
|
};
|
|
14812
14836
|
if (isOpen) {
|
|
14813
|
-
document.addEventListener(
|
|
14837
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
14814
14838
|
}
|
|
14815
14839
|
return () => {
|
|
14816
|
-
document.removeEventListener(
|
|
14840
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
14817
14841
|
};
|
|
14818
14842
|
}, [isOpen]);
|
|
14819
|
-
const visibleActions = actions.filter(action => !action.show || action.show(item));
|
|
14843
|
+
const visibleActions = actions.filter((action) => !action.show || action.show(item));
|
|
14820
14844
|
if (visibleActions.length === 0)
|
|
14821
14845
|
return null;
|
|
14822
14846
|
const dropdownContent = isOpen && (jsxRuntime.jsx("div", { ref: menuRef, className: "fixed w-56 bg-white rounded-lg shadow-lg border border-paper-300 py-1", style: {
|
|
14823
14847
|
zIndex: 999999,
|
|
14824
14848
|
top: `${position.top}px`,
|
|
14825
|
-
left: `${position.left}px
|
|
14849
|
+
left: `${position.left}px`,
|
|
14826
14850
|
}, children: visibleActions.map((action, idx) => {
|
|
14827
14851
|
let iconElement = null;
|
|
14828
14852
|
if (action.icon) {
|
|
@@ -14830,7 +14854,9 @@ function ActionMenu({ actions, item, }) {
|
|
|
14830
14854
|
iconElement = action.icon;
|
|
14831
14855
|
}
|
|
14832
14856
|
else {
|
|
14833
|
-
iconElement = React.createElement(action.icon, {
|
|
14857
|
+
iconElement = React.createElement(action.icon, {
|
|
14858
|
+
className: "h-4 w-4 flex-shrink-0",
|
|
14859
|
+
});
|
|
14834
14860
|
}
|
|
14835
14861
|
}
|
|
14836
14862
|
return (jsxRuntime.jsxs("button", { type: "button", onClick: async (e) => {
|
|
@@ -14840,12 +14866,12 @@ function ActionMenu({ actions, item, }) {
|
|
|
14840
14866
|
await action.onClick(item);
|
|
14841
14867
|
}
|
|
14842
14868
|
catch (error) {
|
|
14843
|
-
console.error(
|
|
14869
|
+
console.error("DataTable action error:", error);
|
|
14844
14870
|
}
|
|
14845
14871
|
setIsOpen(false);
|
|
14846
|
-
}, className: `w-full flex items-center gap-3 px-4 py-2.5 text-sm transition-colors ${action.variant ===
|
|
14847
|
-
?
|
|
14848
|
-
:
|
|
14872
|
+
}, className: `w-full flex items-center gap-3 px-4 py-2.5 text-sm transition-colors ${action.variant === "danger"
|
|
14873
|
+
? "text-error-600 hover:bg-error-50 hover:text-error-700"
|
|
14874
|
+
: "text-ink-700 hover:bg-paper-50"}`, title: action.tooltip, children: [iconElement, jsxRuntime.jsx("span", { className: "flex-1 text-left", children: action.label })] }, idx));
|
|
14849
14875
|
}) }));
|
|
14850
14876
|
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("button", { ref: buttonRef, onClick: (e) => {
|
|
14851
14877
|
e.stopPropagation();
|
|
@@ -14862,13 +14888,20 @@ function getColumnStyle(column, dynamicWidth) {
|
|
|
14862
14888
|
style.width = `${dynamicWidth}px`;
|
|
14863
14889
|
}
|
|
14864
14890
|
else if (column.width !== undefined) {
|
|
14865
|
-
style.width =
|
|
14891
|
+
style.width =
|
|
14892
|
+
typeof column.width === "number" ? `${column.width}px` : column.width;
|
|
14866
14893
|
}
|
|
14867
14894
|
if (column.minWidth !== undefined) {
|
|
14868
|
-
style.minWidth =
|
|
14895
|
+
style.minWidth =
|
|
14896
|
+
typeof column.minWidth === "number"
|
|
14897
|
+
? `${column.minWidth}px`
|
|
14898
|
+
: column.minWidth;
|
|
14869
14899
|
}
|
|
14870
14900
|
if (column.maxWidth !== undefined) {
|
|
14871
|
-
style.maxWidth =
|
|
14901
|
+
style.maxWidth =
|
|
14902
|
+
typeof column.maxWidth === "number"
|
|
14903
|
+
? `${column.maxWidth}px`
|
|
14904
|
+
: column.maxWidth;
|
|
14872
14905
|
}
|
|
14873
14906
|
if (column.flex !== undefined) {
|
|
14874
14907
|
style.flexGrow = column.flex;
|
|
@@ -14927,13 +14960,13 @@ function getColumnStyle(column, dynamicWidth) {
|
|
|
14927
14960
|
* />
|
|
14928
14961
|
* ```
|
|
14929
14962
|
*/
|
|
14930
|
-
function DataTable({ data, columns, loading = false, error = null, emptyMessage =
|
|
14963
|
+
function DataTable({ data, columns, loading = false, error = null, emptyMessage = "No data available", loadingRows = 5, className = "", onSortChange, currentSort = null, onEdit, onDelete, actions = [], enableContextMenu = true, onRowClick, onRowDoubleClick, selectable = false, selectedRows: externalSelectedRows, onRowSelect, keyExtractor, expandable = false, expandedRows: externalExpandedRows, renderExpandedRow, expandedRowConfig, showExpandChevron = false,
|
|
14931
14964
|
// Visual customization props
|
|
14932
|
-
striped = false, stripedColor, density =
|
|
14965
|
+
striped = false, stripedColor, density = "normal", rowClassName, rowHighlight, highlightedRowId, highlightedRows = [], highlightDuration = 2000, bordered = false, borderColor = "border-paper-200", disableHover = false, hiddenColumns = [], headerClassName = "", renderEmptyState: customRenderEmptyState, resizable = false, onColumnResize, reorderable = false, onColumnReorder, virtualized = false, virtualHeight = "600px", virtualRowHeight = 60,
|
|
14933
14966
|
// Pagination props
|
|
14934
14967
|
paginated = false, currentPage = 1, pageSize = 10, totalItems, onPageChange, pageSizeOptions = [10, 25, 50, 100], onPageSizeChange, showPageSizeSelector = true,
|
|
14935
14968
|
// Mobile view props
|
|
14936
|
-
mobileView =
|
|
14969
|
+
mobileView = "auto", cardConfig, cardGap = "md", cardClassName, }) {
|
|
14937
14970
|
// Mobile detection for auto mode
|
|
14938
14971
|
const isMobileViewport = useIsMobile();
|
|
14939
14972
|
// Column resizing state
|
|
@@ -14952,7 +14985,7 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
14952
14985
|
const [hoveredRowKey, setHoveredRowKey] = React.useState(null);
|
|
14953
14986
|
// Keyboard navigation state
|
|
14954
14987
|
const [focusedCell, setFocusedCell] = React.useState(null);
|
|
14955
|
-
const [announcement, setAnnouncement] = React.useState(
|
|
14988
|
+
const [announcement, setAnnouncement] = React.useState("");
|
|
14956
14989
|
const tableBodyRef = React.useRef(null);
|
|
14957
14990
|
// Temporary row highlight state (for flash animation)
|
|
14958
14991
|
const [flashingRows, setFlashingRows] = React.useState(new Set());
|
|
@@ -14964,18 +14997,18 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
14964
14997
|
item: null,
|
|
14965
14998
|
});
|
|
14966
14999
|
// Filter columns based on hiddenColumns
|
|
14967
|
-
const baseVisibleColumns = columns.filter(col => !hiddenColumns.includes(String(col.key)));
|
|
15000
|
+
const baseVisibleColumns = columns.filter((col) => !hiddenColumns.includes(String(col.key)));
|
|
14968
15001
|
// Initialize column order on mount or when columns change
|
|
14969
15002
|
React.useEffect(() => {
|
|
14970
15003
|
if (columnOrder.length === 0) {
|
|
14971
|
-
setColumnOrder(baseVisibleColumns.map(col => String(col.key)));
|
|
15004
|
+
setColumnOrder(baseVisibleColumns.map((col) => String(col.key)));
|
|
14972
15005
|
}
|
|
14973
15006
|
}, [baseVisibleColumns, columnOrder.length]);
|
|
14974
15007
|
// Handle temporary row highlighting (flash animation)
|
|
14975
15008
|
React.useEffect(() => {
|
|
14976
15009
|
if (highlightedRows.length > 0) {
|
|
14977
15010
|
// Add new highlighted rows to flashing set
|
|
14978
|
-
const newFlashingRows = new Set(highlightedRows.map(id => String(id)));
|
|
15011
|
+
const newFlashingRows = new Set(highlightedRows.map((id) => String(id)));
|
|
14979
15012
|
setFlashingRows(newFlashingRows);
|
|
14980
15013
|
// Clear any existing timeout
|
|
14981
15014
|
if (flashTimeoutRef.current) {
|
|
@@ -14995,25 +15028,25 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
14995
15028
|
// Apply column order
|
|
14996
15029
|
const visibleColumns = reorderable && columnOrder.length > 0
|
|
14997
15030
|
? columnOrder
|
|
14998
|
-
.map(key => baseVisibleColumns.find(col => String(col.key) === key))
|
|
15031
|
+
.map((key) => baseVisibleColumns.find((col) => String(col.key) === key))
|
|
14999
15032
|
.filter((col) => col !== undefined)
|
|
15000
15033
|
: baseVisibleColumns;
|
|
15001
15034
|
// Density classes
|
|
15002
15035
|
const densityClasses = {
|
|
15003
15036
|
compact: {
|
|
15004
|
-
cell:
|
|
15005
|
-
text:
|
|
15006
|
-
header:
|
|
15037
|
+
cell: "px-3 py-1",
|
|
15038
|
+
text: "text-xs",
|
|
15039
|
+
header: "px-3 py-2",
|
|
15007
15040
|
},
|
|
15008
15041
|
normal: {
|
|
15009
|
-
cell:
|
|
15010
|
-
text:
|
|
15011
|
-
header:
|
|
15042
|
+
cell: "px-6 py-1.5",
|
|
15043
|
+
text: "text-sm",
|
|
15044
|
+
header: "px-6 py-3",
|
|
15012
15045
|
},
|
|
15013
15046
|
comfortable: {
|
|
15014
|
-
cell:
|
|
15015
|
-
text:
|
|
15016
|
-
header:
|
|
15047
|
+
cell: "px-6 py-3",
|
|
15048
|
+
text: "text-base",
|
|
15049
|
+
header: "px-6 py-4",
|
|
15017
15050
|
},
|
|
15018
15051
|
};
|
|
15019
15052
|
const currentDensity = densityClasses[density];
|
|
@@ -15021,20 +15054,25 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15021
15054
|
const getRowKey = keyExtractor || ((row) => String(row.id));
|
|
15022
15055
|
// Calculate if there are any actions (for keyboard navigation column calculation)
|
|
15023
15056
|
// This is computed early so it can be used in keyboard handlers
|
|
15024
|
-
const hasAnyActions = !!(onEdit ||
|
|
15025
|
-
|
|
15026
|
-
|
|
15057
|
+
const hasAnyActions = !!(onEdit ||
|
|
15058
|
+
onDelete ||
|
|
15059
|
+
actions.length > 0 ||
|
|
15060
|
+
expandedRowConfig?.edit ||
|
|
15061
|
+
expandedRowConfig?.details ||
|
|
15062
|
+
expandedRowConfig?.addRelated?.length ||
|
|
15063
|
+
expandedRowConfig?.manageRelated?.length);
|
|
15027
15064
|
// Get row background class based on striping and highlighting
|
|
15028
15065
|
const getRowBackgroundClass = (item, index) => {
|
|
15029
15066
|
const classes = [];
|
|
15030
15067
|
const rowKey = getRowKey(item);
|
|
15031
15068
|
// Check for temporary flash highlight (takes priority)
|
|
15032
15069
|
if (flashingRows.has(rowKey)) {
|
|
15033
|
-
classes.push(
|
|
15070
|
+
classes.push("animate-row-flash");
|
|
15034
15071
|
}
|
|
15035
15072
|
// Check for highlighted row
|
|
15036
|
-
else if (highlightedRowId !== undefined &&
|
|
15037
|
-
|
|
15073
|
+
else if (highlightedRowId !== undefined &&
|
|
15074
|
+
rowKey === String(highlightedRowId)) {
|
|
15075
|
+
classes.push("bg-accent-100");
|
|
15038
15076
|
}
|
|
15039
15077
|
// Check for custom row highlight
|
|
15040
15078
|
else if (rowHighlight) {
|
|
@@ -15046,24 +15084,27 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15046
15084
|
// Check for striping
|
|
15047
15085
|
else if (striped) {
|
|
15048
15086
|
const isOdd = index % 2 === 0; // 0-indexed, so even index = odd row
|
|
15049
|
-
const shouldStripe = striped === true
|
|
15050
|
-
|
|
15051
|
-
|
|
15052
|
-
|
|
15087
|
+
const shouldStripe = striped === true
|
|
15088
|
+
? isOdd
|
|
15089
|
+
: striped === "odd"
|
|
15090
|
+
? isOdd
|
|
15091
|
+
: striped === "even"
|
|
15092
|
+
? !isOdd
|
|
15093
|
+
: false;
|
|
15053
15094
|
if (shouldStripe) {
|
|
15054
|
-
classes.push(stripedColor ||
|
|
15095
|
+
classes.push(stripedColor || "bg-paper-50");
|
|
15055
15096
|
}
|
|
15056
15097
|
}
|
|
15057
15098
|
// Add custom row class
|
|
15058
15099
|
if (rowClassName) {
|
|
15059
|
-
if (typeof rowClassName ===
|
|
15100
|
+
if (typeof rowClassName === "string") {
|
|
15060
15101
|
classes.push(rowClassName);
|
|
15061
15102
|
}
|
|
15062
15103
|
else {
|
|
15063
15104
|
classes.push(rowClassName(item, index));
|
|
15064
15105
|
}
|
|
15065
15106
|
}
|
|
15066
|
-
return classes.join(
|
|
15107
|
+
return classes.join(" ");
|
|
15067
15108
|
};
|
|
15068
15109
|
// NEW: Expansion mode state management (for expandedRowConfig)
|
|
15069
15110
|
const [expansionState, setExpansionState] = React.useState(null);
|
|
@@ -15078,12 +15119,12 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15078
15119
|
// Column reorder handlers
|
|
15079
15120
|
const handleDragStart = (e, columnKey) => {
|
|
15080
15121
|
setDraggingColumn(columnKey);
|
|
15081
|
-
e.dataTransfer.effectAllowed =
|
|
15082
|
-
e.dataTransfer.setData(
|
|
15122
|
+
e.dataTransfer.effectAllowed = "move";
|
|
15123
|
+
e.dataTransfer.setData("text/html", columnKey);
|
|
15083
15124
|
};
|
|
15084
15125
|
const handleDragOver = (e, columnKey) => {
|
|
15085
15126
|
e.preventDefault();
|
|
15086
|
-
e.dataTransfer.dropEffect =
|
|
15127
|
+
e.dataTransfer.dropEffect = "move";
|
|
15087
15128
|
if (draggingColumn && draggingColumn !== columnKey) {
|
|
15088
15129
|
setDragOverColumn(columnKey);
|
|
15089
15130
|
}
|
|
@@ -15114,7 +15155,7 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15114
15155
|
const handleMouseMove = (e) => {
|
|
15115
15156
|
const delta = e.clientX - resizeStartX;
|
|
15116
15157
|
const newWidth = Math.max(50, resizeStartWidth + delta); // Min width 50px
|
|
15117
|
-
setColumnWidths(prev => ({
|
|
15158
|
+
setColumnWidths((prev) => ({
|
|
15118
15159
|
...prev,
|
|
15119
15160
|
[resizingColumn]: newWidth,
|
|
15120
15161
|
}));
|
|
@@ -15125,56 +15166,62 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15125
15166
|
}
|
|
15126
15167
|
setResizingColumn(null);
|
|
15127
15168
|
};
|
|
15128
|
-
document.addEventListener(
|
|
15129
|
-
document.addEventListener(
|
|
15169
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
15170
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
15130
15171
|
return () => {
|
|
15131
|
-
document.removeEventListener(
|
|
15132
|
-
document.removeEventListener(
|
|
15172
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
15173
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
15133
15174
|
};
|
|
15134
|
-
}, [
|
|
15175
|
+
}, [
|
|
15176
|
+
resizingColumn,
|
|
15177
|
+
resizeStartX,
|
|
15178
|
+
resizeStartWidth,
|
|
15179
|
+
columnWidths,
|
|
15180
|
+
onColumnResize,
|
|
15181
|
+
]);
|
|
15135
15182
|
// Build combined actions: built-in edit/delete + custom actions + expansion mode actions
|
|
15136
15183
|
const builtInActions = [];
|
|
15137
15184
|
// Legacy onEdit (still supported)
|
|
15138
15185
|
if (onEdit) {
|
|
15139
15186
|
builtInActions.push({
|
|
15140
|
-
label:
|
|
15187
|
+
label: "Edit",
|
|
15141
15188
|
icon: lucideReact.Edit,
|
|
15142
15189
|
onClick: onEdit,
|
|
15143
|
-
variant:
|
|
15144
|
-
tooltip:
|
|
15190
|
+
variant: "secondary",
|
|
15191
|
+
tooltip: "Edit item",
|
|
15145
15192
|
});
|
|
15146
15193
|
}
|
|
15147
15194
|
// NEW: Edit mode from expandedRowConfig
|
|
15148
15195
|
if (expandedRowConfig?.edit && !onEdit) {
|
|
15149
15196
|
const editConfig = expandedRowConfig.edit;
|
|
15150
15197
|
builtInActions.push({
|
|
15151
|
-
label: editConfig.menuLabel ||
|
|
15198
|
+
label: editConfig.menuLabel || "Edit",
|
|
15152
15199
|
icon: editConfig.menuIcon || lucideReact.Edit,
|
|
15153
15200
|
onClick: (item) => {
|
|
15154
15201
|
const rowKey = getRowKey(item);
|
|
15155
|
-
handleExpansionWithMode(rowKey,
|
|
15202
|
+
handleExpansionWithMode(rowKey, "edit");
|
|
15156
15203
|
},
|
|
15157
|
-
variant:
|
|
15158
|
-
tooltip:
|
|
15204
|
+
variant: "secondary",
|
|
15205
|
+
tooltip: "Edit inline",
|
|
15159
15206
|
});
|
|
15160
15207
|
}
|
|
15161
15208
|
// NEW: View details mode from expandedRowConfig
|
|
15162
15209
|
if (expandedRowConfig?.details) {
|
|
15163
15210
|
const detailsConfig = expandedRowConfig.details;
|
|
15164
15211
|
builtInActions.push({
|
|
15165
|
-
label: detailsConfig.menuLabel ||
|
|
15212
|
+
label: detailsConfig.menuLabel || "View Details",
|
|
15166
15213
|
icon: detailsConfig.menuIcon,
|
|
15167
15214
|
onClick: (item) => {
|
|
15168
15215
|
const rowKey = getRowKey(item);
|
|
15169
|
-
handleExpansionWithMode(rowKey,
|
|
15216
|
+
handleExpansionWithMode(rowKey, "details");
|
|
15170
15217
|
},
|
|
15171
|
-
variant:
|
|
15172
|
-
tooltip:
|
|
15218
|
+
variant: "ghost",
|
|
15219
|
+
tooltip: "View details",
|
|
15173
15220
|
});
|
|
15174
15221
|
}
|
|
15175
15222
|
// NEW: Add related modes from expandedRowConfig
|
|
15176
15223
|
if (expandedRowConfig?.addRelated) {
|
|
15177
|
-
expandedRowConfig.addRelated.forEach(config => {
|
|
15224
|
+
expandedRowConfig.addRelated.forEach((config) => {
|
|
15178
15225
|
if (config.showInMenu !== false) {
|
|
15179
15226
|
builtInActions.push({
|
|
15180
15227
|
label: config.label,
|
|
@@ -15183,15 +15230,15 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15183
15230
|
const rowKey = getRowKey(item);
|
|
15184
15231
|
handleExpansionWithMode(rowKey, `addRelated-${config.key}`);
|
|
15185
15232
|
},
|
|
15186
|
-
variant:
|
|
15187
|
-
tooltip: config.label
|
|
15233
|
+
variant: "secondary",
|
|
15234
|
+
tooltip: config.label,
|
|
15188
15235
|
});
|
|
15189
15236
|
}
|
|
15190
15237
|
});
|
|
15191
15238
|
}
|
|
15192
15239
|
// NEW: Manage related modes from expandedRowConfig
|
|
15193
15240
|
if (expandedRowConfig?.manageRelated) {
|
|
15194
|
-
expandedRowConfig.manageRelated.forEach(config => {
|
|
15241
|
+
expandedRowConfig.manageRelated.forEach((config) => {
|
|
15195
15242
|
if (config.showInMenu !== false) {
|
|
15196
15243
|
builtInActions.push({
|
|
15197
15244
|
label: config.label,
|
|
@@ -15200,8 +15247,8 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15200
15247
|
const rowKey = getRowKey(item);
|
|
15201
15248
|
handleExpansionWithMode(rowKey, `manageRelated-${config.key}`);
|
|
15202
15249
|
},
|
|
15203
|
-
variant:
|
|
15204
|
-
tooltip: config.label
|
|
15250
|
+
variant: "ghost",
|
|
15251
|
+
tooltip: config.label,
|
|
15205
15252
|
});
|
|
15206
15253
|
}
|
|
15207
15254
|
});
|
|
@@ -15211,11 +15258,11 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15211
15258
|
let deleteAction = null;
|
|
15212
15259
|
if (onDelete) {
|
|
15213
15260
|
deleteAction = {
|
|
15214
|
-
label:
|
|
15261
|
+
label: "Delete",
|
|
15215
15262
|
icon: lucideReact.Trash,
|
|
15216
15263
|
onClick: onDelete,
|
|
15217
|
-
variant:
|
|
15218
|
-
tooltip:
|
|
15264
|
+
variant: "danger",
|
|
15265
|
+
tooltip: "Delete item",
|
|
15219
15266
|
};
|
|
15220
15267
|
}
|
|
15221
15268
|
// Build final actions array with consistent ordering:
|
|
@@ -15228,11 +15275,11 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15228
15275
|
const allActions = [
|
|
15229
15276
|
...builtInActions,
|
|
15230
15277
|
...actions,
|
|
15231
|
-
...(deleteAction ? [deleteAction] : [])
|
|
15278
|
+
...(deleteAction ? [deleteAction] : []),
|
|
15232
15279
|
];
|
|
15233
15280
|
// Convert actions to menu items for context menu
|
|
15234
15281
|
const convertActionsToMenuItems = (item) => {
|
|
15235
|
-
const visibleActions = allActions.filter(action => !action.show || action.show(item));
|
|
15282
|
+
const visibleActions = allActions.filter((action) => !action.show || action.show(item));
|
|
15236
15283
|
return visibleActions.map((action, idx) => {
|
|
15237
15284
|
let iconElement = null;
|
|
15238
15285
|
if (action.icon) {
|
|
@@ -15240,7 +15287,9 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15240
15287
|
iconElement = action.icon;
|
|
15241
15288
|
}
|
|
15242
15289
|
else {
|
|
15243
|
-
iconElement = React.createElement(action.icon, {
|
|
15290
|
+
iconElement = React.createElement(action.icon, {
|
|
15291
|
+
className: "h-4 w-4",
|
|
15292
|
+
});
|
|
15244
15293
|
}
|
|
15245
15294
|
}
|
|
15246
15295
|
return {
|
|
@@ -15248,7 +15297,7 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15248
15297
|
label: action.label,
|
|
15249
15298
|
icon: iconElement,
|
|
15250
15299
|
onClick: () => action.onClick(item),
|
|
15251
|
-
danger: action.variant ===
|
|
15300
|
+
danger: action.variant === "danger",
|
|
15252
15301
|
};
|
|
15253
15302
|
});
|
|
15254
15303
|
};
|
|
@@ -15257,7 +15306,9 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15257
15306
|
// Expansion state management
|
|
15258
15307
|
const [internalExpandedRows, setInternalExpandedRows] = React.useState(new Set());
|
|
15259
15308
|
// Use external selection if provided, otherwise internal
|
|
15260
|
-
const selectedRowsSet = externalSelectedRows !== undefined
|
|
15309
|
+
const selectedRowsSet = externalSelectedRows !== undefined
|
|
15310
|
+
? externalSelectedRows
|
|
15311
|
+
: internalSelectedRows;
|
|
15261
15312
|
const setSelectedRows = (newSet) => {
|
|
15262
15313
|
if (externalSelectedRows !== undefined) {
|
|
15263
15314
|
// Controlled component - notify parent
|
|
@@ -15291,7 +15342,9 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15291
15342
|
}
|
|
15292
15343
|
};
|
|
15293
15344
|
// Use external expansion if provided, otherwise internal
|
|
15294
|
-
const expandedRowsSet = externalExpandedRows !== undefined
|
|
15345
|
+
const expandedRowsSet = externalExpandedRows !== undefined
|
|
15346
|
+
? externalExpandedRows
|
|
15347
|
+
: internalExpandedRows;
|
|
15295
15348
|
const setExpandedRows = (newSet) => {
|
|
15296
15349
|
if (externalExpandedRows !== undefined) ;
|
|
15297
15350
|
else {
|
|
@@ -15333,10 +15386,10 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15333
15386
|
const totalCols = visibleColumns.length;
|
|
15334
15387
|
// If no cell is focused, focus first data cell on first arrow key
|
|
15335
15388
|
if (!focusedCell) {
|
|
15336
|
-
if ([
|
|
15389
|
+
if (["ArrowDown", "ArrowUp", "ArrowLeft", "ArrowRight"].includes(e.key)) {
|
|
15337
15390
|
e.preventDefault();
|
|
15338
15391
|
setFocusedCell({ row: 0, col: 0 });
|
|
15339
|
-
const colHeader = visibleColumns[0]?.header ||
|
|
15392
|
+
const colHeader = visibleColumns[0]?.header || "first column";
|
|
15340
15393
|
setAnnouncement(`Row 1, ${colHeader}`);
|
|
15341
15394
|
return;
|
|
15342
15395
|
}
|
|
@@ -15344,73 +15397,73 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15344
15397
|
}
|
|
15345
15398
|
const { row, col } = focusedCell;
|
|
15346
15399
|
switch (e.key) {
|
|
15347
|
-
case
|
|
15400
|
+
case "ArrowDown":
|
|
15348
15401
|
e.preventDefault();
|
|
15349
15402
|
if (row < totalRows - 1) {
|
|
15350
15403
|
const newRow = row + 1;
|
|
15351
15404
|
setFocusedCell({ row: newRow, col });
|
|
15352
15405
|
const rowItem = data[newRow];
|
|
15353
|
-
const colHeader = visibleColumns[col]?.header ||
|
|
15406
|
+
const colHeader = visibleColumns[col]?.header || "";
|
|
15354
15407
|
const cellValue = rowItem[visibleColumns[col]?.key];
|
|
15355
|
-
setAnnouncement(`Row ${newRow + 1}, ${colHeader}: ${cellValue ||
|
|
15408
|
+
setAnnouncement(`Row ${newRow + 1}, ${colHeader}: ${cellValue || "empty"}`);
|
|
15356
15409
|
}
|
|
15357
15410
|
break;
|
|
15358
|
-
case
|
|
15411
|
+
case "ArrowUp":
|
|
15359
15412
|
e.preventDefault();
|
|
15360
15413
|
if (row > 0) {
|
|
15361
15414
|
const newRow = row - 1;
|
|
15362
15415
|
setFocusedCell({ row: newRow, col });
|
|
15363
15416
|
const rowItem = data[newRow];
|
|
15364
|
-
const colHeader = visibleColumns[col]?.header ||
|
|
15417
|
+
const colHeader = visibleColumns[col]?.header || "";
|
|
15365
15418
|
const cellValue = rowItem[visibleColumns[col]?.key];
|
|
15366
|
-
setAnnouncement(`Row ${newRow + 1}, ${colHeader}: ${cellValue ||
|
|
15419
|
+
setAnnouncement(`Row ${newRow + 1}, ${colHeader}: ${cellValue || "empty"}`);
|
|
15367
15420
|
}
|
|
15368
15421
|
break;
|
|
15369
|
-
case
|
|
15422
|
+
case "ArrowRight":
|
|
15370
15423
|
e.preventDefault();
|
|
15371
15424
|
if (col < totalCols - 1) {
|
|
15372
15425
|
const newCol = col + 1;
|
|
15373
15426
|
setFocusedCell({ row, col: newCol });
|
|
15374
15427
|
const rowItem = data[row];
|
|
15375
|
-
const colHeader = visibleColumns[newCol]?.header ||
|
|
15428
|
+
const colHeader = visibleColumns[newCol]?.header || "";
|
|
15376
15429
|
const cellValue = rowItem[visibleColumns[newCol]?.key];
|
|
15377
|
-
setAnnouncement(`${colHeader}: ${cellValue ||
|
|
15430
|
+
setAnnouncement(`${colHeader}: ${cellValue || "empty"}`);
|
|
15378
15431
|
}
|
|
15379
15432
|
break;
|
|
15380
|
-
case
|
|
15433
|
+
case "ArrowLeft":
|
|
15381
15434
|
e.preventDefault();
|
|
15382
15435
|
if (col > 0) {
|
|
15383
15436
|
const newCol = col - 1;
|
|
15384
15437
|
setFocusedCell({ row, col: newCol });
|
|
15385
15438
|
const rowItem = data[row];
|
|
15386
|
-
const colHeader = visibleColumns[newCol]?.header ||
|
|
15439
|
+
const colHeader = visibleColumns[newCol]?.header || "";
|
|
15387
15440
|
const cellValue = rowItem[visibleColumns[newCol]?.key];
|
|
15388
|
-
setAnnouncement(`${colHeader}: ${cellValue ||
|
|
15441
|
+
setAnnouncement(`${colHeader}: ${cellValue || "empty"}`);
|
|
15389
15442
|
}
|
|
15390
15443
|
break;
|
|
15391
|
-
case
|
|
15444
|
+
case "Home":
|
|
15392
15445
|
e.preventDefault();
|
|
15393
15446
|
if (e.ctrlKey) {
|
|
15394
15447
|
// Ctrl+Home: Go to first cell
|
|
15395
15448
|
setFocusedCell({ row: 0, col: 0 });
|
|
15396
|
-
setAnnouncement(`First cell, Row 1, ${visibleColumns[0]?.header ||
|
|
15449
|
+
setAnnouncement(`First cell, Row 1, ${visibleColumns[0]?.header || ""}`);
|
|
15397
15450
|
}
|
|
15398
15451
|
else {
|
|
15399
15452
|
// Home: Go to first cell in current row
|
|
15400
15453
|
setFocusedCell({ row, col: 0 });
|
|
15401
15454
|
const rowItem = data[row];
|
|
15402
15455
|
const cellValue = rowItem[visibleColumns[0]?.key];
|
|
15403
|
-
setAnnouncement(`${visibleColumns[0]?.header ||
|
|
15456
|
+
setAnnouncement(`${visibleColumns[0]?.header || ""}: ${cellValue || "empty"}`);
|
|
15404
15457
|
}
|
|
15405
15458
|
break;
|
|
15406
|
-
case
|
|
15459
|
+
case "End":
|
|
15407
15460
|
e.preventDefault();
|
|
15408
15461
|
if (e.ctrlKey) {
|
|
15409
15462
|
// Ctrl+End: Go to last cell
|
|
15410
15463
|
const lastRow = totalRows - 1;
|
|
15411
15464
|
const lastCol = totalCols - 1;
|
|
15412
15465
|
setFocusedCell({ row: lastRow, col: lastCol });
|
|
15413
|
-
setAnnouncement(`Last cell, Row ${lastRow + 1}, ${visibleColumns[lastCol]?.header ||
|
|
15466
|
+
setAnnouncement(`Last cell, Row ${lastRow + 1}, ${visibleColumns[lastCol]?.header || ""}`);
|
|
15414
15467
|
}
|
|
15415
15468
|
else {
|
|
15416
15469
|
// End: Go to last cell in current row
|
|
@@ -15418,10 +15471,10 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15418
15471
|
setFocusedCell({ row, col: lastCol });
|
|
15419
15472
|
const rowItem = data[row];
|
|
15420
15473
|
const cellValue = rowItem[visibleColumns[lastCol]?.key];
|
|
15421
|
-
setAnnouncement(`${visibleColumns[lastCol]?.header ||
|
|
15474
|
+
setAnnouncement(`${visibleColumns[lastCol]?.header || ""}: ${cellValue || "empty"}`);
|
|
15422
15475
|
}
|
|
15423
15476
|
break;
|
|
15424
|
-
case
|
|
15477
|
+
case "Enter":
|
|
15425
15478
|
e.preventDefault();
|
|
15426
15479
|
{
|
|
15427
15480
|
const rowItem = data[row];
|
|
@@ -15429,27 +15482,27 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15429
15482
|
// Priority: Edit mode > Details mode > Row double-click handler
|
|
15430
15483
|
if (onEdit) {
|
|
15431
15484
|
onEdit(rowItem);
|
|
15432
|
-
setAnnouncement(
|
|
15485
|
+
setAnnouncement("Opening edit mode");
|
|
15433
15486
|
}
|
|
15434
15487
|
else if (expandedRowConfig?.edit) {
|
|
15435
|
-
handleExpansionWithMode(rowKey,
|
|
15436
|
-
setAnnouncement(
|
|
15488
|
+
handleExpansionWithMode(rowKey, "edit");
|
|
15489
|
+
setAnnouncement("Opening inline edit");
|
|
15437
15490
|
}
|
|
15438
15491
|
else if (expandedRowConfig?.details) {
|
|
15439
|
-
handleExpansionWithMode(rowKey,
|
|
15440
|
-
setAnnouncement(
|
|
15492
|
+
handleExpansionWithMode(rowKey, "details");
|
|
15493
|
+
setAnnouncement("Opening details view");
|
|
15441
15494
|
}
|
|
15442
15495
|
else if (onRowDoubleClick) {
|
|
15443
15496
|
onRowDoubleClick(rowItem);
|
|
15444
|
-
setAnnouncement(
|
|
15497
|
+
setAnnouncement("Activating row");
|
|
15445
15498
|
}
|
|
15446
15499
|
else if (onRowClick) {
|
|
15447
15500
|
onRowClick(rowItem);
|
|
15448
|
-
setAnnouncement(
|
|
15501
|
+
setAnnouncement("Row selected");
|
|
15449
15502
|
}
|
|
15450
15503
|
}
|
|
15451
15504
|
break;
|
|
15452
|
-
case
|
|
15505
|
+
case " ":
|
|
15453
15506
|
// Space: Toggle selection if selectable
|
|
15454
15507
|
if (selectable) {
|
|
15455
15508
|
e.preventDefault();
|
|
@@ -15457,60 +15510,82 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15457
15510
|
const rowKey = getRowKey(rowItem);
|
|
15458
15511
|
handleRowSelect(rowKey);
|
|
15459
15512
|
const isNowSelected = !selectedRowsSet.has(rowKey);
|
|
15460
|
-
setAnnouncement(isNowSelected ?
|
|
15513
|
+
setAnnouncement(isNowSelected ? "Row selected" : "Row deselected");
|
|
15461
15514
|
}
|
|
15462
15515
|
break;
|
|
15463
|
-
case
|
|
15516
|
+
case "Escape":
|
|
15464
15517
|
e.preventDefault();
|
|
15465
15518
|
setFocusedCell(null);
|
|
15466
|
-
setAnnouncement(
|
|
15519
|
+
setAnnouncement("Table navigation exited");
|
|
15467
15520
|
// Return focus to table container
|
|
15468
|
-
tableBodyRef.current?.closest(
|
|
15521
|
+
tableBodyRef.current?.closest("table")?.focus();
|
|
15469
15522
|
break;
|
|
15470
|
-
case
|
|
15523
|
+
case "PageDown":
|
|
15471
15524
|
e.preventDefault();
|
|
15472
15525
|
{
|
|
15473
15526
|
const jumpSize = 10;
|
|
15474
15527
|
const newRow = Math.min(row + jumpSize, totalRows - 1);
|
|
15475
15528
|
setFocusedCell({ row: newRow, col });
|
|
15476
|
-
const colHeader = visibleColumns[col]?.header ||
|
|
15529
|
+
const colHeader = visibleColumns[col]?.header || "";
|
|
15477
15530
|
setAnnouncement(`Row ${newRow + 1} of ${totalRows}, ${colHeader}`);
|
|
15478
15531
|
}
|
|
15479
15532
|
break;
|
|
15480
|
-
case
|
|
15533
|
+
case "PageUp":
|
|
15481
15534
|
e.preventDefault();
|
|
15482
15535
|
{
|
|
15483
15536
|
const jumpSize = 10;
|
|
15484
15537
|
const newRow = Math.max(row - jumpSize, 0);
|
|
15485
15538
|
setFocusedCell({ row: newRow, col });
|
|
15486
|
-
const colHeader = visibleColumns[col]?.header ||
|
|
15539
|
+
const colHeader = visibleColumns[col]?.header || "";
|
|
15487
15540
|
setAnnouncement(`Row ${newRow + 1} of ${totalRows}, ${colHeader}`);
|
|
15488
15541
|
}
|
|
15489
15542
|
break;
|
|
15490
15543
|
}
|
|
15491
|
-
}, [
|
|
15544
|
+
}, [
|
|
15545
|
+
data,
|
|
15546
|
+
visibleColumns,
|
|
15547
|
+
focusedCell,
|
|
15548
|
+
selectable,
|
|
15549
|
+
expandedRowConfig,
|
|
15550
|
+
onEdit,
|
|
15551
|
+
onRowDoubleClick,
|
|
15552
|
+
onRowClick,
|
|
15553
|
+
getRowKey,
|
|
15554
|
+
handleExpansionWithMode,
|
|
15555
|
+
handleRowSelect,
|
|
15556
|
+
selectedRowsSet,
|
|
15557
|
+
]);
|
|
15492
15558
|
// Focus the appropriate cell when focusedCell changes
|
|
15493
15559
|
React.useEffect(() => {
|
|
15494
15560
|
if (focusedCell && tableBodyRef.current) {
|
|
15495
15561
|
const { row, col } = focusedCell;
|
|
15496
|
-
const rows = tableBodyRef.current.querySelectorAll(
|
|
15562
|
+
const rows = tableBodyRef.current.querySelectorAll("tr[data-row-index]");
|
|
15497
15563
|
const targetRow = rows[row];
|
|
15498
15564
|
if (targetRow) {
|
|
15499
15565
|
// Calculate actual column index including extra columns
|
|
15500
15566
|
const hasSelectionCol = selectable;
|
|
15501
15567
|
const hasExpandCol = (expandable || expandedRowConfig) && showExpandChevron;
|
|
15502
15568
|
const hasActionsCol = hasAnyActions;
|
|
15503
|
-
const extraColsBefore = (hasSelectionCol ? 1 : 0) +
|
|
15504
|
-
|
|
15569
|
+
const extraColsBefore = (hasSelectionCol ? 1 : 0) +
|
|
15570
|
+
(hasExpandCol ? 1 : 0) +
|
|
15571
|
+
(hasActionsCol ? 1 : 0);
|
|
15572
|
+
const cells = targetRow.querySelectorAll("td");
|
|
15505
15573
|
const targetCell = cells[col + extraColsBefore];
|
|
15506
15574
|
if (targetCell) {
|
|
15507
15575
|
targetCell.focus();
|
|
15508
15576
|
// Scroll into view if needed
|
|
15509
|
-
targetCell.scrollIntoView({ block:
|
|
15577
|
+
targetCell.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
15510
15578
|
}
|
|
15511
15579
|
}
|
|
15512
15580
|
}
|
|
15513
|
-
}, [
|
|
15581
|
+
}, [
|
|
15582
|
+
focusedCell,
|
|
15583
|
+
selectable,
|
|
15584
|
+
expandable,
|
|
15585
|
+
expandedRowConfig,
|
|
15586
|
+
showExpandChevron,
|
|
15587
|
+
hasAnyActions,
|
|
15588
|
+
]);
|
|
15514
15589
|
// Handle column header click for sorting
|
|
15515
15590
|
const handleSort = (column) => {
|
|
15516
15591
|
if (!column.sortable || !onSortChange)
|
|
@@ -15518,8 +15593,12 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15518
15593
|
const columnKey = String(column.key);
|
|
15519
15594
|
// If clicking the same column, toggle direction
|
|
15520
15595
|
if (currentSort?.key === columnKey) {
|
|
15521
|
-
if (currentSort.direction ===
|
|
15522
|
-
onSortChange({
|
|
15596
|
+
if (currentSort.direction === "asc") {
|
|
15597
|
+
onSortChange({
|
|
15598
|
+
key: columnKey,
|
|
15599
|
+
direction: "desc",
|
|
15600
|
+
label: column.header,
|
|
15601
|
+
});
|
|
15523
15602
|
}
|
|
15524
15603
|
else {
|
|
15525
15604
|
// Remove sort on third click
|
|
@@ -15528,7 +15607,7 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15528
15607
|
}
|
|
15529
15608
|
else {
|
|
15530
15609
|
// New column - start with ascending
|
|
15531
|
-
onSortChange({ key: columnKey, direction:
|
|
15610
|
+
onSortChange({ key: columnKey, direction: "asc", label: column.header });
|
|
15532
15611
|
}
|
|
15533
15612
|
};
|
|
15534
15613
|
// Get sort icon SVG for column (matches reference ui-preview.html)
|
|
@@ -15537,7 +15616,7 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15537
15616
|
return null;
|
|
15538
15617
|
const columnKey = String(column.key);
|
|
15539
15618
|
const isActive = currentSort?.key === columnKey;
|
|
15540
|
-
const isAscending = currentSort?.direction ===
|
|
15619
|
+
const isAscending = currentSort?.direction === "asc";
|
|
15541
15620
|
// Inactive state - show neutral up/down arrows
|
|
15542
15621
|
if (!isActive) {
|
|
15543
15622
|
return (jsxRuntime.jsx("svg", { className: "ml-2 w-4 h-4 text-ink-400 group-hover:text-ink-700", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4" }) }));
|
|
@@ -15550,17 +15629,23 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15550
15629
|
return (jsxRuntime.jsx("svg", { className: "ml-2 w-4 h-4 text-accent-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 17l-4 4m0 0l-4-4m4 4V3" }) }));
|
|
15551
15630
|
};
|
|
15552
15631
|
// Render loading skeleton
|
|
15553
|
-
const renderLoadingSkeleton = () => (jsxRuntime.jsx(jsxRuntime.Fragment, { children: Array.from({ length: loadingRows }, (_, i) => (jsxRuntime.jsxs("tr", { className: `animate-pulse table-row-stable ${bordered ? `border-b ${borderColor}` :
|
|
15632
|
+
const renderLoadingSkeleton = () => (jsxRuntime.jsx(jsxRuntime.Fragment, { children: Array.from({ length: loadingRows }, (_, i) => (jsxRuntime.jsxs("tr", { className: `animate-pulse table-row-stable ${bordered ? `border-b ${borderColor}` : ""}`, children: [selectable && (jsxRuntime.jsx("td", { className: `sticky left-0 bg-white ${currentDensity.cell} border-b ${borderColor} z-10 align-middle`, children: jsxRuntime.jsx("div", { className: "h-4 w-4 bg-paper-200 rounded" }) })), expandable && (jsxRuntime.jsx("td", { className: `sticky left-0 bg-white px-2 ${currentDensity.cell} border-b ${borderColor} z-10`, children: jsxRuntime.jsx("div", { className: "h-4 w-4 bg-paper-200 rounded" }) })), allActions.length > 0 && (jsxRuntime.jsx("td", { className: `sticky left-0 bg-white px-2 ${currentDensity.cell} border-b ${borderColor} z-10`, children: jsxRuntime.jsx("div", { className: "h-8 w-8 bg-paper-200 rounded" }) })), visibleColumns.map((column, colIndex) => {
|
|
15554
15633
|
const columnKey = String(column.key);
|
|
15555
15634
|
const dynamicWidth = columnWidths[columnKey];
|
|
15556
|
-
return (jsxRuntime.jsxs("td", { className: `${currentDensity.cell} whitespace-nowrap table-row-stable ${bordered ? `border ${borderColor}` :
|
|
15635
|
+
return (jsxRuntime.jsxs("td", { className: `${currentDensity.cell} whitespace-nowrap table-row-stable ${bordered ? `border ${borderColor}` : ""}`, style: getColumnStyle(column, dynamicWidth), children: [jsxRuntime.jsx("div", { className: "h-4 bg-paper-200 rounded mb-1" }), jsxRuntime.jsx("div", { className: "h-3 bg-paper-200 rounded w-3/4" })] }, `loading-${i}-${colIndex}`));
|
|
15557
15636
|
})] }, `loading-${i}`))) }));
|
|
15558
15637
|
// Render empty state
|
|
15559
15638
|
const renderEmptyStateContent = () => {
|
|
15560
15639
|
if (customRenderEmptyState) {
|
|
15561
|
-
return (jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { colSpan: visibleColumns.length +
|
|
15640
|
+
return (jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { colSpan: visibleColumns.length +
|
|
15641
|
+
(allActions.length > 0 ? 1 : 0) +
|
|
15642
|
+
(selectable ? 1 : 0) +
|
|
15643
|
+
(expandable ? 1 : 0), children: customRenderEmptyState() }) }));
|
|
15562
15644
|
}
|
|
15563
|
-
return (jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { colSpan: visibleColumns.length +
|
|
15645
|
+
return (jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { colSpan: visibleColumns.length +
|
|
15646
|
+
(allActions.length > 0 ? 1 : 0) +
|
|
15647
|
+
(selectable ? 1 : 0) +
|
|
15648
|
+
(expandable ? 1 : 0), className: `${currentDensity.cell} py-8 text-center text-ink-500`, children: error || emptyMessage }) }));
|
|
15564
15649
|
};
|
|
15565
15650
|
// Virtual scrolling calculations
|
|
15566
15651
|
const getVisibleRange = () => {
|
|
@@ -15590,14 +15675,18 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15590
15675
|
const isSelected = selectedRowsSet.has(rowKey);
|
|
15591
15676
|
const isExpanded = expandedRowsSet.has(rowKey);
|
|
15592
15677
|
const rowBgClass = getRowBackgroundClass(item, index);
|
|
15593
|
-
const borderClass = bordered
|
|
15594
|
-
|
|
15678
|
+
const borderClass = bordered
|
|
15679
|
+
? `border-b ${borderColor}`
|
|
15680
|
+
: !visibleColumns.some((col) => !!col.renderSecondary)
|
|
15681
|
+
? `border-b ${borderColor}`
|
|
15682
|
+
: "";
|
|
15683
|
+
const hasSecondaryRow = visibleColumns.some((col) => !!col.renderSecondary);
|
|
15595
15684
|
// Hover state for row pair (primary + secondary)
|
|
15596
15685
|
const isHovered = hoveredRowKey === rowKey;
|
|
15597
|
-
const hoverClass = disableHover ?
|
|
15686
|
+
const hoverClass = disableHover ? "" : isHovered ? "bg-paper-100" : "";
|
|
15598
15687
|
// Check if this row is keyboard-focused
|
|
15599
15688
|
const isKeyboardFocused = focusedCell?.row === index;
|
|
15600
|
-
return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsxs("tr", { "data-row-index": index, className: `table-row-stable ${onRowDoubleClick || onRowClick || onEdit || expandedRowConfig?.edit || expandedRowConfig?.details || expandedRowConfig?.addRelated?.length || expandedRowConfig?.manageRelated?.length ?
|
|
15689
|
+
return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsxs("tr", { "data-row-index": index, className: `table-row-stable ${onRowDoubleClick || onRowClick || onEdit || expandedRowConfig?.edit || expandedRowConfig?.details || expandedRowConfig?.addRelated?.length || expandedRowConfig?.manageRelated?.length ? "cursor-pointer" : ""} ${isSelected ? "bg-accent-50 border-l-2 border-accent-500" : hoverClass || rowBgClass} ${borderClass} ${isKeyboardFocused ? "ring-2 ring-inset ring-accent-400" : ""}`, onMouseEnter: () => !disableHover && setHoveredRowKey(rowKey), onMouseLeave: () => !disableHover && setHoveredRowKey(null), onClick: () => onRowClick?.(item), onContextMenu: (e) => {
|
|
15601
15690
|
if (enableContextMenu && allActions.length > 0) {
|
|
15602
15691
|
e.preventDefault();
|
|
15603
15692
|
e.stopPropagation();
|
|
@@ -15616,109 +15705,146 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15616
15705
|
}
|
|
15617
15706
|
// Priority 2: If there's an expandable edit mode, trigger it
|
|
15618
15707
|
else if (expandedRowConfig?.edit) {
|
|
15619
|
-
handleExpansionWithMode(rowKey,
|
|
15708
|
+
handleExpansionWithMode(rowKey, "edit");
|
|
15620
15709
|
}
|
|
15621
15710
|
// Priority 3: If there's an expandable details mode, trigger it
|
|
15622
15711
|
else if (expandedRowConfig?.details) {
|
|
15623
|
-
handleExpansionWithMode(rowKey,
|
|
15712
|
+
handleExpansionWithMode(rowKey, "details");
|
|
15624
15713
|
}
|
|
15625
15714
|
// Priority 4: If there's any addRelated mode, trigger the first one
|
|
15626
|
-
else if (expandedRowConfig?.addRelated &&
|
|
15715
|
+
else if (expandedRowConfig?.addRelated &&
|
|
15716
|
+
expandedRowConfig.addRelated.length > 0) {
|
|
15627
15717
|
handleExpansionWithMode(rowKey, `addRelated-${expandedRowConfig.addRelated[0].key}`);
|
|
15628
15718
|
}
|
|
15629
15719
|
// Priority 5: If there's any manageRelated mode, trigger the first one
|
|
15630
|
-
else if (expandedRowConfig?.manageRelated &&
|
|
15720
|
+
else if (expandedRowConfig?.manageRelated &&
|
|
15721
|
+
expandedRowConfig.manageRelated.length > 0) {
|
|
15631
15722
|
handleExpansionWithMode(rowKey, `manageRelated-${expandedRowConfig.manageRelated[0].key}`);
|
|
15632
15723
|
}
|
|
15633
15724
|
// Priority 6: Legacy onRowDoubleClick handler
|
|
15634
15725
|
else {
|
|
15635
15726
|
onRowDoubleClick?.(item);
|
|
15636
15727
|
}
|
|
15637
|
-
}, title: onEdit
|
|
15638
|
-
|
|
15639
|
-
|
|
15640
|
-
|
|
15641
|
-
|
|
15642
|
-
|
|
15643
|
-
|
|
15644
|
-
|
|
15645
|
-
|
|
15646
|
-
|
|
15647
|
-
|
|
15648
|
-
|
|
15649
|
-
|
|
15728
|
+
}, title: onEdit
|
|
15729
|
+
? "Double-click to edit"
|
|
15730
|
+
: expandedRowConfig?.edit
|
|
15731
|
+
? "Double-click to edit inline"
|
|
15732
|
+
: expandedRowConfig?.details
|
|
15733
|
+
? "Double-click to view details"
|
|
15734
|
+
: expandedRowConfig?.addRelated &&
|
|
15735
|
+
expandedRowConfig.addRelated.length > 0
|
|
15736
|
+
? `Double-click to ${expandedRowConfig.addRelated[0].label}`
|
|
15737
|
+
: expandedRowConfig?.manageRelated &&
|
|
15738
|
+
expandedRowConfig.manageRelated.length > 0
|
|
15739
|
+
? `Double-click to ${expandedRowConfig.manageRelated[0].label}`
|
|
15740
|
+
: onRowDoubleClick
|
|
15741
|
+
? "Double-click for details"
|
|
15742
|
+
: onRowClick
|
|
15743
|
+
? "Click to select"
|
|
15744
|
+
: undefined, children: [selectable && (jsxRuntime.jsx("td", { className: `sticky left-0 z-10 ${bordered ? `border ${borderColor}` : ""}`, style: {
|
|
15745
|
+
backgroundColor: "inherit",
|
|
15746
|
+
verticalAlign: "middle",
|
|
15747
|
+
padding: "0.375rem 0.75rem",
|
|
15748
|
+
textAlign: "center",
|
|
15749
|
+
}, rowSpan: hasSecondaryRow ? 2 : 1, children: jsxRuntime.jsx("input", { type: "checkbox", checked: isSelected, onChange: () => handleRowSelect(rowKey), className: "w-4 h-4 text-accent-600 border-paper-300 rounded focus:ring-accent-400", "aria-label": `Select row ${rowKey}` }) })), (expandable || expandedRowConfig) && showExpandChevron && (jsxRuntime.jsx("td", { className: `sticky left-0 px-2 ${currentDensity.cell} z-10 ${bordered ? `border ${borderColor}` : ""}`, style: { backgroundColor: "inherit", verticalAlign: "middle" }, rowSpan: hasSecondaryRow ? 2 : 1, children: jsxRuntime.jsx("button", { onClick: () => {
|
|
15650
15750
|
// NEW: Enhanced logic for expandedRowConfig
|
|
15651
|
-
if (expandedRowConfig?.details &&
|
|
15751
|
+
if (expandedRowConfig?.details &&
|
|
15752
|
+
expandedRowConfig.details.triggerOnExpand !== false) {
|
|
15652
15753
|
// Trigger details mode if configured
|
|
15653
|
-
handleExpansionWithMode(rowKey,
|
|
15754
|
+
handleExpansionWithMode(rowKey, "details");
|
|
15654
15755
|
}
|
|
15655
|
-
else if (expandedRowConfig?.edit &&
|
|
15756
|
+
else if (expandedRowConfig?.edit &&
|
|
15757
|
+
expandedRowConfig.edit.triggerOnDoubleClick !== false) {
|
|
15656
15758
|
// Fallback to edit mode if no details but edit is available
|
|
15657
|
-
handleExpansionWithMode(rowKey,
|
|
15759
|
+
handleExpansionWithMode(rowKey, "edit");
|
|
15658
15760
|
}
|
|
15659
15761
|
else {
|
|
15660
15762
|
// Legacy: use handleRowExpand
|
|
15661
15763
|
handleRowExpand(rowKey);
|
|
15662
15764
|
}
|
|
15663
|
-
}, className: "text-ink-500 hover:text-ink-900 transition-colors", "aria-label": isExpanded ||
|
|
15664
|
-
|
|
15665
|
-
|
|
15666
|
-
|
|
15667
|
-
|
|
15668
|
-
|
|
15765
|
+
}, className: "text-ink-500 hover:text-ink-900 transition-colors", "aria-label": isExpanded || expansionState?.rowKey === rowKey
|
|
15766
|
+
? "Collapse row"
|
|
15767
|
+
: "Expand row", children: isExpanded || expansionState?.rowKey === rowKey ? (jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4" })) : (jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4" })) }) })), allActions.length > 0 && (jsxRuntime.jsx("td", { className: "sticky left-0 whitespace-nowrap shadow-[4px_0_6px_-2px_rgba(0,0,0,0.1)] z-10", style: {
|
|
15768
|
+
width: "28px",
|
|
15769
|
+
padding: "0",
|
|
15770
|
+
backgroundColor: "inherit",
|
|
15771
|
+
verticalAlign: "middle",
|
|
15772
|
+
}, onClick: (e) => e.stopPropagation(), rowSpan: hasSecondaryRow ? 2 : 1, children: jsxRuntime.jsx("div", { style: {
|
|
15773
|
+
display: "inline-flex",
|
|
15774
|
+
alignItems: "center",
|
|
15775
|
+
justifyContent: "center",
|
|
15776
|
+
width: "28px",
|
|
15777
|
+
}, children: jsxRuntime.jsx(ActionMenu, { actions: allActions, item: item }) }) })), visibleColumns.map((column, colIdx) => {
|
|
15669
15778
|
const columnKey = String(column.key);
|
|
15670
15779
|
const dynamicWidth = columnWidths[columnKey];
|
|
15671
|
-
const value = typeof column.key ===
|
|
15780
|
+
const value = typeof column.key === "string"
|
|
15672
15781
|
? item[column.key]
|
|
15673
15782
|
: item[column.key];
|
|
15674
|
-
const primaryContent = column.render
|
|
15783
|
+
const primaryContent = column.render
|
|
15784
|
+
? column.render(item, value)
|
|
15785
|
+
: String(value || "");
|
|
15786
|
+
// Tooltip: caller-provided > raw value stringified. Empty
|
|
15787
|
+
// strings get dropped so we don't render an empty title
|
|
15788
|
+
// attribute (which the browser would still show as a
|
|
15789
|
+
// 0-width tooltip box on hover).
|
|
15790
|
+
const primaryTooltipText = column.tooltip
|
|
15791
|
+
? column.tooltip(item, value)
|
|
15792
|
+
: value !== null && value !== undefined && value !== ""
|
|
15793
|
+
? String(value)
|
|
15794
|
+
: undefined;
|
|
15675
15795
|
// Reduce left padding on first column when there are action buttons
|
|
15676
15796
|
const isFirstColumn = colIdx === 0;
|
|
15677
|
-
const paddingClass = isFirstColumn && allActions.length > 0 ?
|
|
15797
|
+
const paddingClass = isFirstColumn && allActions.length > 0 ? "pl-3" : "";
|
|
15678
15798
|
// Check if this cell is keyboard-focused
|
|
15679
15799
|
const isCellFocused = focusedCell?.row === index && focusedCell?.col === colIdx;
|
|
15680
|
-
return (jsxRuntime.jsx("td", { className: `${currentDensity.cell} ${paddingClass} ${column.className ||
|
|
15681
|
-
})] }), hasSecondaryRow && (jsxRuntime.jsx("tr", { className: `secondary-row ${isSelected ?
|
|
15800
|
+
return (jsxRuntime.jsx("td", { className: `${currentDensity.cell} ${paddingClass} ${column.className || ""} ${bordered ? `border ${borderColor}` : ""} ${isCellFocused ? "outline outline-2 outline-accent-500 outline-offset-[-2px]" : ""}`, style: getColumnStyle(column, dynamicWidth), tabIndex: isCellFocused ? 0 : -1, role: "gridcell", "aria-colindex": colIdx + 1, children: jsxRuntime.jsx("div", { className: `${currentDensity.text} leading-tight`, title: primaryTooltipText, children: primaryContent }) }, `${item.id}-${columnKey}`));
|
|
15801
|
+
})] }), hasSecondaryRow && (jsxRuntime.jsx("tr", { className: `secondary-row ${isSelected ? "bg-accent-50 border-l-2 border-accent-500" : hoverClass || rowBgClass} border-b ${borderColor}`, onMouseEnter: () => !disableHover && setHoveredRowKey(rowKey), onMouseLeave: () => !disableHover && setHoveredRowKey(null), children: visibleColumns.map((column, colIdx) => {
|
|
15682
15802
|
const columnKey = String(column.key);
|
|
15683
15803
|
const dynamicWidth = columnWidths[columnKey];
|
|
15684
|
-
const value = typeof column.key ===
|
|
15804
|
+
const value = typeof column.key === "string"
|
|
15685
15805
|
? item[column.key]
|
|
15686
15806
|
: item[column.key];
|
|
15687
|
-
const secondaryContent = column.renderSecondary
|
|
15807
|
+
const secondaryContent = column.renderSecondary
|
|
15808
|
+
? column.renderSecondary(item, value)
|
|
15809
|
+
: null;
|
|
15810
|
+
// Tooltip on the secondary row prefixes the field label when
|
|
15811
|
+
// available so the otherwise-unlabeled second row stays
|
|
15812
|
+
// self-describing on hover. Caller can override entirely
|
|
15813
|
+
// via `secondaryTooltip`.
|
|
15814
|
+
const hasSecondaryValue = value !== null && value !== undefined && value !== "";
|
|
15815
|
+
let secondaryTooltipText;
|
|
15816
|
+
if (column.secondaryTooltip) {
|
|
15817
|
+
secondaryTooltipText = column.secondaryTooltip(item, value);
|
|
15818
|
+
}
|
|
15819
|
+
else if (hasSecondaryValue) {
|
|
15820
|
+
secondaryTooltipText = column.secondaryHeader
|
|
15821
|
+
? `${column.secondaryHeader}: ${value}`
|
|
15822
|
+
: String(value);
|
|
15823
|
+
}
|
|
15824
|
+
else if (column.secondaryHeader) {
|
|
15825
|
+
secondaryTooltipText = column.secondaryHeader;
|
|
15826
|
+
}
|
|
15688
15827
|
// Reduce left padding on first column when there are action buttons
|
|
15689
15828
|
const isFirstColumn = colIdx === 0;
|
|
15690
|
-
const paddingClass = isFirstColumn && allActions.length > 0 ?
|
|
15691
|
-
return (jsxRuntime.jsx("td", { className: `${currentDensity.cell} py-0.5 ${paddingClass} ${column.className ||
|
|
15829
|
+
const paddingClass = isFirstColumn && allActions.length > 0 ? "pl-3" : "";
|
|
15830
|
+
return (jsxRuntime.jsx("td", { className: `${currentDensity.cell} py-0.5 ${paddingClass} ${column.className || ""} ${bordered ? `border ${borderColor}` : ""}`, style: getColumnStyle(column, dynamicWidth), children: jsxRuntime.jsx("div", { className: "text-xs text-ink-500 leading-tight", title: secondaryTooltipText, children: secondaryContent || jsxRuntime.jsx("span", { className: "invisible", children: "\u2014" }) }) }, `${item.id}-${columnKey}-secondary`));
|
|
15692
15831
|
}) })), expandable && isExpanded && renderExpandedRow && (jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { colSpan: visibleColumns.length +
|
|
15693
15832
|
(selectable ? 1 : 0) +
|
|
15694
|
-
((
|
|
15695
|
-
|
|
15696
|
-
|
|
15697
|
-
|
|
15698
|
-
|
|
15699
|
-
|
|
15700
|
-
|
|
15701
|
-
|
|
15702
|
-
content =
|
|
15703
|
-
|
|
15704
|
-
|
|
15705
|
-
|
|
15706
|
-
|
|
15707
|
-
|
|
15708
|
-
|
|
15709
|
-
}
|
|
15710
|
-
// Details mode
|
|
15711
|
-
else if (mode === 'details' && expandedRowConfig.details) {
|
|
15712
|
-
bgColorClass = 'bg-primary-50/80 border-t border-b border-primary-200/80';
|
|
15713
|
-
content = expandedRowConfig.details.render(item);
|
|
15714
|
-
}
|
|
15715
|
-
// Add related modes
|
|
15716
|
-
else if (mode.startsWith('addRelated-') && expandedRowConfig.addRelated) {
|
|
15717
|
-
const key = mode.replace('addRelated-', '');
|
|
15718
|
-
const config = expandedRowConfig.addRelated.find(c => c.key === key);
|
|
15719
|
-
if (config) {
|
|
15720
|
-
bgColorClass = 'bg-success-50/80 border-t border-b border-success-200/80';
|
|
15721
|
-
content = config.render(item, async (_newItem) => {
|
|
15833
|
+
((expandable || expandedRowConfig) && showExpandChevron
|
|
15834
|
+
? 1
|
|
15835
|
+
: 0) +
|
|
15836
|
+
(allActions.length > 0 ? 1 : 0), className: `${currentDensity.cell} py-4 bg-paper-50`, children: renderExpandedRow(item) }) })), expansionState &&
|
|
15837
|
+
expansionState.rowKey === rowKey &&
|
|
15838
|
+
expandedRowConfig &&
|
|
15839
|
+
(() => {
|
|
15840
|
+
const mode = expansionState.mode;
|
|
15841
|
+
let content = null;
|
|
15842
|
+
let bgColorClass = "bg-paper-50"; // Default
|
|
15843
|
+
// Edit mode
|
|
15844
|
+
if (mode === "edit" && expandedRowConfig.edit) {
|
|
15845
|
+
bgColorClass =
|
|
15846
|
+
"bg-paper-100/80 border-t border-b border-paper-300/80";
|
|
15847
|
+
content = expandedRowConfig.edit.render(item, async (_updated) => {
|
|
15722
15848
|
// Handle save
|
|
15723
15849
|
handleCollapseExpansion();
|
|
15724
15850
|
}, () => {
|
|
@@ -15726,31 +15852,57 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15726
15852
|
handleCollapseExpansion();
|
|
15727
15853
|
});
|
|
15728
15854
|
}
|
|
15729
|
-
|
|
15730
|
-
|
|
15731
|
-
|
|
15732
|
-
|
|
15733
|
-
|
|
15734
|
-
if (config) {
|
|
15735
|
-
bgColorClass = 'bg-slate-50/80 border-t border-b border-slate-200/80';
|
|
15736
|
-
const handleClose = () => setExpansionState(null);
|
|
15737
|
-
content = config.render(item, handleClose);
|
|
15855
|
+
// Details mode
|
|
15856
|
+
else if (mode === "details" && expandedRowConfig.details) {
|
|
15857
|
+
bgColorClass =
|
|
15858
|
+
"bg-primary-50/80 border-t border-b border-primary-200/80";
|
|
15859
|
+
content = expandedRowConfig.details.render(item);
|
|
15738
15860
|
}
|
|
15739
|
-
|
|
15740
|
-
|
|
15741
|
-
|
|
15742
|
-
|
|
15743
|
-
|
|
15744
|
-
|
|
15745
|
-
|
|
15746
|
-
|
|
15861
|
+
// Add related modes
|
|
15862
|
+
else if (mode.startsWith("addRelated-") &&
|
|
15863
|
+
expandedRowConfig.addRelated) {
|
|
15864
|
+
const key = mode.replace("addRelated-", "");
|
|
15865
|
+
const config = expandedRowConfig.addRelated.find((c) => c.key === key);
|
|
15866
|
+
if (config) {
|
|
15867
|
+
bgColorClass =
|
|
15868
|
+
"bg-success-50/80 border-t border-b border-success-200/80";
|
|
15869
|
+
content = config.render(item, async (_newItem) => {
|
|
15870
|
+
// Handle save
|
|
15871
|
+
handleCollapseExpansion();
|
|
15872
|
+
}, () => {
|
|
15873
|
+
// Handle cancel
|
|
15874
|
+
handleCollapseExpansion();
|
|
15875
|
+
});
|
|
15876
|
+
}
|
|
15877
|
+
}
|
|
15878
|
+
// Manage related modes
|
|
15879
|
+
else if (mode.startsWith("manageRelated-") &&
|
|
15880
|
+
expandedRowConfig.manageRelated) {
|
|
15881
|
+
const key = mode.replace("manageRelated-", "");
|
|
15882
|
+
const config = expandedRowConfig.manageRelated.find((c) => c.key === key);
|
|
15883
|
+
if (config) {
|
|
15884
|
+
bgColorClass =
|
|
15885
|
+
"bg-slate-50/80 border-t border-b border-slate-200/80";
|
|
15886
|
+
const handleClose = () => setExpansionState(null);
|
|
15887
|
+
content = config.render(item, handleClose);
|
|
15888
|
+
}
|
|
15889
|
+
}
|
|
15890
|
+
if (!content)
|
|
15891
|
+
return null;
|
|
15892
|
+
return (jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { colSpan: visibleColumns.length +
|
|
15893
|
+
(selectable ? 1 : 0) +
|
|
15894
|
+
((expandable || expandedRowConfig) && showExpandChevron
|
|
15895
|
+
? 1
|
|
15896
|
+
: 0) +
|
|
15897
|
+
(allActions.length > 0 ? 1 : 0), className: `${currentDensity.cell} py-4 ${bgColorClass} animate-expand`, children: content }) }, `expanded-${rowKey}`));
|
|
15898
|
+
})()] }, rowKey));
|
|
15747
15899
|
});
|
|
15748
15900
|
};
|
|
15749
|
-
const tableContent = (jsxRuntime.jsxs("div", { className: `bg-white rounded-lg shadow border-2 ${borderColor} ${virtualized ?
|
|
15901
|
+
const tableContent = (jsxRuntime.jsxs("div", { className: `bg-white rounded-lg shadow border-2 ${borderColor} ${virtualized ? "overflow-hidden" : "overflow-x-auto overflow-y-visible"} ${className}`, style: { position: "relative" }, children: [loading && data.length > 0 && (jsxRuntime.jsx("div", { className: "absolute inset-0 bg-white/75 flex items-center justify-center z-20", style: { backdropFilter: "blur(2px)" }, children: jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-3", children: [jsxRuntime.jsx("div", { className: "loading-spinner", style: { width: "32px", height: "32px", borderWidth: "3px" } }), jsxRuntime.jsx("span", { className: "text-sm font-medium text-ink-600", children: "Loading..." })] }) })), jsxRuntime.jsxs("table", { className: `table-stable w-full ${bordered ? "border-collapse" : ""}`, role: "grid", "aria-label": "Data table", "aria-rowcount": data.length, "aria-colcount": visibleColumns.length, children: [jsxRuntime.jsxs("colgroup", { children: [selectable && jsxRuntime.jsx("col", { className: "w-12" }), (expandable || expandedRowConfig) && showExpandChevron && (jsxRuntime.jsx("col", { className: "w-10" })), allActions.length > 0 && jsxRuntime.jsx("col", { style: { width: "28px" } }), visibleColumns.map((column, index) => {
|
|
15750
15902
|
const columnKey = String(column.key);
|
|
15751
15903
|
const dynamicWidth = columnWidths[columnKey];
|
|
15752
15904
|
return (jsxRuntime.jsx("col", { style: getColumnStyle(column, dynamicWidth) }, index));
|
|
15753
|
-
})] }), jsxRuntime.jsx("thead", { className: `bg-paper-100 sticky top-0 z-10 ${headerClassName}`, children: jsxRuntime.jsxs("tr", { className: "table-header-row", children: [selectable && (jsxRuntime.jsx("th", { className: `sticky left-0 bg-paper-100 ${currentDensity.header} border-b ${borderColor} z-20 w-12 ${bordered ? `border ${borderColor}` :
|
|
15905
|
+
})] }), jsxRuntime.jsx("thead", { className: `bg-paper-100 sticky top-0 z-10 ${headerClassName}`, children: jsxRuntime.jsxs("tr", { className: "table-header-row", children: [selectable && (jsxRuntime.jsx("th", { className: `sticky left-0 bg-paper-100 ${currentDensity.header} border-b ${borderColor} z-20 w-12 ${bordered ? `border ${borderColor}` : ""}`, children: jsxRuntime.jsx("input", { type: "checkbox", checked: selectedRowsSet.size === data.length && data.length > 0, onChange: handleSelectAll, className: "w-4 h-4 text-accent-600 border-paper-300 rounded focus:ring-accent-400", "aria-label": "Select all rows" }) })), (expandable || expandedRowConfig) && showExpandChevron && (jsxRuntime.jsx("th", { className: `sticky left-0 bg-paper-100 px-2 ${currentDensity.header} border-b ${borderColor} z-19 w-10 ${bordered ? `border ${borderColor}` : ""}` })), allActions.length > 0 && (jsxRuntime.jsx("th", { className: `sticky left-0 bg-paper-100 text-center text-xs font-medium text-ink-700 uppercase tracking-wider border-b ${borderColor} z-20 ${bordered ? `border ${borderColor}` : ""}`, style: { width: "28px", padding: "0" } })), visibleColumns.map((column, colIdx) => {
|
|
15754
15906
|
const columnKey = String(column.key);
|
|
15755
15907
|
const dynamicWidth = columnWidths[columnKey];
|
|
15756
15908
|
const thRef = React.useRef(null);
|
|
@@ -15758,24 +15910,28 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15758
15910
|
const isDragOver = dragOverColumn === columnKey;
|
|
15759
15911
|
// Reduce left padding on first column when there are action buttons (match body cells)
|
|
15760
15912
|
const isFirstColumn = colIdx === 0;
|
|
15761
|
-
const headerPaddingClass = isFirstColumn && allActions.length > 0 ?
|
|
15913
|
+
const headerPaddingClass = isFirstColumn && allActions.length > 0 ? "pl-3" : "";
|
|
15762
15914
|
return (jsxRuntime.jsxs("th", { ref: thRef, draggable: reorderable, onDragStart: (e) => reorderable && handleDragStart(e, columnKey), onDragOver: (e) => reorderable && handleDragOver(e, columnKey), onDragEnd: handleDragEnd, onDrop: (e) => reorderable && handleDrop(e, columnKey), className: `
|
|
15763
|
-
${currentDensity.header} ${headerPaddingClass} text-left border-b ${borderColor} ${bordered ? `border ${borderColor}` :
|
|
15764
|
-
${reorderable ?
|
|
15765
|
-
${isDragging ?
|
|
15766
|
-
${isDragOver ?
|
|
15915
|
+
${currentDensity.header} ${headerPaddingClass} text-left border-b ${borderColor} ${bordered ? `border ${borderColor}` : ""} relative
|
|
15916
|
+
${reorderable ? "cursor-move" : ""}
|
|
15917
|
+
${isDragging ? "opacity-50" : ""}
|
|
15918
|
+
${isDragOver ? "bg-accent-100" : ""}
|
|
15767
15919
|
`, style: getColumnStyle(column, dynamicWidth), children: [column.sortable ? (jsxRuntime.jsxs("button", { onClick: () => handleSort(column), className: "group inline-flex items-center text-xs font-medium text-ink-500 uppercase tracking-wider hover:text-ink-900 transition-colors", children: [jsxRuntime.jsx("span", { children: column.header }), getSortIcon(column)] })) : (jsxRuntime.jsx("span", { className: "text-xs font-medium text-ink-500 uppercase tracking-wider", children: column.header })), resizable && (jsxRuntime.jsx("div", { className: "absolute right-0 top-0 bottom-0 w-1 cursor-col-resize hover:bg-accent-400 group", onMouseDown: (e) => {
|
|
15768
15920
|
const currentWidth = thRef.current?.offsetWidth || 100;
|
|
15769
15921
|
handleResizeStart(e, columnKey, currentWidth);
|
|
15770
15922
|
}, children: jsxRuntime.jsx("div", { className: "absolute right-0 top-0 bottom-0 w-1 bg-paper-300 group-hover:bg-accent-400 transition-colors" }) }))] }, columnKey));
|
|
15771
|
-
})] }) }), jsxRuntime.jsx("tbody", { ref: tableBodyRef, className: "bg-white table-loading transition-opacity duration-200", onKeyDown: handleKeyboardNavigation, tabIndex: 0, role: "rowgroup", "aria-label": "Table data", children: loading && data.length === 0
|
|
15923
|
+
})] }) }), jsxRuntime.jsx("tbody", { ref: tableBodyRef, className: "bg-white table-loading transition-opacity duration-200", onKeyDown: handleKeyboardNavigation, tabIndex: 0, role: "rowgroup", "aria-label": "Table data", children: loading && data.length === 0
|
|
15924
|
+
? renderLoadingSkeleton()
|
|
15925
|
+
: data.length === 0
|
|
15926
|
+
? renderEmptyStateContent()
|
|
15927
|
+
: renderDataRows() })] })] }));
|
|
15772
15928
|
// Wrap in scrollable container if virtualized
|
|
15773
|
-
const finalContent = virtualized ? (jsxRuntime.jsx("div", { ref: tableContainerRef, onScroll: handleScroll, style: { height: virtualHeight, overflow:
|
|
15929
|
+
const finalContent = virtualized ? (jsxRuntime.jsx("div", { ref: tableContainerRef, onScroll: handleScroll, style: { height: virtualHeight, overflow: "auto" }, className: "rounded-lg", children: tableContent })) : (tableContent);
|
|
15774
15930
|
// Calculate pagination values
|
|
15775
15931
|
const effectiveTotalItems = totalItems ?? data.length;
|
|
15776
15932
|
const totalPages = Math.ceil(effectiveTotalItems / pageSize);
|
|
15777
15933
|
// Page size selector options
|
|
15778
|
-
const pageSizeSelectOptions = pageSizeOptions.map(size => ({
|
|
15934
|
+
const pageSizeSelectOptions = pageSizeOptions.map((size) => ({
|
|
15779
15935
|
value: String(size),
|
|
15780
15936
|
label: `${size} per page`,
|
|
15781
15937
|
}));
|
|
@@ -15783,17 +15939,20 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15783
15939
|
const renderPaginationControls = () => {
|
|
15784
15940
|
if (!paginated)
|
|
15785
15941
|
return null;
|
|
15786
|
-
return (jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-4 px-1", children: [jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [showPageSizeSelector && onPageSizeChange && (jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsx("span", { className: "text-sm text-ink-600", children: "Show:" }), jsxRuntime.jsx(Select, { options: pageSizeSelectOptions, value: String(pageSize), onChange: (value) => onPageSizeChange?.(Number(value)) })] })), jsxRuntime.jsx("span", { className: "text-sm text-ink-600", children: effectiveTotalItems > 0 ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: ["Showing ", (
|
|
15942
|
+
return (jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-4 px-1", children: [jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [showPageSizeSelector && onPageSizeChange && (jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsx("span", { className: "text-sm text-ink-600", children: "Show:" }), jsxRuntime.jsx(Select, { options: pageSizeSelectOptions, value: String(pageSize), onChange: (value) => onPageSizeChange?.(Number(value)) })] })), jsxRuntime.jsx("span", { className: "text-sm text-ink-600", children: effectiveTotalItems > 0 ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: ["Showing ", (currentPage - 1) * pageSize + 1, " -", " ", Math.min(currentPage * pageSize, effectiveTotalItems), " of", " ", effectiveTotalItems] })) : ("No items") })] }), totalPages > 1 && onPageChange && (jsxRuntime.jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: onPageChange }))] }));
|
|
15787
15943
|
};
|
|
15788
15944
|
// Determine if we should show card view
|
|
15789
|
-
const shouldShowCardView = mobileView ===
|
|
15790
|
-
(mobileView === 'auto' && isMobileViewport);
|
|
15945
|
+
const shouldShowCardView = mobileView === "card" || (mobileView === "auto" && isMobileViewport);
|
|
15791
15946
|
// Card view content
|
|
15792
15947
|
const cardViewContent = shouldShowCardView ? (jsxRuntime.jsx(DataTableCardView, { data: data, columns: visibleColumns, cardConfig: cardConfig, loading: loading, loadingRows: loadingRows, emptyMessage: emptyMessage, onCardClick: onRowClick, onCardLongPress: (item, event) => {
|
|
15793
15948
|
if (enableContextMenu && allActions.length > 0) {
|
|
15794
15949
|
event.preventDefault();
|
|
15795
|
-
const clientX =
|
|
15796
|
-
|
|
15950
|
+
const clientX = "touches" in event
|
|
15951
|
+
? event.touches[0].clientX
|
|
15952
|
+
: event.clientX;
|
|
15953
|
+
const clientY = "touches" in event
|
|
15954
|
+
? event.touches[0].clientY
|
|
15955
|
+
: event.clientY;
|
|
15797
15956
|
setContextMenuState({
|
|
15798
15957
|
isOpen: true,
|
|
15799
15958
|
position: { x: clientX, y: clientY },
|
|
@@ -15802,7 +15961,11 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
15802
15961
|
}
|
|
15803
15962
|
}, selectable: selectable, selectedRows: selectedRowsSet, onSelectionChange: onRowSelect ? (rows) => onRowSelect(rows) : undefined, keyExtractor: getRowKey, actions: allActions, onEdit: onEdit, onDelete: onDelete, className: className, cardClassName: cardClassName, gap: cardGap })) : null;
|
|
15804
15963
|
// Render with context menu
|
|
15805
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { role: "status", "aria-live": "polite", "aria-atomic": "true", className: "sr-only", children: announcement }), renderPaginationControls(), shouldShowCardView ? cardViewContent : finalContent, contextMenuState.isOpen && contextMenuState.item && (jsxRuntime.jsx(Menu, { items: convertActionsToMenuItems(contextMenuState.item), position: contextMenuState.position, onClose: () => setContextMenuState({
|
|
15964
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { role: "status", "aria-live": "polite", "aria-atomic": "true", className: "sr-only", children: announcement }), renderPaginationControls(), shouldShowCardView ? cardViewContent : finalContent, contextMenuState.isOpen && contextMenuState.item && (jsxRuntime.jsx(Menu, { items: convertActionsToMenuItems(contextMenuState.item), position: contextMenuState.position, onClose: () => setContextMenuState({
|
|
15965
|
+
isOpen: false,
|
|
15966
|
+
position: { x: 0, y: 0 },
|
|
15967
|
+
item: null,
|
|
15968
|
+
}) }))] }));
|
|
15806
15969
|
}
|
|
15807
15970
|
|
|
15808
15971
|
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|