UNPKG

aura-glass

Version:

A comprehensive glassmorphism design system for React applications with 142+ production-ready components

248 lines (245 loc) 9.84 kB
'use client'; import { jsx, jsxs } from 'react/jsx-runtime'; import { forwardRef, useMemo, createRef, useState, useEffect, useCallback } from 'react'; import '../../primitives/GlassCore.js'; import '../../primitives/glass/GlassAdvanced.js'; import { OptimizedGlassCore } from '../../primitives/OptimizedGlassCore.js'; import '../../primitives/glass/OptimizedGlassAdvanced.js'; import '../../primitives/MotionNative.js'; import '../../primitives/motion/MotionFramer.js'; import { cn } from '../../lib/utilsComprehensive.js'; import styles from './GlassDataGrid.module.css.js'; // Stub implementations for missing hooks const useSortableData = (data, sortConfig) => ({ sortedData: data, sortConfig, handleSort: () => {} }); const useDraggableListPhysics = options => ({ styles: {}, getHandlers: () => ({}), isDragging: false, draggedIndex: -1 }); const useVectorSpring = options => ({ start: () => {}, value: options?.initialValue || { x: 0, y: 0, z: 0 }, setValue: () => {} }); // Define the component using forwardRef const GlassDataGrid = /*#__PURE__*/forwardRef((props, ref) => { // TODO: Integrate ContrastGuard for table cells, list items, badges, card titles, and other text content for WCAG AA compliance const { data: initialData, columns, className, style, height, initialSort, enableRowDragging = false, onRowOrderChange } = props; const safeInitialData = Array.isArray(initialData) ? initialData : []; const safeColumns = Array.isArray(columns) ? columns : []; const { sortedData: unsortedData = safeInitialData, sortConfig, handleSort } = useSortableData(safeInitialData, initialSort); const normalizedSortedData = Array.isArray(unsortedData) ? unsortedData : safeInitialData; // Create refs for each row element for the physics hook const rowRefs = useMemo(() => Array.from({ length: normalizedSortedData?.length || 0 }, () => /*#__PURE__*/createRef()), [normalizedSortedData?.length]); // Need state for the order controlled by the hook const [renderOrder, setRenderOrder] = useState(() => Array.from({ length: normalizedSortedData.length }, (_, i) => i)); useEffect(() => { setRenderOrder(previous => { if (previous.length === normalizedSortedData.length) { return previous; } return Array.from({ length: normalizedSortedData.length }, (_, i) => i); }); }, [normalizedSortedData.length]); // Display data based on renderOrder const displayData = useMemo(() => (renderOrder || []).map(index => normalizedSortedData?.[index]).filter(row => row !== undefined), [normalizedSortedData, renderOrder]); // Callback for the hook to update our renderOrder useCallback(newOrderIndices => { setRenderOrder(newOrderIndices); if (onRowOrderChange) { // Map original data based on the new order of *original* indices const originalDataInNewOrder = newOrderIndices.map(originalIndex => safeInitialData?.[originalIndex]).filter(row => row !== undefined); onRowOrderChange(originalDataInNewOrder); } }, [onRowOrderChange, safeInitialData]); const { styles: rowStyles, getHandlers, isDragging: isAnyItemDragging, draggedIndex: draggedOriginalItemIndex } = useDraggableListPhysics(); // --- Sort Indicator Animation using useVectorSpring --- const sortIndicatorSpring = useVectorSpring({ config: { tension: 350, // Use tension/friction for config friction: 25 } // initialValue: { x: 0, y: 0, z: 0 } // Optional initial value }); // Map sorting state to target value for animation hook const sortTargetValue = useMemo(() => { if (!sortConfig) return 0; // Target 0 when not sorted return sortConfig.direction === 'asc' ? 1 : -1; // Target 1 for asc -1 for desc }, [sortConfig]); // Trigger animation when target changes useEffect(() => { // Set the target Y value of the vector spring sortIndicatorSpring.start(); }, [sortTargetValue, sortIndicatorSpring]); // --- End Sort Indicator Animation --- const handleHeaderKeyDown = event => { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); // handleSort would need to be updated to work without parameters // For now, we'll skip this functionality } }; const totalColumns = safeColumns.length + (enableRowDragging ? 1 : 0); const hasData = displayData.length > 0; if (!safeColumns.length) { return jsx(OptimizedGlassCore, { "data-glass-component": true, ref: ref, intent: "neutral", elevation: "level2", intensity: "medium", depth: 2, tint: "neutral", border: "subtle", animation: "none", performanceMode: "medium", className: cn('glass-w-full glass-p-6 glass-text-center', className), style: style, children: jsx("p", { className: "glass-text-sm glass-text-secondary", children: "No columns configured for this data grid." }) }); } return jsx(OptimizedGlassCore, { "data-glass-component": true, ref: ref, intent: "neutral", elevation: "level2", intensity: "medium", depth: 2, tint: "neutral", border: "subtle", animation: "none", performanceMode: "medium", className: cn('glass-w-full glass-overflow-hidden', height && 'glass-overflow-y-auto', className), style: { ...style, ...(height && { height: typeof height === 'number' ? `${height}px` : height }), perspective: '1000px' }, children: jsxs("table", { className: styles.table, children: [jsx("thead", { children: jsxs("tr", { className: styles.headerRow, children: [enableRowDragging && jsx("th", { className: cn(styles.headerCell, styles.dragHandleCell), "aria-hidden": "true" }), safeColumns.map(col => { // Determine if this column is the one being sorted const isSortingThisColumn = sortConfig?.key === col.key; const currentSortDirection = sortConfig && isSortingThisColumn ? sortConfig.direction : null; const isSortable = col.sortable; // Calculate indicator style based on animation value (from spring.value.y) const animValue = sortIndicatorSpring.value.y; // Get the animated value from the Y dimension const indicatorOpacity = Math.min(1, Math.abs(animValue) * 1.5); // Fade in/out const indicatorTranslateYPercent = -50 + animValue * -10; // Vertical movement const indicatorScale = 0.8 + Math.abs(animValue) * 0.2; // Scale effect const headerClassName = cn(styles.headerCell, isSortable && styles.headerSortable); return jsxs("th", { className: headerClassName, onClick: () => isSortable && handleSort(), tabIndex: isSortable ? 0 : -1, onKeyDown: e => handleHeaderKeyDown(e), role: "columnheader", "aria-sort": isSortable ? currentSortDirection === 'asc' ? 'ascending' : currentSortDirection === 'desc' ? 'descending' : 'none' : undefined, children: [col.header, jsx("span", { className: styles.sortIndicator, style: { opacity: indicatorOpacity, transform: `translateY(${indicatorTranslateYPercent}%) scale(${indicatorScale})` }, "aria-hidden": !isSortingThisColumn, children: sortTargetValue > 0 ? '▲' : '▼' })] }, col.id); })] }) }), jsx("tbody", { className: styles.body, children: hasData ? displayData.map((row, displayIndex) => { const originalIndex = renderOrder?.[displayIndex]; if (originalIndex === undefined) return null; const rowStyle = rowStyles?.[originalIndex] || {}; const handlers = enableRowDragging ? getHandlers() : { onPointerDown: () => {}, onKeyDown: () => {} }; const isDraggingThisRow = isAnyItemDragging && draggedOriginalItemIndex === originalIndex; const rowClassName = cn(styles.row, isDraggingThisRow && styles.rowDragging); return jsxs("tr", { ref: rowRefs?.[originalIndex], style: rowStyle, className: rowClassName, children: [enableRowDragging && jsx("td", { className: cn(styles.cell, styles.dragHandleCell), children: jsx("span", { ...handlers, tabIndex: 0, role: "button", "aria-label": `Drag row ${displayIndex + 1}`, "aria-grabbed": isDraggingThisRow, "data-drag-handle": "true", className: cn(styles.dragHandle, isDraggingThisRow && styles.dragHandleActive), children: "\u283F" }) }), safeColumns.map(col => jsx("td", { className: styles.cell, children: col.cellRenderer ? col.cellRenderer(row?.[col.accessorKey], row) : row?.[col.accessorKey] ?? col.placeholder ?? '—' }, `${col.id}-${originalIndex}`))] }, `row-${row?.id ?? originalIndex}`); }) : jsx("tr", { children: jsx("td", { className: styles.cell, colSpan: Math.max(1, totalColumns), children: jsx("div", { className: "glass-text-sm glass-text-secondary text-center", children: "No data available." }) }) }) })] }) }); }); // Add display name for better debugging GlassDataGrid.displayName = 'GlassDataGrid'; export { GlassDataGrid }; //# sourceMappingURL=GlassDataGrid.js.map