@stackframe/dashboard-ui-components 2.8.88 → 2.8.91
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/button.d.ts +4 -4
- package/dist/components/data-grid/data-grid-sizing.d.ts +6 -5
- package/dist/components/data-grid/data-grid-sizing.d.ts.map +1 -1
- package/dist/components/data-grid/data-grid-sizing.js +9 -28
- package/dist/components/data-grid/data-grid-sizing.js.map +1 -1
- package/dist/components/data-grid/data-grid.d.ts +17 -237
- package/dist/components/data-grid/data-grid.d.ts.map +1 -1
- package/dist/components/data-grid/data-grid.js +377 -523
- package/dist/components/data-grid/data-grid.js.map +1 -1
- package/dist/components/data-grid/data-grid.test.js +82 -0
- package/dist/components/data-grid/data-grid.test.js.map +1 -1
- package/dist/components/data-grid/index.d.ts +4 -3
- package/dist/components/data-grid/index.js +17 -58
- package/dist/components/data-grid/state.d.ts +4 -61
- package/dist/components/data-grid/state.d.ts.map +1 -1
- package/dist/components/data-grid/state.js +13 -160
- package/dist/components/data-grid/state.js.map +1 -1
- package/dist/components/data-grid/types.d.ts +9 -4
- package/dist/components/data-grid/types.d.ts.map +1 -1
- package/dist/components/data-grid/use-url-state.d.ts +38 -0
- package/dist/components/data-grid/use-url-state.d.ts.map +1 -0
- package/dist/components/data-grid/use-url-state.js +214 -0
- package/dist/components/data-grid/use-url-state.js.map +1 -0
- package/dist/components/data-grid/use-url-state.test.d.ts +1 -0
- package/dist/components/data-grid/use-url-state.test.js +91 -0
- package/dist/components/data-grid/use-url-state.test.js.map +1 -0
- package/dist/components/dialog.d.ts +67 -0
- package/dist/components/dialog.d.ts.map +1 -0
- package/dist/components/dialog.js +94 -0
- package/dist/components/dialog.js.map +1 -0
- package/dist/dashboard-ui-components.global.js +10649 -6395
- package/dist/dashboard-ui-components.global.js.map +4 -4
- package/dist/esm/components/button.d.ts +4 -4
- package/dist/esm/components/data-grid/data-grid-sizing.d.ts +6 -5
- package/dist/esm/components/data-grid/data-grid-sizing.d.ts.map +1 -1
- package/dist/esm/components/data-grid/data-grid-sizing.js +7 -26
- package/dist/esm/components/data-grid/data-grid-sizing.js.map +1 -1
- package/dist/esm/components/data-grid/data-grid.d.ts +17 -237
- package/dist/esm/components/data-grid/data-grid.d.ts.map +1 -1
- package/dist/esm/components/data-grid/data-grid.js +380 -526
- package/dist/esm/components/data-grid/data-grid.js.map +1 -1
- package/dist/esm/components/data-grid/data-grid.test.js +82 -0
- package/dist/esm/components/data-grid/data-grid.test.js.map +1 -1
- package/dist/esm/components/data-grid/index.d.ts +4 -3
- package/dist/esm/components/data-grid/index.js +4 -3
- package/dist/esm/components/data-grid/state.d.ts +4 -61
- package/dist/esm/components/data-grid/state.d.ts.map +1 -1
- package/dist/esm/components/data-grid/state.js +15 -150
- package/dist/esm/components/data-grid/state.js.map +1 -1
- package/dist/esm/components/data-grid/types.d.ts +9 -4
- package/dist/esm/components/data-grid/types.d.ts.map +1 -1
- package/dist/esm/components/data-grid/use-url-state.d.ts +38 -0
- package/dist/esm/components/data-grid/use-url-state.d.ts.map +1 -0
- package/dist/esm/components/data-grid/use-url-state.js +212 -0
- package/dist/esm/components/data-grid/use-url-state.js.map +1 -0
- package/dist/esm/components/data-grid/use-url-state.test.d.ts +1 -0
- package/dist/esm/components/data-grid/use-url-state.test.js +91 -0
- package/dist/esm/components/data-grid/use-url-state.test.js.map +1 -0
- package/dist/esm/components/dialog.d.ts +67 -0
- package/dist/esm/components/dialog.d.ts.map +1 -0
- package/dist/esm/components/dialog.js +86 -0
- package/dist/esm/components/dialog.js.map +1 -0
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +2 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.js +37 -0
- package/package.json +4 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-grid.js","names":[],"sources":["../../../../src/components/data-grid/data-grid.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n ArrowDown,\n ArrowUp,\n CaretDown,\n CaretUp,\n CheckSquare,\n MinusSquare,\n Square,\n} from \"@phosphor-icons/react\";\nimport { throwErr } from \"@stackframe/stack-shared/dist/utils/errors\";\nimport { cn } from \"@stackframe/stack-ui\";\nimport { useVirtualizer, type VirtualItem } from \"@tanstack/react-virtual\";\nimport React, {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n} from \"react\";\n\nimport { DesignSkeleton } from \"../skeleton\";\nimport {\n applyDraggedColumnWidth,\n clampColumnWidth,\n createGridSizingStyle,\n getColumnSizingStyle,\n} from \"./data-grid-sizing\";\nimport { DataGridToolbar } from \"./data-grid-toolbar\";\nimport {\n clearSelection,\n exportToCsv,\n formatGridDate,\n getSortDirection,\n getSortIndex,\n isColumnVisible,\n resolveColumnValue,\n resolveColumnWidth,\n selectAll,\n toggleRowSelection,\n toggleSort,\n} from \"./state\";\nimport { resolveDataGridStrings } from \"./strings\";\nimport type {\n DataGridCellContext,\n DataGridColumnDef,\n DataGridDateDisplay,\n DataGridFooterContext,\n DataGridHeaderContext,\n DataGridPaginationMode,\n DataGridProps,\n DataGridState,\n DataGridStrings,\n DataGridToolbarContext,\n RowId\n} from \"./types\";\n// ─── Resize handle ───────────────────────────────────────────────────\n\nfunction ResizeHandle({\n onResize,\n onResizeEnd,\n}: {\n onResize: (delta: number) => void;\n onResizeEnd: () => void;\n}) {\n const startXRef = useRef(0);\n const rafRef = useRef(0);\n const latestDeltaRef = useRef(0);\n const callbacksRef = useRef({ onResize, onResizeEnd });\n\n callbacksRef.current = { onResize, onResizeEnd };\n\n const onPointerDown = useCallback(\n (e: React.PointerEvent) => {\n e.preventDefault();\n e.stopPropagation();\n startXRef.current = e.clientX;\n latestDeltaRef.current = 0;\n const el = e.currentTarget as HTMLElement;\n el.setPointerCapture(e.pointerId);\n let finished = false;\n\n const onMove = (ev: PointerEvent) => {\n latestDeltaRef.current = ev.clientX - startXRef.current;\n if (rafRef.current !== 0) {\n return;\n }\n\n rafRef.current = requestAnimationFrame(() => {\n rafRef.current = 0;\n callbacksRef.current.onResize(latestDeltaRef.current);\n });\n };\n const finish = () => {\n if (finished) return;\n finished = true;\n if (rafRef.current !== 0) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = 0;\n callbacksRef.current.onResize(latestDeltaRef.current);\n }\n el.removeEventListener(\"pointermove\", onMove);\n el.removeEventListener(\"pointerup\", finish);\n el.removeEventListener(\"pointercancel\", finish);\n el.removeEventListener(\"lostpointercapture\", finish);\n if (el.hasPointerCapture(e.pointerId)) {\n el.releasePointerCapture(e.pointerId);\n }\n callbacksRef.current.onResizeEnd();\n };\n\n el.addEventListener(\"pointermove\", onMove);\n el.addEventListener(\"pointerup\", finish);\n el.addEventListener(\"pointercancel\", finish);\n el.addEventListener(\"lostpointercapture\", finish);\n },\n [],\n );\n\n return (\n <div\n className={cn(\n \"absolute right-0 top-0 bottom-0 z-10 w-[5px] cursor-col-resize touch-none\",\n \"group-hover/header:bg-foreground/[0.06] hover:!bg-blue-500/30\",\n \"transition-colors duration-100\",\n )}\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n }}\n onPointerDown={onPointerDown}\n />\n );\n}\n\nfunction getNearestVerticalScrollElement(element: HTMLElement | null): HTMLElement | Window {\n let current = element?.parentElement ?? null;\n\n while (current) {\n const style = window.getComputedStyle(current);\n const overflowY = style.overflowY === \"visible\" ? style.overflow : style.overflowY;\n const canScrollVertically =\n (overflowY === \"auto\" || overflowY === \"scroll\" || overflowY === \"overlay\") &&\n current.scrollHeight > current.clientHeight + 1;\n\n if (canScrollVertically) {\n return current;\n }\n\n current = current.parentElement;\n }\n\n return window;\n}\n\nfunction getEventTargetElement(target: EventTarget | null): Element | null {\n if (target instanceof Element) {\n return target;\n }\n if (target instanceof Node) {\n return target.parentElement;\n }\n return null;\n}\n\nexport function isDataGridInteractiveRowClickTarget(target: EventTarget | null): boolean {\n const targetElement = getEventTargetElement(target);\n return targetElement?.closest([\n \"a\",\n \"button\",\n \"input\",\n \"select\",\n \"textarea\",\n \"[role=\\\"button\\\"]\",\n \"[role=\\\"menuitem\\\"]\",\n \"[contenteditable]:not([contenteditable=\\\"false\\\"])\",\n \"[data-no-row-click]\",\n ].join(\",\")) != null;\n}\n\nfunction shouldIgnoreRowClick(event: React.MouseEvent): boolean {\n return event.defaultPrevented || isDataGridInteractiveRowClickTarget(event.target);\n}\n\n// ─── Header cell ─────────────────────────────────────────────────────\n\nfunction HeaderCell<TRow>({\n col,\n isSorted,\n sortIndex,\n resizable,\n onSort,\n onResize,\n onResizeEnd,\n}: {\n col: DataGridColumnDef<TRow>;\n isSorted: false | \"asc\" | \"desc\";\n sortIndex: number | null;\n resizable: boolean;\n onSort: (columnId: string, multi: boolean) => void;\n onResize: (columnId: string, delta: number) => void;\n onResizeEnd: () => void;\n}) {\n const ctx: DataGridHeaderContext<TRow> = {\n columnId: col.id,\n columnDef: col,\n isSorted,\n sortIndex,\n };\n const label =\n typeof col.header === \"function\" ? col.header(ctx) : col.header;\n\n const sortable = col.sortable !== false;\n\n return (\n <div\n className={cn(\n \"group/header relative flex items-center gap-1.5 px-3 select-none bg-transparent overflow-hidden\",\n \"border-r border-black/[0.04] dark:border-white/[0.04] last:border-r-0\",\n sortable && \"cursor-pointer\",\n )}\n style={getColumnSizingStyle(col)}\n data-col-id={col.id}\n onClick={(e) => sortable && onSort(col.id, e.metaKey || e.ctrlKey)}\n role=\"columnheader\"\n aria-sort={isSorted === \"asc\" ? \"ascending\" : isSorted === \"desc\" ? \"descending\" : \"none\"}\n >\n <span\n className={cn(\n \"flex-1 min-w-0 truncate text-xs font-semibold uppercase tracking-wider text-muted-foreground\",\n col.align === \"center\" && \"text-center\",\n col.align === \"right\" && \"text-right\",\n )}\n >\n {label}\n </span>\n\n {/* Sort indicator */}\n {isSorted && (\n <span className=\"flex items-center gap-0.5 text-foreground/60\">\n {isSorted === \"asc\" ? (\n <ArrowUp className=\"h-3 w-3\" weight=\"bold\" />\n ) : (\n <ArrowDown className=\"h-3 w-3\" weight=\"bold\" />\n )}\n {sortIndex != null && (\n <span className=\"text-[10px] font-medium tabular-nums\">{sortIndex}</span>\n )}\n </span>\n )}\n\n {/* Unsorted hint on hover */}\n {!isSorted && sortable && (\n <span className=\"hidden group-hover/header:flex items-center text-foreground/20\">\n <CaretUp className=\"h-2.5 w-2.5 -mb-[1px]\" weight=\"bold\" />\n <CaretDown className=\"h-2.5 w-2.5 -mt-[1px]\" weight=\"bold\" />\n </span>\n )}\n\n {/* Resize handle */}\n {resizable && col.resizable !== false && (\n <ResizeHandle\n onResize={(delta) => onResize(col.id, delta)}\n onResizeEnd={onResizeEnd}\n />\n )}\n </div>\n );\n}\n\n// ─── Data cell ───────────────────────────────────────────────────────\n\nfunction DataCell<TRow>({\n col,\n row,\n rowId,\n rowIndex,\n isSelected,\n dateDisplay,\n}: {\n col: DataGridColumnDef<TRow>;\n row: TRow;\n rowId: RowId;\n rowIndex: number;\n isSelected: boolean;\n dateDisplay: DataGridDateDisplay;\n}) {\n const value = resolveColumnValue(col, row);\n const ctx: DataGridCellContext<TRow> = {\n row,\n rowId,\n rowIndex,\n value,\n columnId: col.id,\n isSelected,\n dateDisplay,\n };\n\n const isDateCol = col.type === \"date\" || col.type === \"dateTime\";\n let content: React.ReactNode;\n if (col.renderCell) {\n content = col.renderCell(ctx);\n } else if (isDateCol) {\n content = renderDateCell(value, dateDisplay, col);\n } else {\n content = formatCellValue(value);\n }\n const hasCellClick = col.onCellClick || col.onCellDoubleClick;\n\n const isWrap = col.cellOverflow === \"wrap\";\n\n return (\n <div\n className={cn(\n \"flex px-3 bg-transparent overflow-hidden\",\n \"border-r border-black/[0.04] dark:border-white/[0.04] last:border-r-0\",\n \"text-sm text-foreground\",\n isWrap ? \"items-start py-2\" : \"items-center\",\n col.align === \"center\" && \"justify-center\",\n col.align === \"right\" && \"justify-end\",\n hasCellClick && \"cursor-pointer\",\n )}\n style={getColumnSizingStyle(col)}\n data-col-id={col.id}\n role=\"gridcell\"\n onClick={col.onCellClick ? (e) => {\n e.stopPropagation();\n col.onCellClick!(ctx, e);\n } : undefined}\n onDoubleClick={col.onCellDoubleClick ? (e) => {\n e.stopPropagation();\n col.onCellDoubleClick!(ctx, e);\n } : undefined}\n >\n <div className={cn(\"min-w-0\", isWrap ? \"flex-1\" : \"truncate\")}>\n {content}\n </div>\n </div>\n );\n}\n\nfunction formatCellValue(value: unknown): React.ReactNode {\n if (value == null) return <span className=\"text-muted-foreground/40\">-</span>;\n if (typeof value === \"boolean\") {\n return (\n <span\n className={cn(\n \"inline-flex items-center px-1.5 py-0.5 rounded-md text-xs font-medium\",\n value\n ? \"bg-emerald-500/10 text-emerald-600 dark:text-emerald-400\"\n : \"bg-foreground/[0.04] text-muted-foreground\",\n )}\n >\n {value ? \"Yes\" : \"No\"}\n </span>\n );\n }\n if (value instanceof Date) {\n return (\n <span className=\"tabular-nums text-muted-foreground\">\n {value.toLocaleDateString()}\n </span>\n );\n }\n return <span className=\"truncate\">{String(value)}</span>;\n}\n\n/** Built-in date cell — mirrors what `formatGridDate` returns but wraps\n * the display in a `<span>` with a `title` tooltip showing the absolute\n * datetime. Only used when the column has `type: \"date\" | \"dateTime\"`\n * and no custom `renderCell`. */\nfunction renderDateCell<TRow>(\n value: unknown,\n dateDisplay: DataGridDateDisplay,\n col: DataGridColumnDef<TRow>,\n): React.ReactNode {\n const { display, tooltip } = formatGridDate(value, dateDisplay, {\n parseValue: col.parseValue,\n dateFormat: col.dateFormat,\n });\n if (display == null) return <span className=\"text-muted-foreground/40\">-</span>;\n return (\n <span\n className=\"tabular-nums text-muted-foreground truncate cursor-help\"\n title={tooltip ?? undefined}\n >\n {display}\n </span>\n );\n}\n\n// ─── Skeleton row ────────────────────────────────────────────────────\n\nfunction hashStringToInt(value: string): number {\n let hash = 0;\n for (let i = 0; i < value.length; i++) {\n hash = ((hash << 5) - hash + value.charCodeAt(i)) | 0;\n }\n return Math.abs(hash);\n}\n\nfunction SkeletonRow({\n columns,\n height,\n showCheckbox,\n}: {\n columns: readonly DataGridColumnDef<any>[];\n height: number;\n showCheckbox?: boolean;\n}) {\n return (\n <div className=\"flex\" style={{ height }} role=\"row\">\n {showCheckbox && (\n <div\n className=\"flex items-center justify-center border-r border-black/[0.04] dark:border-white/[0.04]\"\n style={{ width: 44 }}\n >\n <DesignSkeleton className=\"h-4 w-4 rounded\" />\n </div>\n )}\n {columns.map((col) => (\n <div\n key={col.id}\n className=\"flex items-center px-3 border-r border-black/[0.04] dark:border-white/[0.04] last:border-r-0\"\n style={getColumnSizingStyle(col)}\n >\n <DesignSkeleton\n className=\"h-3.5 rounded-md\"\n style={{ width: `${40 + (hashStringToInt(col.id) % 40)}%` }}\n />\n </div>\n ))}\n </div>\n );\n}\n\n// ─── Checkbox cell ───────────────────────────────────────────────────\n\nfunction SelectionCheckbox({\n checked,\n indeterminate,\n onChange,\n ariaLabel,\n}: {\n checked: boolean;\n indeterminate?: boolean;\n onChange: (event: React.MouseEvent<HTMLButtonElement>) => void;\n ariaLabel: string;\n}) {\n const Icon = indeterminate ? MinusSquare : checked ? CheckSquare : Square;\n return (\n <button\n className={cn(\n \"flex items-center justify-center w-full h-full\",\n \"hover:bg-foreground/[0.04] transition-colors duration-75\",\n checked || indeterminate\n ? \"text-blue-600 dark:text-blue-400\"\n : \"text-muted-foreground/40 hover:text-muted-foreground/60\",\n )}\n onClick={(e) => {\n e.stopPropagation();\n onChange(e);\n }}\n aria-label={ariaLabel}\n role=\"checkbox\"\n aria-checked={indeterminate ? \"mixed\" : checked}\n >\n <Icon className=\"h-4 w-4\" weight={checked || indeterminate ? \"fill\" : \"regular\"} />\n </button>\n );\n}\n\n// ─── Infinite scroll sentinel ────────────────────────────────────────\n\n// Stable module-level no-op so passing `onLoadMore ?? NOOP` to\n// InfiniteScrollSentinel doesn't allocate a fresh function every render\n// (which would re-trigger the sentinel's IntersectionObserver effect).\nconst NOOP = () => {};\n\nfunction InfiniteScrollSentinel({\n onIntersect,\n isLoading,\n rootRef,\n strings,\n}: {\n onIntersect: () => void;\n isLoading: boolean;\n rootRef?: React.RefObject<Element | null>;\n strings: DataGridStrings;\n}) {\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries[0]?.isIntersecting) {\n onIntersect();\n }\n },\n {\n root: rootRef?.current ?? null,\n rootMargin: \"200px\",\n },\n );\n observer.observe(el);\n return () => observer.disconnect();\n }, [onIntersect, rootRef]);\n\n return (\n <div ref={ref} className=\"flex items-center justify-center py-4\">\n {isLoading && (\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <div className=\"h-3 w-3 rounded-full border-2 border-current border-t-transparent animate-spin\" />\n {strings.loadingMore}\n </div>\n )}\n </div>\n );\n}\n\n// ─── Footer ──────────────────────────────────────────────────────────\n\nfunction DefaultFooter<TRow>({\n ctx,\n pagination,\n onChange,\n}: {\n ctx: DataGridFooterContext<TRow>;\n pagination: DataGridPaginationMode;\n onChange: React.Dispatch<React.SetStateAction<DataGridState>>;\n}) {\n const { state, totalRowCount, visibleRowCount, selectedRowCount, strings } = ctx;\n const totalPages = totalRowCount != null\n ? Math.max(1, Math.ceil(totalRowCount / state.pagination.pageSize))\n : undefined;\n\n const setPage = (pageIndex: number) =>\n onChange((s) => ({\n ...s,\n pagination: { ...s.pagination, pageIndex },\n }));\n\n const setPageSize = (pageSize: number) =>\n onChange((s) => ({\n ...s,\n pagination: { ...s.pagination, pageSize, pageIndex: 0 },\n }));\n\n return (\n <div className=\"flex items-center justify-between px-4 py-2.5 border-t border-foreground/[0.06] text-xs text-muted-foreground\">\n <div className=\"flex items-center gap-3\">\n {selectedRowCount > 0 && (\n <span className=\"font-medium text-foreground\">\n {strings.rowsSelected(selectedRowCount)}\n </span>\n )}\n {totalRowCount != null && (\n <span>\n {visibleRowCount} of {totalRowCount} rows\n </span>\n )}\n </div>\n\n {pagination !== \"infinite\" && totalPages != null && (\n <div className=\"flex items-center gap-3\">\n {/* Page size selector */}\n <div className=\"flex items-center gap-1.5\">\n <span>{strings.rowsPerPage}</span>\n <select\n className={cn(\n \"h-7 rounded-lg border border-black/[0.08] dark:border-white/[0.08] bg-background px-1.5\",\n \"text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-foreground/[0.1]\",\n \"cursor-pointer\",\n )}\n value={state.pagination.pageSize}\n onChange={(e) => setPageSize(Number(e.target.value))}\n >\n {[10, 25, 50, 100].map((size) => (\n <option key={size} value={size}>\n {size}\n </option>\n ))}\n </select>\n </div>\n\n {/* Page navigation */}\n <div className=\"flex items-center gap-1\">\n <button\n className={cn(\n \"h-7 w-7 flex items-center justify-center rounded-lg\",\n \"hover:bg-foreground/[0.04] disabled:opacity-30 disabled:cursor-not-allowed\",\n \"transition-colors duration-75\",\n )}\n onClick={() => setPage(state.pagination.pageIndex - 1)}\n disabled={state.pagination.pageIndex === 0}\n aria-label=\"Previous page\"\n >\n <CaretUp className=\"h-3.5 w-3.5 -rotate-90\" weight=\"bold\" />\n </button>\n <span className=\"px-2 tabular-nums font-medium\">\n {strings.pageOf(state.pagination.pageIndex + 1, totalPages)}\n </span>\n <button\n className={cn(\n \"h-7 w-7 flex items-center justify-center rounded-lg\",\n \"hover:bg-foreground/[0.04] disabled:opacity-30 disabled:cursor-not-allowed\",\n \"transition-colors duration-75\",\n )}\n onClick={() => setPage(state.pagination.pageIndex + 1)}\n disabled={state.pagination.pageIndex >= totalPages - 1}\n aria-label=\"Next page\"\n >\n <CaretDown className=\"h-3.5 w-3.5 -rotate-90\" weight=\"bold\" />\n </button>\n </div>\n </div>\n )}\n </div>\n );\n}\n\n// ─── Main DataGrid ───────────────────────────────────────────────────\n\n/**\n * Interactive table with sorting, quick search, pagination, selection,\n * and virtualization. Handles 10k+ rows smoothly. Pair with\n * `useDataSource` for client-side data; use an async `dataSource`\n * generator for server or infinite-scroll modes.\n *\n * ## Mental model (read this first — everything else depends on it)\n *\n * DataGrid is a **display** component. It does NOT sort, search, or\n * paginate your data directly — you own that, but `useDataSource` does\n * it for you. The `rows` prop is always the already-processed slice to\n * show. The grid tracks user intent in `state` (sort model, quick\n * search text, page index). You feed that state into `useDataSource`,\n * and its output goes back in as `rows`.\n *\n * `useDataSource` IS the processor. Given your full dataset and the\n * grid's state, it returns the searched + sorted + paginated rows\n * ready to pass to DataGrid. This is the ONLY correct pattern for\n * client-side data — do NOT pass a raw array to `rows`.\n *\n * ## Search (client vs async)\n *\n * - **Client mode** (`useDataSource` with `data`): a case-insensitive\n * substring match across every column is applied automatically.\n * Override the matcher with `matchRow` for fuzzy / weighted search,\n * or disable by passing `matchRow: () => true`.\n * - **Async mode** (`useDataSource` with `dataSource`): `state.quickSearch`\n * is forwarded to the generator as `params.quickSearch`. Same\n * mechanism as `params.sorting` — a change triggers a refetch, and\n * the generator is the \"matching logic\" (typically a WHERE / ILIKE\n * clause in the backend query). The grid does NO client-side\n * filtering in async mode.\n *\n * ## The canonical pattern\n *\n * ```tsx\n * // 1. Columns — define OUTSIDE the component or inside a useMemo. Must be stable.\n * const columns = React.useMemo(() => [\n * { id: \"name\", header: \"Name\", accessor: \"name\", width: 180, type: \"string\" },\n * { id: \"email\", header: \"Email\", accessor: \"email\", width: 240, type: \"string\" },\n * { id: \"role\", header: \"Role\", accessor: \"role\", width: 120, type: \"singleSelect\",\n * valueOptions: [{ value: \"admin\", label: \"Admin\" }, { value: \"member\", label: \"Member\" }] },\n * { id: \"signUps\", header: \"Sign-ups\", accessor: \"signUps\", width: 120, type: \"number\", align: \"right\",\n * renderCell: ({ value }) => <span className=\"tabular-nums\">{Number(value).toLocaleString()}</span> },\n * ], []);\n *\n * // 2. Grid state — one hook, initialized from the columns. NEVER build the state object by hand.\n * const [gridState, setGridState] = React.useState(() => createDefaultDataGridState(columns));\n *\n * // 3. Data source — wires your raw array through the grid state. ALWAYS call this\n * // hook unconditionally at the top level (no if/return before it).\n * const gridData = useDataSource({\n * data: users, // your raw array (can be [] while loading)\n * columns,\n * getRowId: (row) => row.id,\n * sorting: gridState.sorting,\n * quickSearch: gridState.quickSearch,\n * pagination: gridState.pagination,\n * paginationMode: \"client\", // \"client\" | \"server\" | \"infinite\"\n * });\n *\n * // 4. Render — `rows` comes from gridData.rows, NOT from your raw array.\n * <DataGrid\n * columns={columns}\n * rows={gridData.rows}\n * getRowId={(row) => row.id}\n * totalRowCount={gridData.totalRowCount}\n * isLoading={gridData.isLoading}\n * state={gridState}\n * onChange={setGridState}\n * selectionMode=\"none\" // \"none\" | \"single\" | \"multiple\"\n * maxHeight={480}\n * />\n * ```\n *\n * ## Iron rules (violating any of these breaks the grid)\n *\n * 1. The prop is `rows`, NOT `data`. There is no `data` prop on DataGrid.\n * `data` belongs on `useDataSource`.\n * 2. `rows` is ALWAYS `gridData.rows`. Never pass your raw array to\n * `rows` — the grid won't search, sort, or paginate it.\n * 3. Columns must be stable across renders. Define them outside the\n * component or wrap in `React.useMemo`. A fresh columns array every\n * render will reset sorting state.\n * 4. Initialize grid state with `createDefaultDataGridState(columns)`.\n * Do NOT spell out the state object manually — you will miss fields\n * and crash.\n * 5. `onChange` takes a `SetStateAction` (the setter you got from\n * `useState`). Pass `setGridState` directly. Do NOT wrap it unless\n * you know exactly what you're doing.\n * 6. Call `useDataSource` ONCE per grid, at the top level, before any\n * early return. It contains hooks.\n * 7. `renderCell` is a PURE function of its context. NEVER call React\n * hooks inside it (no `useState`, `useMemo`, `useEffect`, nothing).\n * If you need derived data per row, compute it BEFORE the render —\n * e.g. build a `Map<rowId, sparklineData>` in a `useMemo` and look\n * it up in `renderCell`.\n * 8. `toolbar` accepts `false` (hide it) or a render function\n * `(ctx) => ReactNode`. Anything else — `true`, `undefined`, a state\n * variable — will either show the default toolbar or crash. If you\n * just want the default toolbar, omit the prop entirely.\n * 9. The toolbar's search input writes to `state.quickSearch`. That\n * value is consumed by `useDataSource` — client mode filters\n * client-side, async mode forwards to the generator. Do NOT wire\n * a separate \"controlled\" search prop, everything flows through\n * grid state.\n *\n * ## renderCell — what you can and cannot do inside it\n *\n * ```tsx\n * // OK — pure rendering from ctx:\n * renderCell: ({ value }) => <span className=\"tabular-nums\">{Number(value).toLocaleString()}</span>\n * renderCell: ({ row }) => <Badge variant={row.active ? \"default\" : \"outline\"}>{row.status}</Badge>\n *\n * // OK — looking up pre-computed data by row id:\n * // BEFORE the return, in the component body:\n * const sparklinesById = React.useMemo(() => {\n * const m = new Map();\n * for (const u of users) {\n * m.set(u.id, u.recentActivity.map((n, i) => ({ ts: i, values: { primary: n } })));\n * }\n * return m;\n * }, [users]);\n * // Then inside the column def:\n * renderCell: ({ rowId }) => <MiniSparkline data={sparklinesById.get(rowId) ?? []} />\n *\n * // NOT OK — hooks inside renderCell:\n * renderCell: ({ row }) => {\n * const [hovered, setHovered] = React.useState(false); // ← crashes the grid\n * const data = React.useMemo(() => ..., []); // ← crashes the grid\n * return ...;\n * }\n *\n * // NOT OK — embedding AnalyticsChart (or any other controlled, stateful chart) per row:\n * // AnalyticsChart owns its own state, tooltips, zoom, and virtualized data\n * // pipeline. Instantiating one per row is expensive and fights the grid's\n * // virtualizer. Don't do it.\n * ```\n *\n * ## Sparklines and mini-charts in cells — use raw Recharts\n *\n * If you want a tiny chart (sparkline, micro bar chart, trend line) inside\n * a cell, drop down to raw `Recharts.*` components — they are lightweight\n * and stateless, so they render cleanly per row without owning any state.\n * Read pre-computed points off the row (or off a `Map<rowId, points>` you\n * built in a `useMemo` above) and pass them directly to the Recharts\n * primitive. Do NOT wrap them in `DesignChartContainer` or\n * `DesignChartCard` inside a cell — those add chrome meant for full-size\n * charts.\n *\n * ```tsx\n * // OK — raw Recharts sparkline per row:\n * renderCell: ({ rowId }) => {\n * const points = sparklinesById.get(rowId) ?? [];\n * return (\n * <Recharts.ResponsiveContainer width=\"100%\" height={28}>\n * <Recharts.LineChart data={points} margin={{ top: 2, right: 2, bottom: 2, left: 2 }}>\n * <Recharts.Line type=\"monotone\" dataKey=\"v\" stroke=\"currentColor\" strokeWidth={1.5} dot={false} isAnimationActive={false} />\n * </Recharts.LineChart>\n * </Recharts.ResponsiveContainer>\n * );\n * }\n * ```\n *\n * Keep in-cell Recharts configs minimal: no axes, no tooltips, no animation\n * (`isAnimationActive={false}`), tight margins, fixed height. The goal is a\n * visual summary, not an interactive chart.\n *\n * ## State shape (from `createDefaultDataGridState`)\n *\n * ```ts\n * {\n * sorting: [], // { columnId, direction: \"asc\" | \"desc\" }[]\n * quickSearch: \"\", // search input text\n * dateDisplay: \"relative\", // \"relative\" | \"absolute\"\n * columnVisibility: {}, columnWidths: {...},\n * columnPinning: { left: [], right: [] }, columnOrder: [...],\n * pagination: { pageIndex: 0, pageSize: 50 },\n * selection: { selectedIds: new Set(), anchorId: null },\n * }\n * ```\n *\n * Everything is updated through `setGridState` — the toolbar, header,\n * and footer all call it for you. You do not need to wire any of this\n * manually.\n *\n * ## Cell overflow and dynamic row heights\n *\n * By default every cell truncates its content with an ellipsis\n * (`cellOverflow: \"truncate\"`). For columns whose content should wrap\n * — badge lists, multi-line text, permission chips — set\n * `cellOverflow: \"wrap\"` on the column definition.\n *\n * To let rows grow to fit their tallest cell, set `rowHeight=\"auto\"`\n * on the grid. The virtualizer will measure each row after render and\n * adjust scroll positions accordingly. Pair with `estimatedRowHeight`\n * (default 44) for better scroll-position estimates before measurement.\n *\n * ```tsx\n * // Columns: UUIDs truncate, auth-method badges wrap\n * const columns = [\n * { id: \"userId\", header: \"User ID\", width: 130 }, // default truncate\n * { id: \"auth\", header: \"Auth methods\", width: 150, cellOverflow: \"wrap\",\n * renderCell: ({ row }) => (\n * <div className=\"flex flex-wrap gap-1\">\n * {row.authTypes.map((t) => <Badge key={t}>{t}</Badge>)}\n * </div>\n * ),\n * },\n * ];\n *\n * <DataGrid columns={columns} rowHeight=\"auto\" estimatedRowHeight={48} ... />\n * ```\n *\n * With a fixed numeric `rowHeight` (the default), `cellOverflow: \"wrap\"`\n * still lets content wrap within the row, but anything exceeding the\n * fixed height is clipped. This is useful when you want controlled\n * wrapping without variable row heights.\n *\n * ## Height and scrolling\n *\n * DataGrid is NOT a card. It has no border, rounded corners, or shadow of\n * its own. Wrap it in whatever chrome you want — a `DesignCard`, a section,\n * or just raw layout. The grid itself fills its parent's height via\n * `h-full`.\n *\n * How the grid gets its height (pick ONE):\n * 1. Bounded parent — put the grid inside a flex/grid container with a\n * definite height (e.g. `flex-1 min-h-0` inside a page-filling flex\n * column). The grid stretches to that height and scrolls its body.\n * 2. `maxHeight` prop — pass a number (pixels) or CSS string\n * (`\"480px\"`, `\"60vh\"`, `\"100%\"`). The grid caps at that size and\n * scrolls its body.\n * 3. Unbounded — omit `maxHeight` and let the parent grow freely. The\n * grid renders at its full content height and the page scrolls. Fine\n * for small lists; bad UX for thousands of rows.\n *\n * The toolbar, header, and footer are always `shrink-0`; only the body\n * scrolls. You do NOT need to subtract toolbar/footer heights from\n * `maxHeight` — the grid's internal flex layout handles that.\n *\n * ## When to use what\n *\n * - Simple static list, < 20 rows, no interaction → use a plain table component instead.\n * - Interactive table, sortable + searchable, any size → `DataGrid` +\n * `useDataSource` with `paginationMode: \"client\"`.\n * - Infinite scroll over a huge dataset you fetch in pages → `dataSource` async\n * generator + `paginationMode: \"infinite\"`. Only reach for this if you actually\n * need pagination over a remote source. For anything that fits in memory,\n * `\"client\"` is simpler and faster.\n *\n * ## Features you get for free\n *\n * Quick search, sortable columns (shift-click for multi-sort), column\n * visibility toggle, column resize, CSV export, virtualized rendering\n * for 10k+ rows, keyboard navigation, and a relative/absolute date\n * toggle for `date` / `dateTime` columns.\n */\nexport function DataGrid<TRow>(props: DataGridProps<TRow>) {\n const {\n columns: allColumns,\n rows,\n getRowId,\n totalRowCount,\n isLoading = false,\n isRefetching = false,\n hasMore = false,\n isLoadingMore = false,\n onLoadMore,\n state,\n onChange,\n paginationMode = \"paginated\",\n selectionMode = \"none\",\n resizable = true,\n rowHeight: rowHeightProp = 44,\n estimatedRowHeight: estimatedRowHeightProp,\n headerHeight = 44,\n overscan = 5,\n maxHeight,\n fillHeight = true,\n stickyTop,\n toolbar,\n toolbarExtra,\n emptyState,\n loadingState,\n footer,\n footerExtra,\n exportFilename = \"export\",\n strings: stringsOverride,\n className,\n // Callbacks\n onRowClick,\n onRowDoubleClick,\n onSelectionChange,\n onSortChange,\n } = props;\n\n const isDynamicRowHeight = rowHeightProp === \"auto\";\n const fixedRowHeight = isDynamicRowHeight ? undefined : rowHeightProp;\n const estimatedRowHeight = estimatedRowHeightProp ?? (fixedRowHeight ?? 44);\n\n const strings = useMemo(\n () => resolveDataGridStrings(stringsOverride),\n [stringsOverride],\n );\n\n // ── Visible columns ──────────────────────────────────────────\n const visibleColumns = useMemo(\n () =>\n (state.columnOrder.length > 0\n ? state.columnOrder\n .map((id) => allColumns.find((c) => c.id === id))\n .filter(Boolean) as DataGridColumnDef<TRow>[]\n : allColumns\n ).filter((col) => isColumnVisible(col.id, state.columnVisibility)),\n [allColumns, state.columnOrder, state.columnVisibility],\n );\n\n // ── Row IDs (stable) ─────────────────────────────────────────\n const rowIds = useMemo(() => rows.map(getRowId), [rows, getRowId]);\n\n // ── Column widths ────────────────────────────────────────────\n const visibleColumnMetrics = useMemo(() => {\n const widths = new Map<string, number>();\n let totalWidth = selectionMode !== \"none\" ? 44 : 0;\n\n for (const col of visibleColumns) {\n const width = resolveColumnWidth(col, state.columnWidths[col.id]);\n widths.set(col.id, width);\n totalWidth += width;\n }\n\n return { widths, totalWidth };\n }, [selectionMode, state.columnWidths, visibleColumns]);\n\n const gridSizingStyle = useMemo(\n () => createGridSizingStyle(visibleColumnMetrics.widths, visibleColumnMetrics.totalWidth),\n [visibleColumnMetrics],\n );\n\n // Resize drag tracked via ref — zero React re-renders during drag.\n // CSS variables on gridRef are mutated directly; committed on pointer up.\n const resizeRef = useRef<{ columnId: string; baseWidth: number; baseTotalWidth: number; latestWidth: number } | null>(null);\n const gridRef = useRef<HTMLDivElement>(null);\n\n // ── Handlers ─────────────────────────────────────────────────\n const handleSort = useCallback(\n (columnId: string, multi: boolean) => {\n const next = toggleSort(state.sorting, columnId, multi);\n onChange((s) => ({ ...s, sorting: next }));\n onSortChange?.(next);\n },\n [onChange, onSortChange, state.sorting],\n );\n\n const handleResize = useCallback(\n (columnId: string, delta: number) => {\n const col = allColumns.find((c) => c.id === columnId);\n if (!col) return;\n if (!resizeRef.current || resizeRef.current.columnId !== columnId) {\n const baseWidth = visibleColumnMetrics.widths.get(columnId) ?? resolveColumnWidth(col, state.columnWidths[columnId]);\n resizeRef.current = { columnId, baseWidth, baseTotalWidth: visibleColumnMetrics.totalWidth, latestWidth: baseWidth };\n }\n const newWidth = clampColumnWidth(col, resizeRef.current.baseWidth + delta);\n resizeRef.current.latestWidth = newWidth;\n if (gridRef.current) {\n applyDraggedColumnWidth(gridRef.current, columnId, newWidth, resizeRef.current.baseTotalWidth + (newWidth - resizeRef.current.baseWidth));\n }\n },\n [allColumns, state.columnWidths, visibleColumnMetrics],\n );\n\n // Re-apply CSS vars after React re-renders (e.g. sort during drag)\n useLayoutEffect(() => {\n const r = resizeRef.current;\n if (r && gridRef.current) {\n applyDraggedColumnWidth(gridRef.current, r.columnId, r.latestWidth, r.baseTotalWidth + (r.latestWidth - r.baseWidth));\n }\n }, [gridSizingStyle]);\n\n const handleResizeEnd = useCallback(() => {\n const r = resizeRef.current;\n resizeRef.current = null;\n if (!r || r.latestWidth === r.baseWidth) return;\n onChange((s) => ({ ...s, columnWidths: { ...s.columnWidths, [r.columnId]: r.latestWidth } }));\n }, [onChange]);\n\n const handleRowClick = useCallback(\n (row: TRow, rowId: RowId, event: React.MouseEvent) => {\n // Selection callbacks fire outside the updater (fixes strict-mode\n // double-invoke from the old `setTimeout` inside the reducer). Compute\n // from the current controlled prop instead of from inside the updater,\n // because React does not guarantee updater functions run synchronously.\n if (selectionMode !== \"none\") {\n const next = toggleRowSelection(\n state.selection,\n rowId,\n selectionMode,\n event.shiftKey,\n event.metaKey || event.ctrlKey,\n rowIds,\n );\n onChange((s) => ({ ...s, selection: next }));\n if (onSelectionChange) {\n const selectedRows = rows.filter((r) => next.selectedIds.has(getRowId(r)));\n onSelectionChange(next.selectedIds, selectedRows);\n }\n }\n\n onRowClick?.(row, rowId, event);\n },\n [selectionMode, onChange, onRowClick, onSelectionChange, rowIds, rows, getRowId, state.selection],\n );\n\n const handleRowSelectionCheckboxClick = useCallback(\n (\n row: TRow,\n rowId: RowId,\n event: React.MouseEvent<HTMLButtonElement>,\n ) => {\n handleRowClick(row, rowId, event);\n },\n [handleRowClick],\n );\n\n const handleSelectAll = useCallback(() => {\n const allSelectedNow = rowIds.every((id) => state.selection.selectedIds.has(id));\n const next = allSelectedNow ? clearSelection() : selectAll(rowIds);\n const selectedRows = allSelectedNow ? [] : rows;\n onChange((s) => ({ ...s, selection: next }));\n if (onSelectionChange) {\n onSelectionChange(next.selectedIds, [...selectedRows]);\n }\n }, [onChange, rowIds, rows, onSelectionChange, state.selection]);\n\n const handleExportCsv = useCallback(() => {\n exportToCsv(rows, visibleColumns, exportFilename);\n }, [rows, visibleColumns, exportFilename]);\n\n // ── Virtualizer ──────────────────────────────────────────────\n const scrollContainerRef = useRef<HTMLDivElement>(null);\n const headerScrollRef = useRef<HTMLDivElement>(null);\n const stickyChromeRef = useRef<HTMLDivElement>(null);\n const rowsClipRef = useRef<HTMLDivElement>(null);\n const measureElementFn = useCallback(\n (el: Element) => el.getBoundingClientRect().height,\n [],\n );\n // Key each virtual item by its row id (not index) so that when rows are\n // sorted / filtered, the virtualizer's measurement cache follows the row\n // rather than the slot. Matters specifically in dynamic-height mode —\n // otherwise a heavy row scrolling into slot-5 inherits slot-5's old\n // measurement until the browser re-measures.\n const rowVirtualizer = useVirtualizer({\n count: rows.length,\n getScrollElement: () => scrollContainerRef.current,\n estimateSize: () => estimatedRowHeight,\n overscan,\n getItemKey: (index) => {\n const row = rows[index];\n return row != null ? String(getRowId(row)) : index;\n },\n ...(isDynamicRowHeight ? { measureElement: measureElementFn } : {}),\n });\n\n // Composite ancestor backgrounds into a single opaque color so the\n // sticky header fully covers rows scrolling underneath. Handles\n // semi-transparent layers like `bg-white/80` by alpha-blending the\n // full ancestor chain. Re-runs on theme changes (class on <html>).\n useLayoutEffect(() => {\n const grid = gridRef.current;\n const stickyEl = stickyChromeRef.current;\n if (!grid || !stickyEl) return;\n\n const parseRgba = (raw: string): [number, number, number, number] | null => {\n const rgbaMatch = raw.match(/rgba?\\(\\s*([\\d.]+),\\s*([\\d.]+),\\s*([\\d.]+)(?:,\\s*([\\d.]+))?\\s*\\)/);\n if (!rgbaMatch) return null;\n // Alpha is an optional capture group so at runtime this may be\n // undefined, even though TS's types for `RegExp.match` say otherwise.\n const alphaRaw = rgbaMatch[4] as string | undefined;\n return [\n Number(rgbaMatch[1]),\n Number(rgbaMatch[2]),\n Number(rgbaMatch[3]),\n alphaRaw === undefined ? 1 : Number(alphaRaw),\n ];\n };\n\n const blendOver = (\n base: [number, number, number, number],\n top: [number, number, number, number],\n ): [number, number, number, number] => {\n const [tr, tg, tb, ta] = top;\n const [br, bg, bb, ba] = base;\n const outA = ta + ba * (1 - ta);\n if (outA === 0) return [0, 0, 0, 0];\n return [\n (tr * ta + br * ba * (1 - ta)) / outA,\n (tg * ta + bg * ba * (1 - ta)) / outA,\n (tb * ta + bb * ba * (1 - ta)) / outA,\n outA,\n ];\n };\n\n const detect = () => {\n const layers: [number, number, number, number][] = [];\n let ancestor: HTMLElement | null = grid.parentElement;\n while (ancestor) {\n const parsed = parseRgba(getComputedStyle(ancestor).backgroundColor);\n if (parsed && parsed[3] > 0) {\n layers.push(parsed);\n if (parsed[3] >= 1) break;\n }\n ancestor = ancestor.parentElement;\n }\n\n if (layers.length === 0) {\n stickyEl.style.backgroundColor = \"\";\n return;\n }\n\n // Blend bottom-up (deepest ancestor is the base)\n let result: [number, number, number, number] = layers[layers.length - 1]!;\n for (let i = layers.length - 2; i >= 0; i--) {\n result = blendOver(result, layers[i]!);\n }\n\n const [r, g, b] = result;\n stickyEl.style.backgroundColor = `rgb(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)})`;\n };\n\n detect();\n\n const observer = new MutationObserver(detect);\n observer.observe(document.documentElement, { attributes: true, attributeFilter: [\"class\"] });\n return () => observer.disconnect();\n }, []);\n\n // Hide row content scrolling behind the sticky chrome by clipping the\n // rows wrapper. Computes overlap = max(0, stickyBottom - wrapperTop)\n // in viewport coords and writes `clip-path: inset(<overlap>px 0 0 0)`\n // directly to the wrapper on every scroll/resize. Direct DOM writes\n // (no React state, no rAF) keep clip in lockstep with scroll so no\n // row content flashes through the sticky band for a frame.\n useLayoutEffect(() => {\n const gridEl = gridRef.current;\n const stickyEl = stickyChromeRef.current;\n const bodyEl = scrollContainerRef.current;\n const clipEl = rowsClipRef.current;\n if (!gridEl || !stickyEl || !bodyEl || !clipEl) return;\n\n const verticalScrollEl = fillHeight\n ? bodyEl\n : getNearestVerticalScrollElement(gridEl);\n let extraObservedScrollEl: HTMLElement | null = null;\n if (verticalScrollEl instanceof HTMLElement && verticalScrollEl !== bodyEl) {\n extraObservedScrollEl = verticalScrollEl;\n }\n\n const updateClip = () => {\n const stickyRect = stickyEl.getBoundingClientRect();\n const clipRect = clipEl.getBoundingClientRect();\n const overlap = Math.max(0, stickyRect.bottom - clipRect.top);\n const clipValue = overlap > 0 ? `inset(${overlap}px 0 0 0)` : \"\";\n const maskValue = overlap > 0\n ? `linear-gradient(to bottom, transparent 0px, transparent ${overlap}px, black ${overlap}px, black 100%)`\n : \"\";\n clipEl.style.clipPath = clipValue;\n clipEl.style.setProperty(\"-webkit-clip-path\", clipValue);\n clipEl.style.maskImage = maskValue;\n clipEl.style.setProperty(\"-webkit-mask-image\", maskValue);\n };\n\n updateClip();\n\n bodyEl.addEventListener(\"scroll\", updateClip);\n if (verticalScrollEl === window) {\n window.addEventListener(\"scroll\", updateClip, true);\n } else if (extraObservedScrollEl) {\n extraObservedScrollEl.addEventListener(\"scroll\", updateClip);\n }\n window.addEventListener(\"resize\", updateClip);\n const ro = new ResizeObserver(updateClip);\n ro.observe(gridEl);\n ro.observe(stickyEl);\n ro.observe(bodyEl);\n // NOTE: deliberately not observing `clipEl`. The effect writes\n // `clip-path`/`mask-image` to `clipEl`, so observing it creates a\n // feedback loop that thrashes height inside a bounded parent. `gridEl` /\n // `bodyEl` changing size already covers clip-relevant layout changes.\n if (extraObservedScrollEl) {\n ro.observe(extraObservedScrollEl);\n }\n\n return () => {\n bodyEl.removeEventListener(\"scroll\", updateClip);\n if (verticalScrollEl === window) {\n window.removeEventListener(\"scroll\", updateClip, true);\n } else if (extraObservedScrollEl) {\n extraObservedScrollEl.removeEventListener(\"scroll\", updateClip);\n }\n window.removeEventListener(\"resize\", updateClip);\n ro.disconnect();\n };\n }, [fillHeight]);\n\n // Sync horizontal scroll from body to header\n const handleBodyScroll = useCallback(() => {\n const body = scrollContainerRef.current;\n const header = headerScrollRef.current;\n if (body && header) {\n header.scrollLeft = body.scrollLeft;\n }\n }, []);\n\n // ── Toolbar context ──────────────────────────────────────────\n const toolbarCtx: DataGridToolbarContext<TRow> = useMemo(\n () => ({\n state,\n onChange,\n columns: allColumns,\n visibleColumns,\n totalRowCount,\n selectedRowCount: state.selection.selectedIds.size,\n strings,\n exportCsv: handleExportCsv,\n }),\n [state, onChange, allColumns, visibleColumns, totalRowCount, strings, handleExportCsv],\n );\n\n // ── Footer context ───────────────────────────────────────────\n const footerCtx: DataGridFooterContext<TRow> = useMemo(\n () => ({\n state,\n totalRowCount,\n visibleRowCount: rows.length,\n selectedRowCount: state.selection.selectedIds.size,\n paginationMode,\n strings,\n }),\n [state, totalRowCount, rows.length, paginationMode, strings],\n );\n\n // ── Selection state for header checkbox ──────────────────────\n const allSelected = rowIds.length > 0 && rowIds.every((id) => state.selection.selectedIds.has(id));\n const someSelected = !allSelected && rowIds.some((id) => state.selection.selectedIds.has(id));\n const infiniteScrollRootRef =\n paginationMode === \"infinite\" && (fillHeight || maxHeight != null)\n ? scrollContainerRef\n : undefined;\n\n // ── Render ───────────────────────────────────────────────────\n //\n // Height model:\n // - Root is `flex flex-col h-full min-h-0 bg-transparent`. `h-full`\n // makes the grid fill a bounded parent; in an unbounded parent it\n // resolves to `auto` and the grid takes the content's intrinsic size.\n // - Toolbar + header are wrapped in a single `sticky top-0` container\n // so they pin to the top of the nearest scroll ancestor. Footer is\n // `shrink-0`; the scroll body is `flex-1 min-h-0 overflow-auto`.\n // - `maxHeight` is applied directly to the root; the scroll body never\n // subtracts chrome sizes manually (that math breaks when the toolbar\n // wraps, the footer grows, etc.).\n // - `fillHeight={false}` uses `h-auto` and a non-growing scroll body so the grid\n // only occupies the height of its rows (no flex gap above sibling sections).\n return (\n <div\n ref={gridRef}\n className={cn(\n \"flex w-full min-w-0 max-w-full flex-col bg-transparent rounded-[calc(var(--radius)*2)]\",\n fillHeight ? \"min-h-0 h-full\" : \"min-h-0 h-auto\",\n className,\n )}\n style={maxHeight != null ? { ...gridSizingStyle, maxHeight } : gridSizingStyle}\n role=\"grid\"\n aria-rowcount={totalRowCount ?? rows.length}\n aria-colcount={visibleColumns.length}\n >\n {/* Sticky chrome: toolbar + header pin to the top of the nearest\n scroll ancestor so they remain visible while the body scrolls. */}\n <div\n ref={stickyChromeRef}\n className=\"sticky z-20 w-full min-w-0 shrink-0 rounded-t-[calc(var(--radius)*2)] bg-background\"\n style={{ top: stickyTop ?? \"var(--data-grid-sticky-top, 0px)\" }}\n >\n {/* Toolbar */}\n {toolbar !== false && (\n <div className=\"relative bg-transparent\">\n {toolbar ? (\n toolbar(toolbarCtx)\n ) : (\n <DataGridToolbar\n ctx={toolbarCtx}\n extra={\n typeof toolbarExtra === \"function\"\n ? toolbarExtra(toolbarCtx)\n : toolbarExtra\n }\n />\n )}\n </div>\n )}\n\n {/* Header row — syncs horizontal scroll with the body */}\n <div className=\"relative\">\n {isRefetching && (\n <div className=\"absolute top-0 left-0 right-0 h-0.5 z-30 bg-foreground/[0.04] overflow-hidden\">\n <div className=\"h-full w-1/3 bg-blue-500/60 rounded-full animate-pulse\" />\n </div>\n )}\n <div\n ref={headerScrollRef}\n className=\"w-full min-w-0 shrink-0 overflow-hidden border-b border-foreground/[0.06]\"\n >\n <div\n className=\"flex\"\n style={{ height: headerHeight, minWidth: visibleColumnMetrics.totalWidth }}\n role=\"row\"\n >\n {selectionMode !== \"none\" && (\n <div\n className=\"flex items-center justify-center border-r border-foreground/[0.04]\"\n style={{ width: 44 }}\n >\n {selectionMode === \"multiple\" && (\n <SelectionCheckbox\n checked={allSelected}\n indeterminate={someSelected}\n onChange={handleSelectAll}\n ariaLabel=\"Select all rows\"\n />\n )}\n </div>\n )}\n {visibleColumns.map((col) => (\n <HeaderCell\n key={col.id}\n col={col}\n isSorted={getSortDirection(state.sorting, col.id)}\n sortIndex={getSortIndex(state.sorting, col.id)}\n resizable={resizable}\n onSort={handleSort}\n onResize={handleResize}\n onResizeEnd={handleResizeEnd}\n />\n ))}\n </div>\n </div>\n </div>\n </div>\n\n {/* Scrollable body — flex-1 + min-h-0 when filling parent; flex-none when\n `fillHeight` is false so row stack height drives the grid (page scroll). */}\n <div\n ref={scrollContainerRef}\n className={cn(\n \"w-full min-w-0 overflow-auto bg-transparent\",\n fillHeight ? \"min-h-0 flex-1\" : \"flex-none\",\n \"[&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar]:h-1.5\",\n \"[&::-webkit-scrollbar-track]:bg-transparent\",\n \"[&::-webkit-scrollbar-thumb]:bg-foreground/[0.08] [&::-webkit-scrollbar-thumb]:rounded-full\",\n \"[&::-webkit-scrollbar-thumb]:hover:bg-foreground/[0.15]\",\n )}\n onScroll={handleBodyScroll}\n >\n {/* Clip wrapper — `clip-path` updated on scroll/resize so row\n content scrolling behind the sticky chrome is physically cut\n out instead of bleeding through. */}\n <div ref={rowsClipRef}>\n {/* Loading initial */}\n {isLoading && (\n <div style={{ minWidth: visibleColumnMetrics.totalWidth }}>\n {loadingState ??\n Array.from({ length: 8 }).map((_, i) => (\n <SkeletonRow\n key={i}\n columns={visibleColumns}\n height={estimatedRowHeight}\n showCheckbox={selectionMode !== \"none\"}\n />\n ))}\n </div>\n )}\n\n {/* Empty state */}\n {!isLoading && rows.length === 0 && (\n <div\n className=\"flex items-center justify-center py-16 text-sm text-muted-foreground\"\n style={{ minWidth: visibleColumnMetrics.totalWidth }}\n >\n {emptyState ?? strings.noData}\n </div>\n )}\n\n {/* Virtualized rows */}\n {!isLoading && rows.length > 0 && (\n <div\n style={{\n height: rowVirtualizer.getTotalSize(),\n width: \"100%\",\n minWidth: visibleColumnMetrics.totalWidth,\n position: \"relative\",\n }}\n >\n {rowVirtualizer.getVirtualItems().map((virtualRow: VirtualItem) => {\n const row = rows[virtualRow.index] ?? throwErr(\n `DataGrid: virtualized row index ${virtualRow.index} out of range (rows.length=${rows.length})`,\n );\n const rowId = getRowId(row);\n const isSelected = state.selection.selectedIds.has(rowId);\n\n const isOddRow = virtualRow.index % 2 === 1;\n return (\n <div\n key={rowId}\n ref={isDynamicRowHeight ? rowVirtualizer.measureElement : undefined}\n data-index={virtualRow.index}\n className={cn(\n \"absolute left-0 w-full flex\",\n \"border-b border-black/[0.03] dark:border-white/[0.03]\",\n \"transition-colors duration-75\",\n isSelected\n ? \"bg-blue-500/[0.06] dark:bg-blue-400/[0.08] hover:bg-blue-500/[0.08] dark:hover:bg-blue-400/[0.1]\"\n : isOddRow\n ? \"bg-foreground/[0.02] dark:bg-foreground/[0.03] hover:bg-foreground/[0.04] dark:hover:bg-foreground/[0.06]\"\n : \"hover:bg-foreground/[0.025] dark:hover:bg-foreground/[0.04]\",\n (selectionMode !== \"none\" || onRowClick) && \"cursor-pointer\",\n )}\n style={{\n ...(isDynamicRowHeight\n ? { minHeight: estimatedRowHeight }\n : { height: fixedRowHeight }),\n transform: `translateY(${virtualRow.start}px)`,\n }}\n onClick={(e) => {\n if (shouldIgnoreRowClick(e)) {\n return;\n }\n handleRowClick(row, rowId, e);\n }}\n onDoubleClick={(e) => {\n if (shouldIgnoreRowClick(e)) {\n return;\n }\n onRowDoubleClick?.(row, rowId, e);\n }}\n role=\"row\"\n aria-rowindex={virtualRow.index + 2}\n aria-selected={isSelected}\n data-row-id={rowId}\n data-state={isSelected ? \"selected\" : undefined}\n >\n {/* Selection checkbox */}\n {selectionMode !== \"none\" && (\n <div\n className=\"flex items-center justify-center border-r border-black/[0.04] dark:border-white/[0.04]\"\n style={{ width: 44 }}\n >\n <SelectionCheckbox\n checked={isSelected}\n onChange={(event) => handleRowSelectionCheckboxClick(row, rowId, event)}\n ariaLabel={`Select row ${rowId}`}\n />\n </div>\n )}\n\n {/* Data cells */}\n {visibleColumns.map((col) => (\n <DataCell\n key={col.id}\n col={col}\n row={row}\n rowId={rowId}\n rowIndex={virtualRow.index}\n isSelected={isSelected}\n dateDisplay={state.dateDisplay}\n />\n ))}\n </div>\n );\n })}\n </div>\n )}\n\n {/* Infinite scroll sentinel */}\n {paginationMode === \"infinite\" && hasMore && !isLoading && (\n <InfiniteScrollSentinel\n onIntersect={onLoadMore ?? NOOP}\n isLoading={isLoadingMore}\n rootRef={infiniteScrollRootRef}\n strings={strings}\n />\n )}\n </div>\n </div>\n\n {/* Footer */}\n {footer !== false && (\n <div className=\"relative z-10 shrink-0 bg-transparent\">\n {footer ? (\n footer(footerCtx)\n ) : (\n <DefaultFooter\n ctx={footerCtx}\n pagination={paginationMode}\n onChange={onChange}\n />\n )}\n {footerExtra && (\n typeof footerExtra === \"function\" ? footerExtra(footerCtx) : footerExtra\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"data-grid.js","names":[],"sources":["../../../../src/components/data-grid/data-grid.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n ArrowDown,\n ArrowUp,\n CaretDown,\n CaretUp,\n CheckSquare,\n MinusSquare,\n Square,\n} from \"@phosphor-icons/react\";\nimport { throwErr } from \"@stackframe/stack-shared/dist/utils/errors\";\nimport { cn } from \"@stackframe/stack-ui\";\nimport {\n type ColumnDef,\n type ColumnOrderState,\n type ColumnPinningState,\n type ColumnSizingState,\n type RowSelectionState,\n type SortingState,\n type VisibilityState,\n type Header,\n type Updater,\n getCoreRowModel,\n useReactTable,\n} from \"@tanstack/react-table\";\nimport { useVirtualizer, type VirtualItem } from \"@tanstack/react-virtual\";\nimport React, {\n type CSSProperties,\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport { DesignSkeleton } from \"../skeleton\";\nimport { DEFAULT_COL_WIDTH, clampColumnWidth, getEffectiveMaxWidth, getEffectiveMinWidth } from \"./data-grid-sizing\";\nimport { DataGridToolbar } from \"./data-grid-toolbar\";\nimport { exportToCsv, formatGridDate, resolveColumnValue } from \"./state\";\nimport { resolveDataGridStrings } from \"./strings\";\nimport type {\n DataGridCellContext,\n DataGridColumnDef,\n DataGridDateDisplay,\n DataGridFooterContext,\n DataGridHeaderContext,\n DataGridPaginationMode,\n DataGridProps,\n DataGridSelectionModel,\n DataGridSortItem,\n DataGridSortModel,\n DataGridState,\n DataGridStrings,\n DataGridToolbarContext,\n RowId,\n} from \"./types\";\n\n// ─── Row click target ────────────────────────────────────────────────\n\nfunction getEventTargetElement(target: EventTarget | null): Element | null {\n if (target instanceof Element) return target;\n if (target instanceof Node) return target.parentElement;\n return null;\n}\n\nexport function isDataGridInteractiveRowClickTarget(target: EventTarget | null): boolean {\n const el = getEventTargetElement(target);\n return el?.closest([\n \"a\",\n \"button\",\n \"input\",\n \"select\",\n \"textarea\",\n \"[role=\\\"button\\\"]\",\n \"[role=\\\"menuitem\\\"]\",\n \"[contenteditable]:not([contenteditable=\\\"false\\\"])\",\n \"[data-no-row-click]\",\n ].join(\",\")) != null;\n}\n\nfunction shouldIgnoreRowClick(event: React.MouseEvent): boolean {\n return event.defaultPrevented || isDataGridInteractiveRowClickTarget(event.target);\n}\n\n// ─── State translators (DataGridState ⇄ TanStack) ────────────────────\n\nfunction toTanstackSorting(sorting: DataGridSortModel): SortingState {\n return sorting.map((s) => ({ id: s.columnId, desc: s.direction === \"desc\" }));\n}\n\nfunction fromTanstackSorting(sorting: SortingState): DataGridSortModel {\n return sorting.map((s) => ({ columnId: s.id, direction: s.desc ? \"desc\" : \"asc\" }));\n}\n\nfunction toTanstackRowSelection(ids: ReadonlySet<RowId>): RowSelectionState {\n const out: RowSelectionState = {};\n for (const id of ids) out[id] = true;\n return out;\n}\n\nfunction resolveUpdater<T>(updater: Updater<T>, current: T): T {\n return typeof updater === \"function\" ? (updater as (old: T) => T)(current) : updater;\n}\n\n// ─── Flex column width distribution ──────────────────────────────────\n\nfunction distributeFlexWidths<TRow>(\n sizes: Record<string, number>,\n visibleColumns: readonly DataGridColumnDef<TRow>[],\n available: number,\n): void {\n const flexCols = visibleColumns.filter((c) => c.flex != null && c.flex > 0);\n if (flexCols.length === 0 || available <= 0) return;\n const totalFlex = flexCols.reduce((acc, c) => acc + (c.flex ?? 0), 0);\n let remaining = available;\n flexCols.forEach((col, i) => {\n const isLast = i === flexCols.length - 1;\n const share = isLast\n ? remaining\n : Math.floor(available * ((col.flex ?? 0) / totalFlex));\n const max = col.maxWidth ?? Infinity;\n const add = Math.max(0, Math.min(share, max - sizes[col.id]));\n sizes[col.id] += add;\n remaining -= add;\n });\n}\n\n// ─── Selection logic (with shift-range anchor) ───────────────────────\n\ntype SelectionInput = {\n current: DataGridSelectionModel;\n rowId: RowId;\n mode: \"single\" | \"multiple\";\n modifiers: { shift: boolean; ctrl: boolean };\n allRowIds: readonly RowId[];\n};\n\nfunction selectSingle(current: DataGridSelectionModel, rowId: RowId): DataGridSelectionModel {\n const isSelected = current.selectedIds.has(rowId);\n return {\n selectedIds: isSelected ? new Set() : new Set([rowId]),\n anchorId: isSelected ? null : rowId,\n };\n}\n\nfunction selectRange(\n current: DataGridSelectionModel,\n rowId: RowId,\n allRowIds: readonly RowId[],\n additive: boolean,\n): DataGridSelectionModel | null {\n if (current.anchorId == null) return null;\n const anchorIdx = allRowIds.indexOf(current.anchorId);\n const currentIdx = allRowIds.indexOf(rowId);\n if (anchorIdx < 0 || currentIdx < 0) return null;\n const start = Math.min(anchorIdx, currentIdx);\n const end = Math.max(anchorIdx, currentIdx);\n const next = additive ? new Set(current.selectedIds) : new Set<RowId>();\n for (let i = start; i <= end; i++) next.add(allRowIds[i]!);\n return { selectedIds: next, anchorId: current.anchorId };\n}\n\nfunction selectToggle(current: DataGridSelectionModel, rowId: RowId): DataGridSelectionModel {\n const next = new Set(current.selectedIds);\n if (next.has(rowId)) next.delete(rowId);\n else next.add(rowId);\n return { selectedIds: next, anchorId: rowId };\n}\n\nfunction nextSelection(input: SelectionInput): DataGridSelectionModel {\n const { current, rowId, mode, modifiers, allRowIds } = input;\n if (mode === \"single\") return selectSingle(current, rowId);\n if (modifiers.shift) {\n const range = selectRange(current, rowId, allRowIds, modifiers.ctrl);\n if (range != null) return range;\n }\n if (modifiers.ctrl) return selectToggle(current, rowId);\n return { selectedIds: new Set([rowId]), anchorId: rowId };\n}\n\n// ─── Header cell ─────────────────────────────────────────────────────\n\nfunction HeaderCell<TRow>({\n header,\n col,\n resizable,\n}: {\n header: Header<TRow, unknown>;\n col: DataGridColumnDef<TRow>;\n resizable: boolean;\n}) {\n const sorted = header.column.getIsSorted(); // false | \"asc\" | \"desc\"\n const sortIndex = header.column.getSortIndex();\n const totalSorts = header.column.getCanMultiSort()\n ? header.getContext().table.getState().sorting.length\n : 0;\n const ctx: DataGridHeaderContext<TRow> = {\n columnId: col.id,\n columnDef: col,\n isSorted: sorted === false ? false : sorted,\n sortIndex: totalSorts > 1 && sortIndex >= 0 ? sortIndex + 1 : null,\n };\n const label = typeof col.header === \"function\" ? col.header(ctx) : col.header;\n const sortable = header.column.getCanSort();\n const canResize = resizable && header.column.getCanResize();\n const isResizing = header.column.getIsResizing();\n\n return (\n <div\n className={cn(\n \"group/header relative flex items-center gap-1.5 px-3 select-none bg-transparent overflow-hidden\",\n \"border-r border-black/[0.04] dark:border-white/[0.04] last:border-r-0\",\n sortable && \"cursor-pointer\",\n )}\n style={{ width: `calc(var(--col-${col.id}-size) * 1px)` }}\n data-col-id={col.id}\n onClick={sortable ? header.column.getToggleSortingHandler() : undefined}\n role=\"columnheader\"\n aria-sort={sorted === \"asc\" ? \"ascending\" : sorted === \"desc\" ? \"descending\" : \"none\"}\n >\n <span\n className={cn(\n \"flex-1 min-w-0 truncate text-xs font-semibold uppercase tracking-wider text-muted-foreground\",\n col.align === \"center\" && \"text-center\",\n col.align === \"right\" && \"text-right\",\n )}\n >\n {label}\n </span>\n\n {sorted && (\n <span className=\"flex items-center gap-0.5 text-foreground/60\">\n {sorted === \"asc\"\n ? <ArrowUp className=\"h-3 w-3\" weight=\"bold\" />\n : <ArrowDown className=\"h-3 w-3\" weight=\"bold\" />}\n {ctx.sortIndex != null && (\n <span className=\"text-[10px] font-medium tabular-nums\">{ctx.sortIndex}</span>\n )}\n </span>\n )}\n\n {!sorted && sortable && (\n <span className=\"hidden group-hover/header:flex items-center text-foreground/20\">\n <CaretUp className=\"h-2.5 w-2.5 -mb-[1px]\" weight=\"bold\" />\n <CaretDown className=\"h-2.5 w-2.5 -mt-[1px]\" weight=\"bold\" />\n </span>\n )}\n\n {canResize && (\n <div\n onMouseDown={header.getResizeHandler()}\n onTouchStart={header.getResizeHandler()}\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n }}\n className={cn(\n \"absolute right-0 top-0 bottom-0 z-10 w-[5px] cursor-col-resize touch-none\",\n \"group-hover/header:bg-foreground/[0.06] hover:!bg-blue-500/30\",\n \"transition-colors duration-100\",\n isResizing && \"bg-blue-500/40\",\n )}\n />\n )}\n </div>\n );\n}\n\n// ─── Data cell ───────────────────────────────────────────────────────\n\nfunction DataCell<TRow>({\n col,\n row,\n rowId,\n rowIndex,\n isSelected,\n dateDisplay,\n}: {\n col: DataGridColumnDef<TRow>;\n row: TRow;\n rowId: RowId;\n rowIndex: number;\n isSelected: boolean;\n dateDisplay: DataGridDateDisplay;\n}) {\n const value = resolveColumnValue(col, row);\n const ctx: DataGridCellContext<TRow> = { row, rowId, rowIndex, value, columnId: col.id, isSelected, dateDisplay };\n\n const isDateCol = col.type === \"date\" || col.type === \"dateTime\";\n let content: React.ReactNode;\n if (col.renderCell) content = col.renderCell(ctx);\n else if (isDateCol) content = renderDateCell(value, dateDisplay, col);\n else content = formatCellValue(value);\n\n const hasCellClick = col.onCellClick || col.onCellDoubleClick;\n const isWrap = col.cellOverflow === \"wrap\";\n\n return (\n <div\n className={cn(\n \"flex px-3 bg-transparent overflow-hidden\",\n \"border-r border-black/[0.04] dark:border-white/[0.04] last:border-r-0\",\n \"text-sm text-foreground\",\n isWrap ? \"items-start py-2\" : \"items-center\",\n col.align === \"center\" && \"justify-center\",\n col.align === \"right\" && \"justify-end\",\n hasCellClick && \"cursor-pointer\",\n )}\n style={{ width: `calc(var(--col-${col.id}-size) * 1px)` }}\n data-col-id={col.id}\n role=\"gridcell\"\n onClick={col.onCellClick ? (e) => {\n e.stopPropagation();\n col.onCellClick!(ctx, e);\n } : undefined}\n onDoubleClick={col.onCellDoubleClick ? (e) => {\n e.stopPropagation();\n col.onCellDoubleClick!(ctx, e);\n } : undefined}\n >\n <div className={cn(\"min-w-0\", isWrap ? \"flex-1\" : \"truncate\")}>{content}</div>\n </div>\n );\n}\n\nfunction formatCellValue(value: unknown): React.ReactNode {\n if (value == null) return <span className=\"text-muted-foreground/40\">-</span>;\n if (typeof value === \"boolean\") {\n return (\n <span className={cn(\n \"inline-flex items-center px-1.5 py-0.5 rounded-md text-xs font-medium\",\n value\n ? \"bg-emerald-500/10 text-emerald-600 dark:text-emerald-400\"\n : \"bg-foreground/[0.04] text-muted-foreground\",\n )}>\n {value ? \"Yes\" : \"No\"}\n </span>\n );\n }\n if (value instanceof Date) {\n return <span className=\"tabular-nums text-muted-foreground\">{value.toLocaleDateString()}</span>;\n }\n return <span className=\"truncate\">{String(value)}</span>;\n}\n\nfunction renderDateCell<TRow>(\n value: unknown,\n dateDisplay: DataGridDateDisplay,\n col: DataGridColumnDef<TRow>,\n): React.ReactNode {\n const { display, tooltip } = formatGridDate(value, dateDisplay, {\n parseValue: col.parseValue,\n dateFormat: col.dateFormat,\n });\n if (display == null) return <span className=\"text-muted-foreground/40\">-</span>;\n return (\n <span className=\"tabular-nums text-muted-foreground truncate cursor-help\" title={tooltip ?? undefined}>\n {display}\n </span>\n );\n}\n\n// ─── Skeleton row ────────────────────────────────────────────────────\n\nfunction hashStringToInt(value: string): number {\n let hash = 0;\n for (let i = 0; i < value.length; i++) hash = ((hash << 5) - hash + value.charCodeAt(i)) | 0;\n return Math.abs(hash);\n}\n\nfunction SkeletonRow({\n columns,\n height,\n showCheckbox,\n}: {\n columns: readonly DataGridColumnDef<any>[];\n height: number;\n showCheckbox?: boolean;\n}) {\n return (\n <div className=\"flex\" style={{ height }} role=\"row\">\n {showCheckbox && (\n <div className=\"flex items-center justify-center border-r border-black/[0.04] dark:border-white/[0.04]\" style={{ width: 44 }}>\n <DesignSkeleton className=\"h-4 w-4 rounded\" />\n </div>\n )}\n {columns.map((col) => (\n <div\n key={col.id}\n className=\"flex items-center px-3 border-r border-black/[0.04] dark:border-white/[0.04] last:border-r-0\"\n style={{ width: `calc(var(--col-${col.id}-size) * 1px)` }}\n >\n <DesignSkeleton className=\"h-3.5 rounded-md\" style={{ width: `${40 + (hashStringToInt(col.id) % 40)}%` }} />\n </div>\n ))}\n </div>\n );\n}\n\n// ─── Selection checkbox ──────────────────────────────────────────────\n\nfunction SelectionCheckbox({\n checked,\n indeterminate,\n onChange,\n ariaLabel,\n title,\n}: {\n checked: boolean;\n indeterminate?: boolean;\n onChange: (event: React.MouseEvent<HTMLButtonElement>) => void;\n ariaLabel: string;\n title?: string;\n}) {\n const Icon = indeterminate ? MinusSquare : checked ? CheckSquare : Square;\n return (\n <button\n className={cn(\n \"flex items-center justify-center w-full h-full\",\n \"hover:bg-foreground/[0.04] transition-colors duration-75\",\n checked || indeterminate\n ? \"text-blue-600 dark:text-blue-400\"\n : \"text-muted-foreground/40 hover:text-muted-foreground/60\",\n )}\n onClick={(e) => {\n e.stopPropagation();\n onChange(e);\n }}\n aria-label={ariaLabel}\n title={title ?? ariaLabel}\n role=\"checkbox\"\n aria-checked={indeterminate ? \"mixed\" : checked}\n >\n <Icon className=\"h-4 w-4\" weight={checked || indeterminate ? \"fill\" : \"regular\"} />\n </button>\n );\n}\n\n// ─── Infinite scroll sentinel ────────────────────────────────────────\n\nconst NOOP = () => {};\n\nfunction InfiniteScrollSentinel({\n onIntersect,\n isLoading,\n rootRef,\n strings,\n}: {\n onIntersect: () => void;\n isLoading: boolean;\n rootRef?: React.RefObject<Element | null>;\n strings: DataGridStrings;\n}) {\n const ref = useRef<HTMLDivElement>(null);\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n const observer = new IntersectionObserver(\n (entries) => { if (entries[0]?.isIntersecting) onIntersect(); },\n { root: rootRef?.current ?? null, rootMargin: \"200px\" },\n );\n observer.observe(el);\n return () => observer.disconnect();\n }, [onIntersect, rootRef]);\n return (\n <div ref={ref} className=\"flex items-center justify-center py-4\">\n {isLoading && (\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <div className=\"h-3 w-3 rounded-full border-2 border-current border-t-transparent animate-spin\" />\n {strings.loadingMore}\n </div>\n )}\n </div>\n );\n}\n\n// ─── Footer ──────────────────────────────────────────────────────────\n\nfunction DefaultFooter<TRow>({\n ctx,\n pagination,\n onChange,\n}: {\n ctx: DataGridFooterContext<TRow>;\n pagination: DataGridPaginationMode;\n onChange: React.Dispatch<React.SetStateAction<DataGridState>>;\n}) {\n const { state, totalRowCount, visibleRowCount, selectedRowCount, strings } = ctx;\n const totalPages = totalRowCount != null\n ? Math.max(1, Math.ceil(totalRowCount / state.pagination.pageSize))\n : undefined;\n\n const setPage = (pageIndex: number) =>\n onChange((s) => ({ ...s, pagination: { ...s.pagination, pageIndex } }));\n const setPageSize = (pageSize: number) =>\n onChange((s) => ({ ...s, pagination: { ...s.pagination, pageSize, pageIndex: 0 } }));\n\n return (\n <div className=\"flex items-center justify-between px-4 py-2.5 border-t border-foreground/[0.06] text-xs text-muted-foreground\">\n <div className=\"flex items-center gap-3\">\n {selectedRowCount > 0 && (\n <span className=\"font-medium text-foreground\">{strings.rowsSelected(selectedRowCount)}</span>\n )}\n {totalRowCount != null && <span>{visibleRowCount} of {totalRowCount} rows</span>}\n </div>\n\n {pagination !== \"infinite\" && totalPages != null && (\n <div className=\"flex items-center gap-3\">\n <div className=\"flex items-center gap-1.5\">\n <span>{strings.rowsPerPage}</span>\n <select\n className={cn(\n \"h-7 rounded-lg border border-black/[0.08] dark:border-white/[0.08] bg-background px-1.5\",\n \"text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-foreground/[0.1]\",\n \"cursor-pointer\",\n )}\n value={state.pagination.pageSize}\n onChange={(e) => setPageSize(Number(e.target.value))}\n >\n {[10, 25, 50, 100].map((size) => <option key={size} value={size}>{size}</option>)}\n </select>\n </div>\n <div className=\"flex items-center gap-1\">\n <button\n className={cn(\n \"h-7 w-7 flex items-center justify-center rounded-lg\",\n \"hover:bg-foreground/[0.04] disabled:opacity-30 disabled:cursor-not-allowed\",\n \"transition-colors duration-75\",\n )}\n onClick={() => setPage(state.pagination.pageIndex - 1)}\n disabled={state.pagination.pageIndex === 0}\n aria-label=\"Previous page\"\n >\n <CaretUp className=\"h-3.5 w-3.5 -rotate-90\" weight=\"bold\" />\n </button>\n <span className=\"px-2 tabular-nums font-medium\">\n {strings.pageOf(state.pagination.pageIndex + 1, totalPages)}\n </span>\n <button\n className={cn(\n \"h-7 w-7 flex items-center justify-center rounded-lg\",\n \"hover:bg-foreground/[0.04] disabled:opacity-30 disabled:cursor-not-allowed\",\n \"transition-colors duration-75\",\n )}\n onClick={() => setPage(state.pagination.pageIndex + 1)}\n disabled={state.pagination.pageIndex >= totalPages - 1}\n aria-label=\"Next page\"\n >\n <CaretDown className=\"h-3.5 w-3.5 -rotate-90\" weight=\"bold\" />\n </button>\n </div>\n </div>\n )}\n </div>\n );\n}\n\n// ─── Main DataGrid ───────────────────────────────────────────────────\n\n/**\n * Interactive table built on TanStack Table v8. Sorting, column sizing,\n * visibility, ordering, and pinning are owned by the table instance; we\n * layer virtualization, sticky toolbar/header/footer, infinite scroll,\n * quick search, CSV export, and date-format toggling on top.\n *\n * The grid is display-only — it does not fetch or page data itself. Pair\n * with `useDataSource` for client- or server-side data and pass the\n * already-processed slice through `rows`.\n *\n * ```tsx\n * const columns = useMemo(() => [...], []);\n * const [gridState, setGridState] = useState(() => createDefaultDataGridState(columns));\n * const gridData = useDataSource({\n * data: users, columns, getRowId: (r) => r.id,\n * sorting: gridState.sorting,\n * quickSearch: gridState.quickSearch,\n * pagination: gridState.pagination,\n * paginationMode: \"client\",\n * });\n *\n * <DataGrid\n * columns={columns}\n * rows={gridData.rows}\n * getRowId={(r) => r.id}\n * totalRowCount={gridData.totalRowCount}\n * isLoading={gridData.isLoading}\n * state={gridState}\n * onChange={setGridState}\n * />\n * ```\n *\n * Iron rules:\n * - `rows` is always `gridData.rows`, never your raw array.\n * - Columns must be stable (define outside the component or wrap in `useMemo`).\n * - Initialize state with `createDefaultDataGridState(columns)`.\n * - `renderCell` must be a pure function — no React hooks inside.\n */\nexport function DataGrid<TRow>(props: DataGridProps<TRow>) {\n const {\n columns: allColumns,\n rows,\n getRowId,\n totalRowCount,\n isLoading = false,\n isRefetching = false,\n hasMore = false,\n isLoadingMore = false,\n onLoadMore,\n state,\n onChange,\n paginationMode = \"paginated\",\n selectionMode = \"none\",\n resizable = true,\n rowHeight: rowHeightProp = 44,\n estimatedRowHeight: estimatedRowHeightProp,\n headerHeight = 44,\n overscan = 5,\n maxHeight,\n fillHeight = true,\n stickyTop,\n toolbar,\n toolbarExtra,\n emptyState,\n loadingState,\n footer,\n footerExtra,\n exportFilename = \"export\",\n strings: stringsOverride,\n className,\n onRowClick,\n onRowDoubleClick,\n onSelectionChange,\n onSortChange,\n onColumnResize,\n onColumnVisibilityChange,\n } = props;\n\n const isDynamicRowHeight = rowHeightProp === \"auto\";\n const fixedRowHeight = isDynamicRowHeight ? undefined : rowHeightProp;\n const estimatedRowHeight = estimatedRowHeightProp ?? (fixedRowHeight ?? 44);\n\n const strings = useMemo(() => resolveDataGridStrings(stringsOverride), [stringsOverride]);\n\n // ── Build TanStack column defs from our column defs ──────────\n const tableColumns = useMemo<ColumnDef<TRow>[]>(\n () =>\n allColumns.map((col) => ({\n id: col.id,\n accessorFn: (row) => resolveColumnValue(col, row),\n header: typeof col.header === \"string\" ? col.header : col.id,\n size: col.width ?? DEFAULT_COL_WIDTH,\n minSize: getEffectiveMinWidth(col),\n maxSize: getEffectiveMaxWidth(col),\n enableSorting: col.sortable !== false,\n enableHiding: col.hideable !== false,\n enableResizing: col.resizable !== false,\n enableMultiSort: true,\n })),\n [allColumns],\n );\n\n // ── Translate our state ⇄ TanStack state via change handlers ───\n const tanstackSorting = useMemo(() => toTanstackSorting(state.sorting), [state.sorting]);\n const tanstackRowSelection = useMemo(\n () => toTanstackRowSelection(state.selection.selectedIds),\n [state.selection.selectedIds],\n );\n // ColumnSizing/Visibility/Order/Pinning share the same shape with TanStack.\n const tanstackColumnPinning = useMemo<ColumnPinningState>(\n () => ({ left: [...state.columnPinning.left], right: [...state.columnPinning.right] }),\n [state.columnPinning],\n );\n const tanstackColumnOrder = useMemo<ColumnOrderState>(\n () => [...state.columnOrder],\n [state.columnOrder],\n );\n\n const allColumnsRef = useRef(allColumns);\n allColumnsRef.current = allColumns;\n\n const handleSortingChange = useCallback(\n (updater: Updater<SortingState>) => {\n const next = resolveUpdater(updater, toTanstackSorting(state.sorting));\n const ours: DataGridSortItem[] = fromTanstackSorting(next).map((s) => ({ ...s }));\n // Reset to page 0 — page N of the new sort order is meaningless when\n // the order itself changed, and would silently scroll past relevant\n // rows.\n onChange((s) => ({\n ...s,\n sorting: ours,\n pagination: { ...s.pagination, pageIndex: 0 },\n }));\n onSortChange?.(ours);\n },\n [onChange, onSortChange, state.sorting],\n );\n\n const handleColumnSizingChange = useCallback(\n (updater: Updater<ColumnSizingState>) => {\n const next = resolveUpdater(updater, state.columnWidths);\n // Clamp each new width to our canvas-measured min and explicit max.\n const clamped: Record<string, number> = {};\n for (const [id, w] of Object.entries(next)) {\n const col = allColumnsRef.current.find((c) => c.id === id);\n clamped[id] = col ? clampColumnWidth(col, w) : w;\n }\n onChange((s) => ({ ...s, columnWidths: clamped }));\n // Fire onColumnResize for any column whose width changed.\n if (onColumnResize) {\n for (const [id, w] of Object.entries(clamped)) {\n if (state.columnWidths[id] !== w) onColumnResize(id, w);\n }\n }\n },\n [onChange, onColumnResize, state.columnWidths],\n );\n\n const handleVisibilityChange = useCallback(\n (updater: Updater<VisibilityState>) => {\n const next = resolveUpdater(updater, state.columnVisibility);\n onChange((s) => ({ ...s, columnVisibility: next }));\n onColumnVisibilityChange?.(next);\n },\n [onChange, onColumnVisibilityChange, state.columnVisibility],\n );\n\n const handleColumnOrderChange = useCallback(\n (updater: Updater<ColumnOrderState>) => {\n const next = resolveUpdater(updater, [...state.columnOrder]);\n onChange((s) => ({ ...s, columnOrder: next }));\n },\n [onChange, state.columnOrder],\n );\n\n const handleColumnPinningChange = useCallback(\n (updater: Updater<ColumnPinningState>) => {\n const current: ColumnPinningState = {\n left: [...state.columnPinning.left],\n right: [...state.columnPinning.right],\n };\n const next = resolveUpdater(updater, current);\n onChange((s) => ({\n ...s,\n columnPinning: { left: next.left ?? [], right: next.right ?? [] },\n }));\n },\n [onChange, state.columnPinning],\n );\n\n // ── TanStack Table instance ──────────────────────────────────\n const table = useReactTable<TRow>({\n data: rows as TRow[],\n columns: tableColumns,\n getRowId: (row) => getRowId(row),\n getCoreRowModel: getCoreRowModel(),\n state: {\n sorting: tanstackSorting,\n columnVisibility: state.columnVisibility,\n columnSizing: state.columnWidths,\n columnOrder: tanstackColumnOrder,\n columnPinning: tanstackColumnPinning,\n rowSelection: tanstackRowSelection,\n },\n onSortingChange: handleSortingChange,\n onColumnSizingChange: handleColumnSizingChange,\n onColumnVisibilityChange: handleVisibilityChange,\n onColumnOrderChange: handleColumnOrderChange,\n onColumnPinningChange: handleColumnPinningChange,\n columnResizeMode: \"onEnd\",\n enableRowSelection: selectionMode !== \"none\",\n enableMultiRowSelection: selectionMode === \"multiple\",\n enableColumnResizing: resizable,\n manualSorting: true,\n manualPagination: true,\n manualFiltering: true,\n });\n\n // ── Visible columns (in TanStack-resolved order, after visibility) ──\n const visibleColumns = useMemo(() => {\n const colMap = new Map(allColumns.map((c) => [c.id, c]));\n return table\n .getVisibleLeafColumns()\n .map((c) => colMap.get(c.id))\n .filter(Boolean) as DataGridColumnDef<TRow>[];\n }, [allColumns, table, state.columnOrder, state.columnVisibility]);\n\n // ── Row IDs (stable across this render) ──────────────────────\n const rowIds = useMemo(() => rows.map(getRowId), [rows, getRowId]);\n\n // ── Container width tracking (for `flex` column distribution) ─\n // Measure the scroll container's clientWidth, which excludes the vertical\n // scrollbar — using the outer grid would leave a few pixels of phantom\n // horizontal scroll when rows overflow.\n const [containerWidth, setContainerWidth] = useState(0);\n useLayoutEffect(() => {\n const grid = gridRef.current;\n const scroller = scrollContainerRef.current;\n if (!grid) return;\n const update = () => {\n const w = scroller?.clientWidth ?? grid.clientWidth;\n if (w > 0) setContainerWidth(w);\n };\n update();\n const observer = new ResizeObserver(update);\n observer.observe(grid);\n if (scroller) observer.observe(scroller);\n return () => observer.disconnect();\n }, []);\n\n // ── Column width CSS variables (TanStack pattern) ────────────\n // With `columnResizeMode: \"onEnd\"`, live drag width comes from deltaOffset; committed sizes update on pointer-up.\n const columnSizingInfo = table.getState().columnSizingInfo;\n const columnSizes = useMemo<Record<string, number>>(() => {\n const sizes: Record<string, number> = {};\n let baseTotal = selectionMode !== \"none\" ? 44 : 0;\n const resizingId = columnSizingInfo.isResizingColumn || null;\n const deltaOffset = columnSizingInfo.deltaOffset ?? 0;\n for (const col of visibleColumns) {\n const tsCol = table.getColumn(col.id);\n const baseSize = tsCol?.getSize() ?? col.width ?? DEFAULT_COL_WIDTH;\n const liveSize = resizingId === col.id\n ? clampColumnWidth(col, baseSize + deltaOffset)\n : baseSize;\n sizes[col.id] = liveSize;\n baseTotal += liveSize;\n }\n distributeFlexWidths(sizes, visibleColumns, containerWidth - baseTotal);\n return sizes;\n }, [visibleColumns, table, columnSizingInfo, state.columnWidths, containerWidth, selectionMode]);\n\n const totalContentWidth = useMemo(() => {\n let total = selectionMode !== \"none\" ? 44 : 0;\n for (const col of visibleColumns) total += columnSizes[col.id] ?? 0;\n return total;\n }, [visibleColumns, columnSizes, selectionMode]);\n\n const cssVars = useMemo<CSSProperties>(() => {\n const vars: Record<string, string | number> = { \"--grid-total-w\": `${totalContentWidth}px` };\n for (const col of visibleColumns) {\n vars[`--col-${col.id}-size`] = columnSizes[col.id];\n }\n return vars as CSSProperties;\n }, [visibleColumns, columnSizes, totalContentWidth]);\n\n // ── Selection handlers ───────────────────────────────────────\n const fireSelection = useCallback(\n (next: DataGridSelectionModel) => {\n onChange((s) => ({ ...s, selection: next }));\n if (onSelectionChange) {\n const idSet = next.selectedIds;\n const selectedRows = rows.filter((r) => idSet.has(getRowId(r)));\n onSelectionChange(idSet, selectedRows);\n }\n },\n [onChange, onSelectionChange, rows, getRowId],\n );\n\n const handleRowClick = useCallback(\n (row: TRow, rowId: RowId, event: React.MouseEvent) => {\n if (selectionMode !== \"none\") {\n const next = nextSelection({\n current: state.selection,\n rowId,\n mode: selectionMode,\n modifiers: { shift: event.shiftKey, ctrl: event.metaKey || event.ctrlKey },\n allRowIds: rowIds,\n });\n fireSelection(next);\n }\n onRowClick?.(row, rowId, event);\n },\n [selectionMode, state.selection, rowIds, fireSelection, onRowClick],\n );\n\n const handleSelectAll = useCallback(() => {\n const allSelectedNow = rowIds.length > 0 && rowIds.every((id) => state.selection.selectedIds.has(id));\n fireSelection(\n allSelectedNow\n ? { selectedIds: new Set(), anchorId: null }\n : { selectedIds: new Set(rowIds), anchorId: null },\n );\n }, [rowIds, state.selection.selectedIds, fireSelection]);\n\n // ── CSV export ───────────────────────────────────────────────\n // The grid only knows about rows currently in memory (the visible page in\n // paginated mode, or the loaded prefix in infinite mode). To avoid users\n // assuming \"Export CSV\" means \"everything that exists on the server\", we\n // confirm with the loaded-row count before downloading. Consumers that\n // want true full-dataset export can override this via a parent toolbar.\n const handleExportCsv = useCallback(() => {\n if (typeof window !== \"undefined\" && rows.length > 0) {\n const totalSuffix = totalRowCount != null && totalRowCount > rows.length\n ? ` of ${totalRowCount} total — load more rows first to include them`\n : \"\";\n const confirmed = window.confirm(\n `Export ${rows.length.toLocaleString()} loaded row${rows.length === 1 ? \"\" : \"s\"}${totalSuffix}?`,\n );\n if (!confirmed) return;\n }\n exportToCsv(rows, visibleColumns, exportFilename);\n }, [rows, visibleColumns, exportFilename, totalRowCount]);\n\n // ── Virtualizer ──────────────────────────────────────────────\n const scrollContainerRef = useRef<HTMLDivElement>(null);\n const headerScrollRef = useRef<HTMLDivElement>(null);\n const stickyChromeRef = useRef<HTMLDivElement>(null);\n const rowsClipRef = useRef<HTMLDivElement>(null);\n const gridRef = useRef<HTMLDivElement>(null);\n const measureElementFn = useCallback((el: Element) => el.getBoundingClientRect().height, []);\n\n const rowVirtualizer = useVirtualizer({\n count: rows.length,\n getScrollElement: () => scrollContainerRef.current,\n estimateSize: () => estimatedRowHeight,\n overscan,\n getItemKey: (index) => {\n const row = rows[index];\n return row != null ? String(getRowId(row)) : index;\n },\n ...(isDynamicRowHeight ? { measureElement: measureElementFn } : {}),\n });\n\n // ── Sticky chrome clipping ───────────────────────────────────\n useLayoutEffect(() => {\n const gridEl = gridRef.current;\n const stickyEl = stickyChromeRef.current;\n const bodyEl = scrollContainerRef.current;\n const clipEl = rowsClipRef.current;\n if (!gridEl || !stickyEl || !bodyEl || !clipEl) return;\n\n const updateClip = () => {\n const stickyRect = stickyEl.getBoundingClientRect();\n const clipRect = clipEl.getBoundingClientRect();\n const overlap = Math.max(0, stickyRect.bottom - clipRect.top);\n clipEl.style.setProperty(\"--data-grid-sticky-overlap\", `${overlap}px`);\n };\n updateClip();\n bodyEl.addEventListener(\"scroll\", updateClip);\n window.addEventListener(\"scroll\", updateClip, true);\n window.addEventListener(\"resize\", updateClip);\n const observer = new ResizeObserver(updateClip);\n observer.observe(gridEl);\n observer.observe(stickyEl);\n observer.observe(bodyEl);\n return () => {\n bodyEl.removeEventListener(\"scroll\", updateClip);\n window.removeEventListener(\"scroll\", updateClip, true);\n window.removeEventListener(\"resize\", updateClip);\n observer.disconnect();\n };\n }, []);\n\n const handleBodyScroll = useCallback(() => {\n const body = scrollContainerRef.current;\n const header = headerScrollRef.current;\n if (body && header) header.scrollLeft = body.scrollLeft;\n }, []);\n\n // ── Toolbar / Footer context ─────────────────────────────────\n const toolbarCtx: DataGridToolbarContext<TRow> = useMemo(\n () => ({\n state,\n onChange,\n columns: allColumns,\n visibleColumns,\n totalRowCount,\n selectedRowCount: state.selection.selectedIds.size,\n strings,\n exportCsv: handleExportCsv,\n }),\n [state, onChange, allColumns, visibleColumns, totalRowCount, strings, handleExportCsv],\n );\n const footerCtx: DataGridFooterContext<TRow> = useMemo(\n () => ({\n state,\n totalRowCount,\n visibleRowCount: rows.length,\n selectedRowCount: state.selection.selectedIds.size,\n paginationMode,\n strings,\n }),\n [state, totalRowCount, rows.length, paginationMode, strings],\n );\n\n const allSelected = rowIds.length > 0 && rowIds.every((id) => state.selection.selectedIds.has(id));\n const someSelected = !allSelected && rowIds.some((id) => state.selection.selectedIds.has(id));\n const infiniteScrollRootRef =\n paginationMode === \"infinite\" && (fillHeight || maxHeight != null)\n ? scrollContainerRef\n : undefined;\n\n // ── Header rendering helper (TanStack header objects, in order) ──\n const headers: Header<TRow, unknown>[] = useMemo(\n () =>\n table\n .getHeaderGroups()[0]?.headers\n .filter((h) => visibleColumns.some((c) => c.id === h.column.id)) ?? [],\n [table, visibleColumns],\n );\n const headerByColId = useMemo(() => {\n const m = new Map<string, Header<TRow, unknown>>();\n for (const h of headers) m.set(h.column.id, h);\n return m;\n }, [headers]);\n\n const isBounded = fillHeight || maxHeight != null;\n\n return (\n <div\n ref={gridRef}\n className={cn(\n \"isolate flex w-full min-w-0 max-w-full flex-col bg-transparent rounded-[calc(var(--radius)*2)]\",\n fillHeight ? \"min-h-0 h-full\" : \"min-h-0 h-auto\",\n isBounded && \"overflow-hidden\",\n className,\n )}\n style={maxHeight != null ? { ...cssVars, maxHeight } : cssVars}\n role=\"grid\"\n aria-rowcount={totalRowCount ?? rows.length}\n aria-colcount={visibleColumns.length}\n >\n <div\n ref={stickyChromeRef}\n className=\"sticky z-30 w-full min-w-0 shrink-0 overflow-visible rounded-t-[calc(var(--radius)*2)] bg-white/90 dark:bg-background/60 backdrop-blur-xl\"\n style={{ top: stickyTop ?? (maxHeight != null ? 0 : \"var(--data-grid-sticky-top, 0px)\") }}\n >\n {toolbar !== false && (\n <div className=\"relative bg-transparent\">\n {toolbar\n ? toolbar(toolbarCtx)\n : (\n <DataGridToolbar\n ctx={toolbarCtx}\n extra={typeof toolbarExtra === \"function\" ? toolbarExtra(toolbarCtx) : toolbarExtra}\n />\n )}\n </div>\n )}\n\n <div className=\"relative\">\n {isRefetching && (\n <div className=\"absolute top-0 left-0 right-0 h-0.5 z-30 bg-foreground/[0.04] overflow-hidden\">\n <div className=\"h-full w-1/3 bg-blue-500/60 rounded-full animate-pulse\" />\n </div>\n )}\n <div\n ref={headerScrollRef}\n className=\"w-full min-w-0 shrink-0 overflow-hidden border-b border-foreground/[0.06]\"\n >\n <div\n className=\"flex\"\n style={{ height: headerHeight, minWidth: totalContentWidth }}\n role=\"row\"\n >\n {selectionMode !== \"none\" && (\n <div\n className=\"flex items-center justify-center border-r border-foreground/[0.04]\"\n style={{ width: 44 }}\n >\n {selectionMode === \"multiple\" && (\n <SelectionCheckbox\n checked={allSelected}\n indeterminate={someSelected}\n onChange={handleSelectAll}\n ariaLabel=\"Select all rows on this page\"\n title=\"Select all rows on this page\"\n />\n )}\n </div>\n )}\n {visibleColumns.map((col) => {\n const header = headerByColId.get(col.id);\n if (!header) return null;\n return <HeaderCell key={col.id} header={header} col={col} resizable={resizable} />;\n })}\n </div>\n </div>\n </div>\n </div>\n\n <div\n ref={scrollContainerRef}\n className={cn(\n \"relative z-0 w-full min-w-0 overflow-auto bg-transparent\",\n isBounded ? \"min-h-0 flex-1\" : \"flex-none\",\n \"[&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar]:h-1.5\",\n \"[&::-webkit-scrollbar-track]:bg-transparent\",\n \"[&::-webkit-scrollbar-thumb]:bg-foreground/[0.08] [&::-webkit-scrollbar-thumb]:rounded-full\",\n \"[&::-webkit-scrollbar-thumb]:hover:bg-foreground/[0.15]\",\n )}\n onScroll={handleBodyScroll}\n >\n <div\n ref={rowsClipRef}\n data-data-grid-rows-clip=\"\"\n className=\"relative z-0\"\n style={{\n minWidth: totalContentWidth,\n clipPath: \"inset(var(--data-grid-sticky-overlap, 0px) 0 0 0)\",\n }}\n >\n {isLoading && (\n <div style={{ minWidth: totalContentWidth }}>\n {loadingState ?? Array.from({ length: 8 }).map((_, i) => (\n <SkeletonRow\n key={i}\n columns={visibleColumns}\n height={estimatedRowHeight}\n showCheckbox={selectionMode !== \"none\"}\n />\n ))}\n </div>\n )}\n\n {!isLoading && rows.length === 0 && (\n <div\n className=\"flex items-center justify-center py-16 text-sm text-muted-foreground\"\n style={{ minWidth: totalContentWidth }}\n >\n {emptyState ?? strings.noData}\n </div>\n )}\n\n {!isLoading && rows.length > 0 && (\n <div\n style={{\n height: rowVirtualizer.getTotalSize(),\n width: \"100%\",\n minWidth: totalContentWidth,\n position: \"relative\",\n }}\n >\n {rowVirtualizer.getVirtualItems().map((virtualRow: VirtualItem) => {\n const row = rows[virtualRow.index] ?? throwErr(\n `DataGrid: virtualized row index ${virtualRow.index} out of range (rows.length=${rows.length})`,\n );\n const rowId = getRowId(row);\n const isSelected = state.selection.selectedIds.has(rowId);\n const isOddRow = virtualRow.index % 2 === 1;\n return (\n <div\n key={rowId}\n ref={isDynamicRowHeight ? rowVirtualizer.measureElement : undefined}\n data-index={virtualRow.index}\n className={cn(\n \"absolute left-0 w-full flex\",\n \"border-b border-black/[0.03] dark:border-white/[0.03]\",\n \"transition-colors duration-75\",\n isSelected\n ? \"bg-blue-500/[0.06] dark:bg-blue-400/[0.08] hover:bg-blue-500/[0.08] dark:hover:bg-blue-400/[0.1]\"\n : isOddRow\n ? \"bg-foreground/[0.02] dark:bg-foreground/[0.03] hover:bg-foreground/[0.04] dark:hover:bg-foreground/[0.06]\"\n : \"hover:bg-foreground/[0.025] dark:hover:bg-foreground/[0.04]\",\n (selectionMode !== \"none\" || onRowClick) && \"cursor-pointer\",\n )}\n style={{\n ...(isDynamicRowHeight\n ? { minHeight: estimatedRowHeight }\n : { height: fixedRowHeight }),\n transform: `translateY(${virtualRow.start}px)`,\n }}\n onClick={(e) => { if (!shouldIgnoreRowClick(e)) handleRowClick(row, rowId, e); }}\n onDoubleClick={(e) => { if (!shouldIgnoreRowClick(e)) onRowDoubleClick?.(row, rowId, e); }}\n role=\"row\"\n aria-rowindex={virtualRow.index + 2}\n aria-selected={isSelected}\n data-row-id={rowId}\n data-state={isSelected ? \"selected\" : undefined}\n >\n {selectionMode !== \"none\" && (\n <div\n className=\"flex items-center justify-center border-r border-black/[0.04] dark:border-white/[0.04]\"\n style={{ width: 44 }}\n >\n <SelectionCheckbox\n checked={isSelected}\n onChange={(event) => handleRowClick(row, rowId, event)}\n ariaLabel={`Select row ${rowId}`}\n />\n </div>\n )}\n {visibleColumns.map((col) => (\n <DataCell\n key={col.id}\n col={col}\n row={row}\n rowId={rowId}\n rowIndex={virtualRow.index}\n isSelected={isSelected}\n dateDisplay={state.dateDisplay}\n />\n ))}\n </div>\n );\n })}\n </div>\n )}\n\n {paginationMode === \"infinite\" && hasMore && !isLoading && (\n <InfiniteScrollSentinel\n onIntersect={onLoadMore ?? NOOP}\n isLoading={isLoadingMore}\n rootRef={infiniteScrollRootRef}\n strings={strings}\n />\n )}\n </div>\n </div>\n\n {footer !== false && (\n <div className=\"sticky bottom-0 z-30 shrink-0 overflow-hidden rounded-b-[calc(var(--radius)*2)] bg-white/90 dark:bg-background/60 backdrop-blur-xl\">\n {footer ? footer(footerCtx) : <DefaultFooter ctx={footerCtx} pagination={paginationMode} onChange={onChange} />}\n {footerExtra && (typeof footerExtra === \"function\" ? footerExtra(footerCtx) : footerExtra)}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -15,6 +15,21 @@ const columns = [{
|
|
|
15
15
|
sortable: true,
|
|
16
16
|
type: "string"
|
|
17
17
|
}];
|
|
18
|
+
const wideColumns = [{
|
|
19
|
+
id: "name",
|
|
20
|
+
header: "Name",
|
|
21
|
+
accessor: (row) => row.name,
|
|
22
|
+
width: 320,
|
|
23
|
+
minWidth: 80,
|
|
24
|
+
type: "string"
|
|
25
|
+
}, {
|
|
26
|
+
id: "email",
|
|
27
|
+
header: "Email",
|
|
28
|
+
accessor: (row) => `${row.name.toLowerCase().replaceAll(" ", ".")}@example.com`,
|
|
29
|
+
width: 420,
|
|
30
|
+
minWidth: 80,
|
|
31
|
+
type: "string"
|
|
32
|
+
}];
|
|
18
33
|
let intersectionObserverRecords = [];
|
|
19
34
|
var MockIntersectionObserver = class {
|
|
20
35
|
constructor(callback, options) {
|
|
@@ -84,6 +99,22 @@ function InteractiveDataGridHarness(props) {
|
|
|
84
99
|
onSelectionChange: props.onSelectionChange
|
|
85
100
|
});
|
|
86
101
|
}
|
|
102
|
+
function WideDataGridHarness() {
|
|
103
|
+
const [state, setState] = useState(() => createDefaultDataGridState(wideColumns));
|
|
104
|
+
return /* @__PURE__ */ jsx("div", {
|
|
105
|
+
style: { width: 320 },
|
|
106
|
+
children: /* @__PURE__ */ jsx(DataGrid, {
|
|
107
|
+
columns: wideColumns,
|
|
108
|
+
rows: [{
|
|
109
|
+
id: "row-1",
|
|
110
|
+
name: "Row 1"
|
|
111
|
+
}],
|
|
112
|
+
getRowId: (row) => row.id,
|
|
113
|
+
state,
|
|
114
|
+
onChange: setState
|
|
115
|
+
})
|
|
116
|
+
});
|
|
117
|
+
}
|
|
87
118
|
describe("DataGrid infinite scroll observer", () => {
|
|
88
119
|
beforeEach(() => {
|
|
89
120
|
intersectionObserverRecords = [];
|
|
@@ -209,6 +240,57 @@ describe("DataGrid controlled callbacks", () => {
|
|
|
209
240
|
expect(isDataGridInteractiveRowClickTarget(cell)).toBe(false);
|
|
210
241
|
});
|
|
211
242
|
});
|
|
243
|
+
describe("DataGrid horizontal scrolling", () => {
|
|
244
|
+
beforeEach(() => {
|
|
245
|
+
vi.stubGlobal("ResizeObserver", MockResizeObserver);
|
|
246
|
+
vi.spyOn(HTMLElement.prototype, "getBoundingClientRect").mockImplementation(function getBoundingClientRect() {
|
|
247
|
+
return {
|
|
248
|
+
x: 0,
|
|
249
|
+
y: 0,
|
|
250
|
+
width: 320,
|
|
251
|
+
height: 44,
|
|
252
|
+
top: 0,
|
|
253
|
+
left: 0,
|
|
254
|
+
right: 320,
|
|
255
|
+
bottom: 44,
|
|
256
|
+
toJSON() {
|
|
257
|
+
return this;
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
});
|
|
261
|
+
Object.defineProperty(HTMLElement.prototype, "clientHeight", {
|
|
262
|
+
configurable: true,
|
|
263
|
+
get() {
|
|
264
|
+
return 400;
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
Object.defineProperty(HTMLElement.prototype, "scrollHeight", {
|
|
268
|
+
configurable: true,
|
|
269
|
+
get() {
|
|
270
|
+
return 400;
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
afterEach(() => {
|
|
275
|
+
cleanup();
|
|
276
|
+
vi.restoreAllMocks();
|
|
277
|
+
vi.unstubAllGlobals();
|
|
278
|
+
});
|
|
279
|
+
it("sizes the sticky clipping layer to the full row width", () => {
|
|
280
|
+
const { container } = render(/* @__PURE__ */ jsx(WideDataGridHarness, {}));
|
|
281
|
+
const rowsClip = container.querySelector("[data-data-grid-rows-clip]");
|
|
282
|
+
expect(rowsClip).toBeInstanceOf(HTMLElement);
|
|
283
|
+
expect(rowsClip.style.minWidth).toBe("740px");
|
|
284
|
+
});
|
|
285
|
+
it("lets the columns popover escape the sticky toolbar bounds", () => {
|
|
286
|
+
const { container, getByTitle } = render(/* @__PURE__ */ jsx(WideDataGridHarness, {}));
|
|
287
|
+
fireEvent.click(getByTitle("Columns"));
|
|
288
|
+
const stickyChrome = container.querySelector("[role=\"grid\"]")?.firstElementChild;
|
|
289
|
+
expect(stickyChrome).toBeInstanceOf(HTMLElement);
|
|
290
|
+
expect(stickyChrome.className).toContain("overflow-visible");
|
|
291
|
+
expect(container.textContent).toContain("Email");
|
|
292
|
+
});
|
|
293
|
+
});
|
|
212
294
|
|
|
213
295
|
//#endregion
|
|
214
296
|
export { };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-grid.test.js","names":[],"sources":["../../../../src/components/data-grid/data-grid.test.tsx"],"sourcesContent":["// @vitest-environment jsdom\n\nimport { cleanup, fireEvent, render, waitFor } from \"@testing-library/react\";\nimport { useState } from \"react\";\nimport { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport {\n createDefaultDataGridState,\n DataGrid,\n isDataGridInteractiveRowClickTarget,\n type DataGridColumnDef,\n type DataGridProps,\n} from \"./index\";\n\ntype Row = {\n id: string,\n name: string,\n};\n\nconst columns: DataGridColumnDef<Row>[] = [\n {\n id: \"name\",\n header: \"Name\",\n accessor: (row) => row.name,\n width: 160,\n minWidth: 80,\n sortable: true,\n type: \"string\",\n },\n];\n\ntype ObserverRecord = {\n options?: IntersectionObserverInit,\n};\n\nlet intersectionObserverRecords: ObserverRecord[] = [];\n\nclass MockIntersectionObserver implements IntersectionObserver {\n readonly root: Element | Document | null;\n readonly rootMargin: string;\n readonly thresholds: ReadonlyArray<number>;\n private readonly callback: IntersectionObserverCallback;\n private readonly record: ObserverRecord;\n\n constructor(\n callback: IntersectionObserverCallback,\n options?: IntersectionObserverInit,\n ) {\n this.callback = callback;\n this.root = options?.root ?? null;\n this.rootMargin = options?.rootMargin ?? \"\";\n this.thresholds = Array.isArray(options?.threshold)\n ? options.threshold\n : [options?.threshold ?? 0];\n this.record = { options };\n intersectionObserverRecords.push(this.record);\n }\n\n disconnect() {}\n observe() {}\n takeRecords(): IntersectionObserverEntry[] {\n return [];\n }\n unobserve() {}\n\n trigger(entry: Partial<IntersectionObserverEntry> = {}) {\n this.callback(\n [\n {\n boundingClientRect: {} as DOMRectReadOnly,\n intersectionRatio: 1,\n intersectionRect: {} as DOMRectReadOnly,\n isIntersecting: true,\n rootBounds: null,\n target: document.createElement(\"div\"),\n time: 0,\n ...entry,\n },\n ],\n this,\n );\n }\n}\n\nclass MockResizeObserver implements ResizeObserver {\n disconnect() {}\n observe() {}\n unobserve() {}\n}\n\nfunction DataGridHarness(props: { fillHeight?: boolean }) {\n const [state, setState] = useState(() => createDefaultDataGridState(columns));\n\n return (\n <div style={{ height: 400 }}>\n <DataGrid<Row>\n columns={columns}\n rows={[{ id: \"row-1\", name: \"Row 1\" }]}\n getRowId={(row) => row.id}\n state={state}\n onChange={setState}\n paginationMode=\"infinite\"\n hasMore\n fillHeight={props.fillHeight}\n />\n </div>\n );\n}\n\nfunction InteractiveDataGridHarness(props: {\n onSortChange?: DataGridProps<Row>[\"onSortChange\"],\n onSelectionChange?: DataGridProps<Row>[\"onSelectionChange\"],\n}) {\n const [state, setState] = useState(() => createDefaultDataGridState(columns));\n\n return (\n <DataGrid<Row>\n columns={columns}\n rows={[{ id: \"row-1\", name: \"Row 1\" }]}\n getRowId={(row) => row.id}\n state={state}\n onChange={setState}\n selectionMode=\"multiple\"\n onSortChange={props.onSortChange}\n onSelectionChange={props.onSelectionChange}\n />\n );\n}\n\ndescribe(\"DataGrid infinite scroll observer\", () => {\n beforeEach(() => {\n intersectionObserverRecords = [];\n\n vi.stubGlobal(\"IntersectionObserver\", MockIntersectionObserver);\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n vi.spyOn(HTMLElement.prototype, \"getBoundingClientRect\").mockImplementation(\n function getBoundingClientRect() {\n return {\n x: 0,\n y: 0,\n width: 320,\n height: 44,\n top: 0,\n left: 0,\n right: 320,\n bottom: 44,\n toJSON() {\n return this;\n },\n } as DOMRect;\n },\n );\n Object.defineProperty(HTMLElement.prototype, \"clientHeight\", {\n configurable: true,\n get() {\n return 400;\n },\n });\n Object.defineProperty(HTMLElement.prototype, \"scrollHeight\", {\n configurable: true,\n get() {\n return 400;\n },\n });\n });\n\n afterEach(() => {\n cleanup();\n vi.restoreAllMocks();\n vi.unstubAllGlobals();\n });\n\n it(\"observes against the grid body when the grid owns vertical scrolling\", async () => {\n const { container } = render(<DataGridHarness fillHeight />);\n\n await waitFor(() => {\n expect(intersectionObserverRecords.length).toBeGreaterThan(0);\n });\n\n const grid = container.querySelector('[role=\"grid\"]');\n expect(grid).not.toBeNull();\n const scrollContainer = grid?.children.item(1);\n\n expect(intersectionObserverRecords.at(-1)?.options?.root).toBe(scrollContainer);\n });\n\n it(\"falls back to the viewport when the page owns vertical scrolling\", async () => {\n render(<DataGridHarness fillHeight={false} />);\n\n await waitFor(() => {\n expect(intersectionObserverRecords.length).toBeGreaterThan(0);\n });\n\n expect(intersectionObserverRecords.at(-1)?.options?.root ?? null).toBeNull();\n });\n});\n\ndescribe(\"DataGrid controlled callbacks\", () => {\n beforeEach(() => {\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n vi.spyOn(HTMLElement.prototype, \"getBoundingClientRect\").mockImplementation(\n function getBoundingClientRect() {\n return {\n x: 0,\n y: 0,\n width: 320,\n height: 44,\n top: 0,\n left: 0,\n right: 320,\n bottom: 44,\n toJSON() {\n return this;\n },\n } as DOMRect;\n },\n );\n Object.defineProperty(HTMLElement.prototype, \"clientHeight\", {\n configurable: true,\n get() {\n return 400;\n },\n });\n Object.defineProperty(HTMLElement.prototype, \"scrollHeight\", {\n configurable: true,\n get() {\n return 400;\n },\n });\n });\n\n afterEach(() => {\n cleanup();\n vi.restoreAllMocks();\n vi.unstubAllGlobals();\n });\n\n it(\"fires onSortChange from the current controlled sort state\", () => {\n const onSortChange = vi.fn();\n const { getByRole } = render(<InteractiveDataGridHarness onSortChange={onSortChange} />);\n\n fireEvent.click(getByRole(\"columnheader\", { name: /name/i }));\n\n expect(onSortChange).toHaveBeenCalledTimes(1);\n expect(onSortChange).toHaveBeenCalledWith([{ columnId: \"name\", direction: \"asc\" }]);\n });\n\n it(\"fires onSelectionChange when selecting all rows\", () => {\n const onSelectionChange = vi.fn();\n const { getByRole } = render(<InteractiveDataGridHarness onSelectionChange={onSelectionChange} />);\n\n fireEvent.click(getByRole(\"checkbox\", { name: /select all rows/i }));\n\n expect(onSelectionChange).toHaveBeenCalledTimes(1);\n const [selectedIds, selectedRows] = onSelectionChange.mock.calls[0];\n expect([...selectedIds]).toEqual([\"row-1\"]);\n expect(selectedRows).toEqual([{ id: \"row-1\", name: \"Row 1\" }]);\n });\n\n it(\"identifies nested interactive controls as row-click blockers\", () => {\n const cell = document.createElement(\"div\");\n const button = document.createElement(\"button\");\n const label = document.createElement(\"span\");\n label.textContent = \"Open menu\";\n button.append(label);\n cell.append(button);\n\n expect(isDataGridInteractiveRowClickTarget(label.firstChild)).toBe(true);\n expect(isDataGridInteractiveRowClickTarget(cell)).toBe(false);\n });\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"data-grid.test.js","names":[],"sources":["../../../../src/components/data-grid/data-grid.test.tsx"],"sourcesContent":["// @vitest-environment jsdom\n\nimport { cleanup, fireEvent, render, waitFor } from \"@testing-library/react\";\nimport { useState } from \"react\";\nimport { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport {\n createDefaultDataGridState,\n DataGrid,\n isDataGridInteractiveRowClickTarget,\n type DataGridColumnDef,\n type DataGridProps,\n} from \"./index\";\n\ntype Row = {\n id: string,\n name: string,\n};\n\nconst columns: DataGridColumnDef<Row>[] = [\n {\n id: \"name\",\n header: \"Name\",\n accessor: (row) => row.name,\n width: 160,\n minWidth: 80,\n sortable: true,\n type: \"string\",\n },\n];\n\nconst wideColumns: DataGridColumnDef<Row>[] = [\n {\n id: \"name\",\n header: \"Name\",\n accessor: (row) => row.name,\n width: 320,\n minWidth: 80,\n type: \"string\",\n },\n {\n id: \"email\",\n header: \"Email\",\n accessor: (row) => `${row.name.toLowerCase().replaceAll(\" \", \".\")}@example.com`,\n width: 420,\n minWidth: 80,\n type: \"string\",\n },\n];\n\ntype ObserverRecord = {\n options?: IntersectionObserverInit,\n};\n\nlet intersectionObserverRecords: ObserverRecord[] = [];\n\nclass MockIntersectionObserver implements IntersectionObserver {\n readonly root: Element | Document | null;\n readonly rootMargin: string;\n readonly thresholds: ReadonlyArray<number>;\n private readonly callback: IntersectionObserverCallback;\n private readonly record: ObserverRecord;\n\n constructor(\n callback: IntersectionObserverCallback,\n options?: IntersectionObserverInit,\n ) {\n this.callback = callback;\n this.root = options?.root ?? null;\n this.rootMargin = options?.rootMargin ?? \"\";\n this.thresholds = Array.isArray(options?.threshold)\n ? options.threshold\n : [options?.threshold ?? 0];\n this.record = { options };\n intersectionObserverRecords.push(this.record);\n }\n\n disconnect() {}\n observe() {}\n takeRecords(): IntersectionObserverEntry[] {\n return [];\n }\n unobserve() {}\n\n trigger(entry: Partial<IntersectionObserverEntry> = {}) {\n this.callback(\n [\n {\n boundingClientRect: {} as DOMRectReadOnly,\n intersectionRatio: 1,\n intersectionRect: {} as DOMRectReadOnly,\n isIntersecting: true,\n rootBounds: null,\n target: document.createElement(\"div\"),\n time: 0,\n ...entry,\n },\n ],\n this,\n );\n }\n}\n\nclass MockResizeObserver implements ResizeObserver {\n disconnect() {}\n observe() {}\n unobserve() {}\n}\n\nfunction DataGridHarness(props: { fillHeight?: boolean }) {\n const [state, setState] = useState(() => createDefaultDataGridState(columns));\n\n return (\n <div style={{ height: 400 }}>\n <DataGrid<Row>\n columns={columns}\n rows={[{ id: \"row-1\", name: \"Row 1\" }]}\n getRowId={(row) => row.id}\n state={state}\n onChange={setState}\n paginationMode=\"infinite\"\n hasMore\n fillHeight={props.fillHeight}\n />\n </div>\n );\n}\n\nfunction InteractiveDataGridHarness(props: {\n onSortChange?: DataGridProps<Row>[\"onSortChange\"],\n onSelectionChange?: DataGridProps<Row>[\"onSelectionChange\"],\n}) {\n const [state, setState] = useState(() => createDefaultDataGridState(columns));\n\n return (\n <DataGrid<Row>\n columns={columns}\n rows={[{ id: \"row-1\", name: \"Row 1\" }]}\n getRowId={(row) => row.id}\n state={state}\n onChange={setState}\n selectionMode=\"multiple\"\n onSortChange={props.onSortChange}\n onSelectionChange={props.onSelectionChange}\n />\n );\n}\n\nfunction WideDataGridHarness() {\n const [state, setState] = useState(() => createDefaultDataGridState(wideColumns));\n\n return (\n <div style={{ width: 320 }}>\n <DataGrid<Row>\n columns={wideColumns}\n rows={[{ id: \"row-1\", name: \"Row 1\" }]}\n getRowId={(row) => row.id}\n state={state}\n onChange={setState}\n />\n </div>\n );\n}\n\ndescribe(\"DataGrid infinite scroll observer\", () => {\n beforeEach(() => {\n intersectionObserverRecords = [];\n\n vi.stubGlobal(\"IntersectionObserver\", MockIntersectionObserver);\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n vi.spyOn(HTMLElement.prototype, \"getBoundingClientRect\").mockImplementation(\n function getBoundingClientRect() {\n return {\n x: 0,\n y: 0,\n width: 320,\n height: 44,\n top: 0,\n left: 0,\n right: 320,\n bottom: 44,\n toJSON() {\n return this;\n },\n } as DOMRect;\n },\n );\n Object.defineProperty(HTMLElement.prototype, \"clientHeight\", {\n configurable: true,\n get() {\n return 400;\n },\n });\n Object.defineProperty(HTMLElement.prototype, \"scrollHeight\", {\n configurable: true,\n get() {\n return 400;\n },\n });\n });\n\n afterEach(() => {\n cleanup();\n vi.restoreAllMocks();\n vi.unstubAllGlobals();\n });\n\n it(\"observes against the grid body when the grid owns vertical scrolling\", async () => {\n const { container } = render(<DataGridHarness fillHeight />);\n\n await waitFor(() => {\n expect(intersectionObserverRecords.length).toBeGreaterThan(0);\n });\n\n const grid = container.querySelector('[role=\"grid\"]');\n expect(grid).not.toBeNull();\n const scrollContainer = grid?.children.item(1);\n\n expect(intersectionObserverRecords.at(-1)?.options?.root).toBe(scrollContainer);\n });\n\n it(\"falls back to the viewport when the page owns vertical scrolling\", async () => {\n render(<DataGridHarness fillHeight={false} />);\n\n await waitFor(() => {\n expect(intersectionObserverRecords.length).toBeGreaterThan(0);\n });\n\n expect(intersectionObserverRecords.at(-1)?.options?.root ?? null).toBeNull();\n });\n});\n\ndescribe(\"DataGrid controlled callbacks\", () => {\n beforeEach(() => {\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n vi.spyOn(HTMLElement.prototype, \"getBoundingClientRect\").mockImplementation(\n function getBoundingClientRect() {\n return {\n x: 0,\n y: 0,\n width: 320,\n height: 44,\n top: 0,\n left: 0,\n right: 320,\n bottom: 44,\n toJSON() {\n return this;\n },\n } as DOMRect;\n },\n );\n Object.defineProperty(HTMLElement.prototype, \"clientHeight\", {\n configurable: true,\n get() {\n return 400;\n },\n });\n Object.defineProperty(HTMLElement.prototype, \"scrollHeight\", {\n configurable: true,\n get() {\n return 400;\n },\n });\n });\n\n afterEach(() => {\n cleanup();\n vi.restoreAllMocks();\n vi.unstubAllGlobals();\n });\n\n it(\"fires onSortChange from the current controlled sort state\", () => {\n const onSortChange = vi.fn();\n const { getByRole } = render(<InteractiveDataGridHarness onSortChange={onSortChange} />);\n\n fireEvent.click(getByRole(\"columnheader\", { name: /name/i }));\n\n expect(onSortChange).toHaveBeenCalledTimes(1);\n expect(onSortChange).toHaveBeenCalledWith([{ columnId: \"name\", direction: \"asc\" }]);\n });\n\n it(\"fires onSelectionChange when selecting all rows\", () => {\n const onSelectionChange = vi.fn();\n const { getByRole } = render(<InteractiveDataGridHarness onSelectionChange={onSelectionChange} />);\n\n fireEvent.click(getByRole(\"checkbox\", { name: /select all rows/i }));\n\n expect(onSelectionChange).toHaveBeenCalledTimes(1);\n const [selectedIds, selectedRows] = onSelectionChange.mock.calls[0];\n expect([...selectedIds]).toEqual([\"row-1\"]);\n expect(selectedRows).toEqual([{ id: \"row-1\", name: \"Row 1\" }]);\n });\n\n it(\"identifies nested interactive controls as row-click blockers\", () => {\n const cell = document.createElement(\"div\");\n const button = document.createElement(\"button\");\n const label = document.createElement(\"span\");\n label.textContent = \"Open menu\";\n button.append(label);\n cell.append(button);\n\n expect(isDataGridInteractiveRowClickTarget(label.firstChild)).toBe(true);\n expect(isDataGridInteractiveRowClickTarget(cell)).toBe(false);\n });\n});\n\ndescribe(\"DataGrid horizontal scrolling\", () => {\n beforeEach(() => {\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n vi.spyOn(HTMLElement.prototype, \"getBoundingClientRect\").mockImplementation(\n function getBoundingClientRect() {\n return {\n x: 0,\n y: 0,\n width: 320,\n height: 44,\n top: 0,\n left: 0,\n right: 320,\n bottom: 44,\n toJSON() {\n return this;\n },\n } as DOMRect;\n },\n );\n Object.defineProperty(HTMLElement.prototype, \"clientHeight\", {\n configurable: true,\n get() {\n return 400;\n },\n });\n Object.defineProperty(HTMLElement.prototype, \"scrollHeight\", {\n configurable: true,\n get() {\n return 400;\n },\n });\n });\n\n afterEach(() => {\n cleanup();\n vi.restoreAllMocks();\n vi.unstubAllGlobals();\n });\n\n it(\"sizes the sticky clipping layer to the full row width\", () => {\n const { container } = render(<WideDataGridHarness />);\n\n const rowsClip = container.querySelector(\"[data-data-grid-rows-clip]\");\n\n expect(rowsClip).toBeInstanceOf(HTMLElement);\n expect((rowsClip as HTMLElement).style.minWidth).toBe(\"740px\");\n });\n\n it(\"lets the columns popover escape the sticky toolbar bounds\", () => {\n const { container, getByTitle } = render(<WideDataGridHarness />);\n\n fireEvent.click(getByTitle(\"Columns\"));\n\n const stickyChrome = container.querySelector('[role=\"grid\"]')?.firstElementChild;\n expect(stickyChrome).toBeInstanceOf(HTMLElement);\n expect((stickyChrome as HTMLElement).className).toContain(\"overflow-visible\");\n expect(container.textContent).toContain(\"Email\");\n });\n});\n"],"mappings":"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"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { DATA_GRID_DEFAULT_STRINGS, resolveDataGridStrings } from "./strings";
|
|
2
2
|
import { DataGridCallbacks, DataGridCellContext, DataGridColumnAlign, DataGridColumnDef, DataGridColumnPin, DataGridColumnPinning, DataGridColumnType, DataGridColumnVisibility, DataGridDataPaginationMode, DataGridDataSource, DataGridDateDisplay, DataGridDateFormat, DataGridFetchParams, DataGridFetchResult, DataGridFooterContext, DataGridHeaderContext, DataGridPaginationMode, DataGridPaginationModel, DataGridProps, DataGridSelectOption, DataGridSelectionMode, DataGridSelectionModel, DataGridSortItem, DataGridSortModel, DataGridState, DataGridStrings, DataGridToolbarContext, RowId } from "./types";
|
|
3
|
-
import {
|
|
3
|
+
import { applyQuickSearch, buildRowComparator, createDefaultDataGridState, defaultFormatAbsolute, defaultFormatRelative, defaultMatchRow, defaultParseDate, exportToCsv, formatGridDate, paginateRows, resolveColumnValue } from "./state";
|
|
4
4
|
import { DataGrid, isDataGridInteractiveRowClickTarget } from "./data-grid";
|
|
5
5
|
import { DataGridToolbar } from "./data-grid-toolbar";
|
|
6
|
-
import { getEffectiveMinWidth } from "./data-grid-sizing";
|
|
6
|
+
import { DEFAULT_COL_WIDTH, DEFAULT_MAX_COL_WIDTH, clampColumnWidth, getEffectiveMaxWidth, getEffectiveMinWidth } from "./data-grid-sizing";
|
|
7
7
|
import { UseDataSourceResult, useDataSource } from "./use-data-source";
|
|
8
|
-
|
|
8
|
+
import { useDataGridUrlState } from "./use-url-state";
|
|
9
|
+
export { DATA_GRID_DEFAULT_STRINGS, DEFAULT_COL_WIDTH, DEFAULT_MAX_COL_WIDTH, DataGrid, type DataGridCallbacks, type DataGridCellContext, type DataGridColumnAlign, type DataGridColumnDef, type DataGridColumnPin, type DataGridColumnPinning, type DataGridColumnType, type DataGridColumnVisibility, type DataGridDataPaginationMode, type DataGridDataSource, type DataGridDateDisplay, type DataGridDateFormat, type DataGridFetchParams, type DataGridFetchResult, type DataGridFooterContext, type DataGridHeaderContext, type DataGridPaginationMode, type DataGridPaginationModel, type DataGridProps, type DataGridSelectOption, type DataGridSelectionMode, type DataGridSelectionModel, type DataGridSortItem, type DataGridSortModel, type DataGridState, type DataGridStrings, DataGridToolbar, type DataGridToolbarContext, type RowId, type UseDataSourceResult, applyQuickSearch, buildRowComparator, clampColumnWidth, createDefaultDataGridState, defaultFormatAbsolute, defaultFormatRelative, defaultMatchRow, defaultParseDate, exportToCsv, formatGridDate, getEffectiveMaxWidth, getEffectiveMinWidth, isDataGridInteractiveRowClickTarget, paginateRows, resolveColumnValue, resolveDataGridStrings, useDataGridUrlState, useDataSource };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { applyQuickSearch, buildRowComparator, createDefaultDataGridState, defaultFormatAbsolute, defaultFormatRelative, defaultMatchRow, defaultParseDate, exportToCsv, formatGridDate, paginateRows, resolveColumnValue } from "./state.js";
|
|
2
2
|
import { DATA_GRID_DEFAULT_STRINGS, resolveDataGridStrings } from "./strings.js";
|
|
3
|
-
import { getEffectiveMinWidth } from "./data-grid-sizing.js";
|
|
3
|
+
import { DEFAULT_COL_WIDTH, DEFAULT_MAX_COL_WIDTH, clampColumnWidth, getEffectiveMaxWidth, getEffectiveMinWidth } from "./data-grid-sizing.js";
|
|
4
4
|
import { DataGridToolbar } from "./data-grid-toolbar.js";
|
|
5
5
|
import { DataGrid, isDataGridInteractiveRowClickTarget } from "./data-grid.js";
|
|
6
6
|
import { useDataSource } from "./use-data-source.js";
|
|
7
|
+
import { useDataGridUrlState } from "./use-url-state.js";
|
|
7
8
|
|
|
8
|
-
export { DATA_GRID_DEFAULT_STRINGS,
|
|
9
|
+
export { DATA_GRID_DEFAULT_STRINGS, DEFAULT_COL_WIDTH, DEFAULT_MAX_COL_WIDTH, DataGrid, DataGridToolbar, applyQuickSearch, buildRowComparator, clampColumnWidth, createDefaultDataGridState, defaultFormatAbsolute, defaultFormatRelative, defaultMatchRow, defaultParseDate, exportToCsv, formatGridDate, getEffectiveMaxWidth, getEffectiveMinWidth, isDataGridInteractiveRowClickTarget, paginateRows, resolveColumnValue, resolveDataGridStrings, useDataGridUrlState, useDataSource };
|