aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
195 lines (192 loc) • 9.17 kB
JavaScript
'use client';
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
import { useState, useMemo } from 'react';
import { GlassCore } from '../../../primitives/GlassCore.js';
import '../../../primitives/glass/GlassAdvanced.js';
import '../../../primitives/OptimizedGlassCore.js';
import '../../../primitives/glass/OptimizedGlassAdvanced.js';
import '../../../primitives/MotionNative.js';
import '../../../primitives/motion/MotionFramer.js';
import { cn } from '../../../lib/utilsComprehensive.js';
function GlassDataTable({
// TODO: Integrate ContrastGuard for table cells, list items, badges, card titles, and other text content for WCAG AA compliance
data = [],
columns = [],
loading = false,
searchable = true,
paginated = true,
pageSize = 10,
emptyMessage = "No data available",
loadingRows = 5,
className,
...props
}) {
const safeData = Array.isArray(data) ? data : [];
const safeColumns = Array.isArray(columns) ? columns : [];
const effectivePageSize = typeof pageSize === "number" && pageSize > 0 ? pageSize : safeData.length || 1;
const effectiveLoadingRows = typeof loadingRows === "number" && loadingRows > 0 ? loadingRows : 5;
const [sortColumn, setSortColumn] = useState(null);
const [sortDirection, setSortDirection] = useState("asc");
const [searchTerm, setSearchTerm] = useState("");
const [currentPage, setCurrentPage] = useState(1);
// Filter and sort data
const processedData = useMemo(() => {
let result = [...safeData];
// Search
if (searchTerm) {
result = result.filter(item => safeColumns.some(col => col.filterable !== false && String(item[col.key]).toLowerCase().includes(searchTerm.toLowerCase())));
}
// Sort
if (sortColumn) {
result.sort((a, b) => {
const aVal = a[sortColumn];
const bVal = b[sortColumn];
if (aVal < bVal) return sortDirection === "asc" ? -1 : 1;
if (aVal > bVal) return sortDirection === "asc" ? 1 : -1;
return 0;
});
}
return result;
}, [safeData, safeColumns, searchTerm, sortColumn, sortDirection]);
// Pagination
const totalPages = Math.max(1, Math.ceil(processedData.length / effectivePageSize));
const startIndex = (currentPage - 1) * effectivePageSize;
const paginatedData = paginated ? processedData.slice(startIndex, startIndex + effectivePageSize) : processedData;
const handleSort = columnKey => {
const column = safeColumns.find(col => col.key === columnKey);
if (!column?.sortable) return;
if (sortColumn === columnKey) {
setSortDirection(sortDirection === "asc" ? "desc" : "asc");
} else {
setSortColumn(columnKey);
setSortDirection("asc");
}
};
const renderLoadingSkeleton = () => jsx(Fragment, {
children: Array.from({
length: effectiveLoadingRows
}).map((_, index) => jsx("tr", {
className: 'animate-pulse',
children: safeColumns.map((column, colIndex) => jsx("td", {
className: "glass-p-4",
children: jsx("div", {
className: 'h-4 glass-surface-subtle glass-radius'
})
}, `loading-${index}-${colIndex}`))
}, `loading-${index}`))
});
const renderEmptyState = () => jsx("tr", {
children: jsx("td", {
colSpan: safeColumns.length || 1,
className: 'glass-p-12 text-center',
children: jsxs("div", {
className: "glass-flex glass-flex-col glass-items-center glass-gap-4 glass-text-secondary",
children: [jsx("div", {
className: 'text-6xl opacity-50',
children: "\uD83D\uDCED"
}), jsx("div", {
className: 'font-medium',
children: emptyMessage
}), searchTerm && jsx("button", {
onClick: () => setSearchTerm(""),
className: 'glass-px-4 glass-py-2 glass-surface-primary/10 hover:glass-surface-primary/20 glass-radius-lg text-primary glass-text-sm font-medium transition-colors glass-focus glass-touch-target glass-contrast-guard',
children: "Clear Search"
})]
})
})
});
const renderPagination = () => {
if (!paginated || totalPages <= 1) return null;
return jsxs("div", {
"data-glass-component": true,
className: "glass-flex glass-items-center glass-justify-between glass-p-4 glass-border-t glass-border-glass-border",
children: [jsxs("div", {
className: "glass-text-sm glass-text-secondary",
children: ["Showing ", startIndex + 1, " to", " ", Math.min(startIndex + effectivePageSize, processedData.length), " of", " ", processedData.length, " results"]
}), jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2",
children: [jsx("button", {
onClick: () => setCurrentPage(Math.max(1, currentPage - 1)),
disabled: currentPage === 1,
className: 'glass-px-3 glass-py-1 glass-radius-md glass-border glass-border-glass-border hover:glass-surface-subtle disabled:opacity-50 disabled:cursor-not-allowed transition-colors glass-focus glass-touch-target glass-contrast-guard',
children: "Previous"
}), Array.from({
length: Math.min(5, totalPages)
}).map((_, index) => {
const page = index + 1;
return jsx("button", {
onClick: () => setCurrentPage(page),
className: cn("glass-px-3 glass-py-1 glass-radius-md border transition-colors glass-focus glass-touch-target glass-contrast-guard", currentPage === page ? "bg-primary text-primary-foreground border-primary" : "border-border hover:bg-muted"),
children: page
}, page);
}), jsx("button", {
onClick: () => setCurrentPage(Math.min(totalPages, currentPage + 1)),
disabled: currentPage === totalPages,
className: 'glass-px-3 glass-py-1 glass-radius-md glass-border glass-border-glass-border hover:glass-surface-subtle disabled:opacity-50 disabled:cursor-not-allowed transition-colors glass-focus glass-touch-target glass-contrast-guard',
children: "Next"
})]
})]
});
};
return jsxs(GlassCore, {
className: cn("glass-radius-xl overflow-hidden", className),
...props,
children: [searchable && jsx("div", {
className: "glass-p-4 glass-border-b glass-border-glass-border",
children: jsxs("div", {
className: 'relative',
children: [jsx("input", {
type: "text",
placeholder: "Search...",
value: searchTerm,
onChange: e => {
setSearchTerm(e.target.value);
setCurrentPage(1);
},
className: 'glass-w-full glass-px-4 glass-py-2 glass-pl-10 bg-background glass-border glass-border-glass-border glass-radius-lg focus:outline-none focus:ring-2 focus:ring-primary/20 transition-colors glass-focus glass-touch-target glass-contrast-guard'
}), jsx("div", {
className: 'absolute left-3 glass-top-1/2 -translate-y-1/2 glass-text-secondary',
children: "\uD83D\uDD0D"
})]
})
}), jsx("div", {
className: 'overflow-x-auto',
children: jsxs("table", {
className: "glass-w-full",
children: [jsx("thead", {
className: "glass-surface-subtle",
children: jsx("tr", {
children: columns.map(column => jsx("th", {
className: cn("glass-px-4 glass-py-3 text-left text-sm font-medium glass-text-secondary", column.sortable && "cursor-pointer hover:bg-muted/80 transition-colors select-none", column.width && `w-${column.width}`),
onClick: () => handleSort(column.key),
children: jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2",
children: [column.label, column.sortable && jsxs("div", {
className: "glass-flex glass-flex-col",
children: [jsx("div", {
className: cn("text-xs transition-colors", sortColumn === column.key && sortDirection === "asc" ? "text-primary" : "glass-text-secondary/50"),
children: "\u25B2"
}), jsx("div", {
className: cn("text-xs transition-colors -mt-1", sortColumn === column.key && sortDirection === "desc" ? "text-primary" : "glass-text-secondary/50"),
children: "\u25BC"
})]
})]
})
}, String(column.key)))
})
}), jsx("tbody", {
children: loading ? renderLoadingSkeleton() : paginatedData.length === 0 ? renderEmptyState() : paginatedData.map((item, index) => jsx("tr", {
className: 'hover:glass-surface-subtle transition-colors glass-border-b glass-border-glass-border last:border-b-0',
children: columns.map(column => jsx("td", {
className: "glass-px-4 glass-py-3 glass-text-sm",
children: column.render ? column.render(item[column.key], item) : String(item[column.key])
}, String(column.key)))
}, index))
})]
})
}), renderPagination()]
});
}
GlassDataTable.displayName = "GlassDataTable";
export { GlassDataTable };
//# sourceMappingURL=GlassDataTable.js.map