@hexclave/dashboard-ui-components 1.0.0 → 1.0.1
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/alert.js +4 -4
- package/dist/components/alert.js.map +1 -1
- package/dist/components/analytics-chart/analytics-chart-pie.js +33 -11
- package/dist/components/analytics-chart/analytics-chart-pie.js.map +1 -1
- package/dist/components/analytics-chart/analytics-chart.js +3 -3
- package/dist/components/analytics-chart/analytics-chart.js.map +1 -1
- package/dist/components/analytics-chart/default-analytics-chart-tooltip.js +4 -4
- package/dist/components/analytics-chart/default-analytics-chart-tooltip.js.map +1 -1
- package/dist/components/analytics-chart/types.d.ts +1 -0
- package/dist/components/analytics-chart/types.d.ts.map +1 -1
- package/dist/components/analytics-chart/types.js.map +1 -1
- package/dist/components/badge.js +2 -2
- package/dist/components/badge.js.map +1 -1
- package/dist/components/button.js +11 -11
- package/dist/components/button.js.map +1 -1
- package/dist/components/card.js +7 -7
- package/dist/components/card.js.map +1 -1
- package/dist/components/chart-card.js +3 -3
- package/dist/components/chart-card.js.map +1 -1
- package/dist/components/chart-container.js +2 -2
- package/dist/components/chart-container.js.map +1 -1
- package/dist/components/chart-legend.js +3 -3
- package/dist/components/chart-legend.js.map +1 -1
- package/dist/components/chart-tooltip.js +7 -7
- package/dist/components/chart-tooltip.js.map +1 -1
- package/dist/components/data-grid/data-grid-toolbar.js +7 -7
- package/dist/components/data-grid/data-grid-toolbar.js.map +1 -1
- package/dist/components/data-grid/data-grid.js +16 -16
- package/dist/components/data-grid/data-grid.js.map +1 -1
- package/dist/components/data-grid/state.js +2 -2
- package/dist/components/data-grid/state.js.map +1 -1
- package/dist/components/dialog.js +22 -22
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/empty-state.js +2 -2
- package/dist/components/empty-state.js.map +1 -1
- package/dist/components/input.js +8 -8
- package/dist/components/input.js.map +1 -1
- package/dist/components/metric-card.js +5 -5
- package/dist/components/metric-card.js.map +1 -1
- package/dist/components/pill-toggle.js +11 -10
- package/dist/components/pill-toggle.js.map +1 -1
- package/dist/components/progress-bar.js +4 -4
- package/dist/components/progress-bar.js.map +1 -1
- package/dist/components/separator.js +2 -2
- package/dist/components/separator.js.map +1 -1
- package/dist/components/skeleton.js +2 -2
- package/dist/components/skeleton.js.map +1 -1
- package/dist/components/table.js +14 -14
- package/dist/components/table.js.map +1 -1
- package/dist/components/tabs.js +10 -10
- package/dist/components/tabs.js.map +1 -1
- package/dist/dashboard-ui-components.global.js +1647 -1605
- package/dist/dashboard-ui-components.global.js.map +4 -4
- package/dist/esm/components/alert.js.map +1 -1
- package/dist/esm/components/analytics-chart/analytics-chart-pie.js +33 -11
- package/dist/esm/components/analytics-chart/analytics-chart-pie.js.map +1 -1
- package/dist/esm/components/analytics-chart/analytics-chart.js.map +1 -1
- package/dist/esm/components/analytics-chart/default-analytics-chart-tooltip.js.map +1 -1
- package/dist/esm/components/analytics-chart/types.d.ts +1 -0
- package/dist/esm/components/analytics-chart/types.d.ts.map +1 -1
- package/dist/esm/components/analytics-chart/types.js.map +1 -1
- package/dist/esm/components/badge.js.map +1 -1
- package/dist/esm/components/button.js.map +1 -1
- package/dist/esm/components/card.js.map +1 -1
- package/dist/esm/components/chart-card.js.map +1 -1
- package/dist/esm/components/chart-container.js.map +1 -1
- package/dist/esm/components/chart-legend.js.map +1 -1
- package/dist/esm/components/chart-tooltip.js.map +1 -1
- package/dist/esm/components/data-grid/data-grid-toolbar.js.map +1 -1
- package/dist/esm/components/data-grid/data-grid.js.map +1 -1
- package/dist/esm/components/data-grid/state.js.map +1 -1
- package/dist/esm/components/dialog.js.map +1 -1
- package/dist/esm/components/empty-state.js.map +1 -1
- package/dist/esm/components/input.js.map +1 -1
- package/dist/esm/components/metric-card.js.map +1 -1
- package/dist/esm/components/pill-toggle.js +1 -0
- package/dist/esm/components/pill-toggle.js.map +1 -1
- package/dist/esm/components/progress-bar.js.map +1 -1
- package/dist/esm/components/separator.js.map +1 -1
- package/dist/esm/components/skeleton.js.map +1 -1
- package/dist/esm/components/table.js.map +1 -1
- package/dist/esm/components/tabs.js.map +1 -1
- package/package.json +4 -4
|
@@ -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 {\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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"}
|
|
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 \"@hexclave/shared/dist/utils/errors\";\nimport { cn } from \"@hexclave/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":";;;;;;;;;;;;;;;;AA6DA,SAAS,sBAAsB,QAA4C;AACzE,KAAI,kBAAkB,QAAS,QAAO;AACtC,KAAI,kBAAkB,KAAM,QAAO,OAAO;AAC1C,QAAO;;AAGT,SAAgB,oCAAoC,QAAqC;AAEvF,QADW,sBAAsB,OAAO,EAC7B,QAAQ;EACjB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,KAAK,IAAI,CAAC,IAAI;;AAGlB,SAAS,qBAAqB,OAAkC;AAC9D,QAAO,MAAM,oBAAoB,oCAAoC,MAAM,OAAO;;AAKpF,SAAS,kBAAkB,SAA0C;AACnE,QAAO,QAAQ,KAAK,OAAO;EAAE,IAAI,EAAE;EAAU,MAAM,EAAE,cAAc;EAAQ,EAAE;;AAG/E,SAAS,oBAAoB,SAA0C;AACrE,QAAO,QAAQ,KAAK,OAAO;EAAE,UAAU,EAAE;EAAI,WAAW,EAAE,OAAO,SAAS;EAAO,EAAE;;AAGrF,SAAS,uBAAuB,KAA4C;CAC1E,MAAM,MAAyB,EAAE;AACjC,MAAK,MAAM,MAAM,IAAK,KAAI,MAAM;AAChC,QAAO;;AAGT,SAAS,eAAkB,SAAqB,SAAe;AAC7D,QAAO,OAAO,YAAY,aAAc,QAA0B,QAAQ,GAAG;;AAK/E,SAAS,qBACP,OACA,gBACA,WACM;CACN,MAAM,WAAW,eAAe,QAAQ,MAAM,EAAE,QAAQ,QAAQ,EAAE,OAAO,EAAE;AAC3E,KAAI,SAAS,WAAW,KAAK,aAAa,EAAG;CAC7C,MAAM,YAAY,SAAS,QAAQ,KAAK,MAAM,OAAO,EAAE,QAAQ,IAAI,EAAE;CACrE,IAAI,YAAY;AAChB,UAAS,SAAS,KAAK,MAAM;EAE3B,MAAM,QADS,MAAM,SAAS,SAAS,IAEnC,YACA,KAAK,MAAM,cAAc,IAAI,QAAQ,KAAK,WAAW;EACzD,MAAM,MAAM,IAAI,YAAY;EAC5B,MAAM,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,OAAO,MAAM,MAAM,IAAI,IAAI,CAAC;AAC7D,QAAM,IAAI,OAAO;AACjB,eAAa;GACb;;AAaJ,SAAS,aAAa,SAAiC,OAAsC;CAC3F,MAAM,aAAa,QAAQ,YAAY,IAAI,MAAM;AACjD,QAAO;EACL,aAAa,6BAAa,IAAI,KAAK,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC;EACtD,UAAU,aAAa,OAAO;EAC/B;;AAGH,SAAS,YACP,SACA,OACA,WACA,UAC+B;AAC/B,KAAI,QAAQ,YAAY,KAAM,QAAO;CACrC,MAAM,YAAY,UAAU,QAAQ,QAAQ,SAAS;CACrD,MAAM,aAAa,UAAU,QAAQ,MAAM;AAC3C,KAAI,YAAY,KAAK,aAAa,EAAG,QAAO;CAC5C,MAAM,QAAQ,KAAK,IAAI,WAAW,WAAW;CAC7C,MAAM,MAAM,KAAK,IAAI,WAAW,WAAW;CAC3C,MAAM,OAAO,WAAW,IAAI,IAAI,QAAQ,YAAY,mBAAG,IAAI,KAAY;AACvE,MAAK,IAAI,IAAI,OAAO,KAAK,KAAK,IAAK,MAAK,IAAI,UAAU,GAAI;AAC1D,QAAO;EAAE,aAAa;EAAM,UAAU,QAAQ;EAAU;;AAG1D,SAAS,aAAa,SAAiC,OAAsC;CAC3F,MAAM,OAAO,IAAI,IAAI,QAAQ,YAAY;AACzC,KAAI,KAAK,IAAI,MAAM,CAAE,MAAK,OAAO,MAAM;KAClC,MAAK,IAAI,MAAM;AACpB,QAAO;EAAE,aAAa;EAAM,UAAU;EAAO;;AAG/C,SAAS,cAAc,OAA+C;CACpE,MAAM,EAAE,SAAS,OAAO,MAAM,WAAW,cAAc;AACvD,KAAI,SAAS,SAAU,QAAO,aAAa,SAAS,MAAM;AAC1D,KAAI,UAAU,OAAO;EACnB,MAAM,QAAQ,YAAY,SAAS,OAAO,WAAW,UAAU,KAAK;AACpE,MAAI,SAAS,KAAM,QAAO;;AAE5B,KAAI,UAAU,KAAM,QAAO,aAAa,SAAS,MAAM;AACvD,QAAO;EAAE,aAAa,IAAI,IAAI,CAAC,MAAM,CAAC;EAAE,UAAU;EAAO;;AAK3D,SAAS,WAAiB,EACxB,QACA,KACA,aAKC;CACD,MAAM,SAAS,OAAO,OAAO,aAAa;CAC1C,MAAM,YAAY,OAAO,OAAO,cAAc;CAC9C,MAAM,aAAa,OAAO,OAAO,iBAAiB,GAC9C,OAAO,YAAY,CAAC,MAAM,UAAU,CAAC,QAAQ,SAC7C;CACJ,MAAM,MAAmC;EACvC,UAAU,IAAI;EACd,WAAW;EACX,UAAU,WAAW,QAAQ,QAAQ;EACrC,WAAW,aAAa,KAAK,aAAa,IAAI,YAAY,IAAI;EAC/D;CACD,MAAM,QAAQ,OAAO,IAAI,WAAW,aAAa,IAAI,OAAO,IAAI,GAAG,IAAI;CACvE,MAAM,WAAW,OAAO,OAAO,YAAY;CAC3C,MAAM,YAAY,aAAa,OAAO,OAAO,cAAc;CAC3D,MAAM,aAAa,OAAO,OAAO,eAAe;AAEhD,QACE,qBAAC;EACC,WAAW,GACT,mGACA,yEACA,YAAY,iBACb;EACD,OAAO,EAAE,OAAO,kBAAkB,IAAI,GAAG,gBAAgB;EACzD,eAAa,IAAI;EACjB,SAAS,WAAW,OAAO,OAAO,yBAAyB,GAAG;EAC9D,MAAK;EACL,aAAW,WAAW,QAAQ,cAAc,WAAW,SAAS,eAAe;;GAE/E,oBAAC;IACC,WAAW,GACT,gGACA,IAAI,UAAU,YAAY,eAC1B,IAAI,UAAU,WAAW,aAC1B;cAEA;KACI;GAEN,UACC,qBAAC;IAAK,WAAU;eACb,WAAW,QACR,oBAAC;KAAQ,WAAU;KAAU,QAAO;MAAS,GAC7C,oBAAC;KAAU,WAAU;KAAU,QAAO;MAAS,EAClD,IAAI,aAAa,QAChB,oBAAC;KAAK,WAAU;eAAwC,IAAI;MAAiB;KAE1E;GAGR,CAAC,UAAU,YACV,qBAAC;IAAK,WAAU;eACd,oBAAC;KAAQ,WAAU;KAAwB,QAAO;MAAS,EAC3D,oBAAC;KAAU,WAAU;KAAwB,QAAO;MAAS;KACxD;GAGR,aACC,oBAAC;IACC,aAAa,OAAO,kBAAkB;IACtC,cAAc,OAAO,kBAAkB;IACvC,UAAU,MAAM;AACd,OAAE,gBAAgB;AAClB,OAAE,iBAAiB;;IAErB,WAAW,GACT,6EACA,iEACA,kCACA,cAAc,iBACf;KACD;;GAEA;;AAMV,SAAS,SAAe,EACtB,KACA,KACA,OACA,UACA,YACA,eAQC;CACD,MAAM,QAAQ,mBAAmB,KAAK,IAAI;CAC1C,MAAM,MAAiC;EAAE;EAAK;EAAO;EAAU;EAAO,UAAU,IAAI;EAAI;EAAY;EAAa;CAEjH,MAAM,YAAY,IAAI,SAAS,UAAU,IAAI,SAAS;CACtD,IAAI;AACJ,KAAI,IAAI,WAAY,WAAU,IAAI,WAAW,IAAI;UACxC,UAAW,WAAU,eAAe,OAAO,aAAa,IAAI;KAChE,WAAU,gBAAgB,MAAM;CAErC,MAAM,eAAe,IAAI,eAAe,IAAI;CAC5C,MAAM,SAAS,IAAI,iBAAiB;AAEpC,QACE,oBAAC;EACC,WAAW,GACT,4CACA,yEACA,2BACA,SAAS,qBAAqB,gBAC9B,IAAI,UAAU,YAAY,kBAC1B,IAAI,UAAU,WAAW,eACzB,gBAAgB,iBACjB;EACD,OAAO,EAAE,OAAO,kBAAkB,IAAI,GAAG,gBAAgB;EACzD,eAAa,IAAI;EACjB,MAAK;EACL,SAAS,IAAI,eAAe,MAAM;AAChC,KAAE,iBAAiB;AACnB,OAAI,YAAa,KAAK,EAAE;MACtB;EACJ,eAAe,IAAI,qBAAqB,MAAM;AAC5C,KAAE,iBAAiB;AACnB,OAAI,kBAAmB,KAAK,EAAE;MAC5B;YAEJ,oBAAC;GAAI,WAAW,GAAG,WAAW,SAAS,WAAW,WAAW;aAAG;IAAc;GAC1E;;AAIV,SAAS,gBAAgB,OAAiC;AACxD,KAAI,SAAS,KAAM,QAAO,oBAAC;EAAK,WAAU;YAA2B;GAAQ;AAC7E,KAAI,OAAO,UAAU,UACnB,QACE,oBAAC;EAAK,WAAW,GACf,yEACA,QACI,6DACA,6CACL;YACE,QAAQ,QAAQ;GACZ;AAGX,KAAI,iBAAiB,KACnB,QAAO,oBAAC;EAAK,WAAU;YAAsC,MAAM,oBAAoB;GAAQ;AAEjG,QAAO,oBAAC;EAAK,WAAU;YAAY,OAAO,MAAM;GAAQ;;AAG1D,SAAS,eACP,OACA,aACA,KACiB;CACjB,MAAM,EAAE,SAAS,YAAY,eAAe,OAAO,aAAa;EAC9D,YAAY,IAAI;EAChB,YAAY,IAAI;EACjB,CAAC;AACF,KAAI,WAAW,KAAM,QAAO,oBAAC;EAAK,WAAU;YAA2B;GAAQ;AAC/E,QACE,oBAAC;EAAK,WAAU;EAA0D,OAAO,WAAW;YACzF;GACI;;AAMX,SAAS,gBAAgB,OAAuB;CAC9C,IAAI,OAAO;AACX,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,IAAK,SAAS,QAAQ,KAAK,OAAO,MAAM,WAAW,EAAE,GAAI;AAC3F,QAAO,KAAK,IAAI,KAAK;;AAGvB,SAAS,YAAY,EACnB,SACA,QACA,gBAKC;AACD,QACE,qBAAC;EAAI,WAAU;EAAO,OAAO,EAAE,QAAQ;EAAE,MAAK;aAC3C,gBACC,oBAAC;GAAI,WAAU;GAAyF,OAAO,EAAE,OAAO,IAAI;aAC1H,oBAAC,kBAAe,WAAU,oBAAoB;IAC1C,EAEP,QAAQ,KAAK,QACZ,oBAAC;GAEC,WAAU;GACV,OAAO,EAAE,OAAO,kBAAkB,IAAI,GAAG,gBAAgB;aAEzD,oBAAC;IAAe,WAAU;IAAmB,OAAO,EAAE,OAAO,GAAG,KAAM,gBAAgB,IAAI,GAAG,GAAG,GAAI,IAAI;KAAI;KAJvG,IAAI,GAKL,CACN;GACE;;AAMV,SAAS,kBAAkB,EACzB,SACA,eACA,UACA,WACA,SAOC;CACD,MAAM,OAAO,gBAAgB,cAAc,UAAU,cAAc;AACnE,QACE,oBAAC;EACC,WAAW,GACT,kDACA,4DACA,WAAW,gBACP,qCACA,0DACL;EACD,UAAU,MAAM;AACd,KAAE,iBAAiB;AACnB,YAAS,EAAE;;EAEb,cAAY;EACZ,OAAO,SAAS;EAChB,MAAK;EACL,gBAAc,gBAAgB,UAAU;YAExC,oBAAC;GAAK,WAAU;GAAU,QAAQ,WAAW,gBAAgB,SAAS;IAAa;GAC5E;;AAMb,MAAM,aAAa;AAEnB,SAAS,uBAAuB,EAC9B,aACA,WACA,SACA,WAMC;CACD,MAAM,MAAM,OAAuB,KAAK;AACxC,iBAAgB;EACd,MAAM,KAAK,IAAI;AACf,MAAI,CAAC,GAAI;EACT,MAAM,WAAW,IAAI,sBAClB,YAAY;AAAE,OAAI,QAAQ,IAAI,eAAgB,cAAa;KAC5D;GAAE,MAAM,SAAS,WAAW;GAAM,YAAY;GAAS,CACxD;AACD,WAAS,QAAQ,GAAG;AACpB,eAAa,SAAS,YAAY;IACjC,CAAC,aAAa,QAAQ,CAAC;AAC1B,QACE,oBAAC;EAAS;EAAK,WAAU;YACtB,aACC,qBAAC;GAAI,WAAU;cACb,oBAAC,SAAI,WAAU,mFAAmF,EACjG,QAAQ;IACL;GAEJ;;AAMV,SAAS,cAAoB,EAC3B,KACA,YACA,YAKC;CACD,MAAM,EAAE,OAAO,eAAe,iBAAiB,kBAAkB,YAAY;CAC7E,MAAM,aAAa,iBAAiB,OAChC,KAAK,IAAI,GAAG,KAAK,KAAK,gBAAgB,MAAM,WAAW,SAAS,CAAC,GACjE;CAEJ,MAAM,WAAW,cACf,UAAU,OAAO;EAAE,GAAG;EAAG,YAAY;GAAE,GAAG,EAAE;GAAY;GAAW;EAAE,EAAE;CACzE,MAAM,eAAe,aACnB,UAAU,OAAO;EAAE,GAAG;EAAG,YAAY;GAAE,GAAG,EAAE;GAAY;GAAU,WAAW;GAAG;EAAE,EAAE;AAEtF,QACE,qBAAC;EAAI,WAAU;aACb,qBAAC;GAAI,WAAU;cACZ,mBAAmB,KAClB,oBAAC;IAAK,WAAU;cAA+B,QAAQ,aAAa,iBAAiB;KAAQ,EAE9F,iBAAiB,QAAQ,qBAAC;IAAM;IAAgB;IAAK;IAAc;OAAY;IAC5E,EAEL,eAAe,cAAc,cAAc,QAC1C,qBAAC;GAAI,WAAU;cACb,qBAAC;IAAI,WAAU;eACb,oBAAC,oBAAM,QAAQ,cAAmB,EAClC,oBAAC;KACC,WAAW,GACT,2FACA,uFACA,iBACD;KACD,OAAO,MAAM,WAAW;KACxB,WAAW,MAAM,YAAY,OAAO,EAAE,OAAO,MAAM,CAAC;eAEnD;MAAC;MAAI;MAAI;MAAI;MAAI,CAAC,KAAK,SAAS,oBAAC;MAAkB,OAAO;gBAAO;QAApB,KAAkC,CAAC;MAC1E;KACL,EACN,qBAAC;IAAI,WAAU;;KACb,oBAAC;MACC,WAAW,GACT,uDACA,8EACA,gCACD;MACD,eAAe,QAAQ,MAAM,WAAW,YAAY,EAAE;MACtD,UAAU,MAAM,WAAW,cAAc;MACzC,cAAW;gBAEX,oBAAC;OAAQ,WAAU;OAAyB,QAAO;QAAS;OACrD;KACT,oBAAC;MAAK,WAAU;gBACb,QAAQ,OAAO,MAAM,WAAW,YAAY,GAAG,WAAW;OACtD;KACP,oBAAC;MACC,WAAW,GACT,uDACA,8EACA,gCACD;MACD,eAAe,QAAQ,MAAM,WAAW,YAAY,EAAE;MACtD,UAAU,MAAM,WAAW,aAAa,aAAa;MACrD,cAAW;gBAEX,oBAAC;OAAU,WAAU;OAAyB,QAAO;QAAS;OACvD;;KACL;IACF;GAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CV,SAAgB,SAAe,OAA4B;CACzD,MAAM,EACJ,SAAS,YACT,MACA,UACA,eACA,YAAY,OACZ,eAAe,OACf,UAAU,OACV,gBAAgB,OAChB,YACA,OACA,UACA,iBAAiB,aACjB,gBAAgB,QAChB,YAAY,MACZ,WAAW,gBAAgB,IAC3B,oBAAoB,wBACpB,eAAe,IACf,WAAW,GACX,WACA,aAAa,MACb,WACA,SACA,cACA,YACA,cACA,QACA,aACA,iBAAiB,UACjB,SAAS,iBACT,WACA,YACA,kBACA,mBACA,cACA,gBACA,6BACE;CAEJ,MAAM,qBAAqB,kBAAkB;CAC7C,MAAM,iBAAiB,qBAAqB,SAAY;CACxD,MAAM,qBAAqB,0BAA2B,kBAAkB;CAExE,MAAM,UAAU,cAAc,uBAAuB,gBAAgB,EAAE,CAAC,gBAAgB,CAAC;CAGzF,MAAM,eAAe,cAEjB,WAAW,KAAK,SAAS;EACvB,IAAI,IAAI;EACR,aAAa,QAAQ,mBAAmB,KAAK,IAAI;EACjD,QAAQ,OAAO,IAAI,WAAW,WAAW,IAAI,SAAS,IAAI;EAC1D,MAAM,IAAI,SAAS;EACnB,SAAS,qBAAqB,IAAI;EAClC,SAAS,qBAAqB,IAAI;EAClC,eAAe,IAAI,aAAa;EAChC,cAAc,IAAI,aAAa;EAC/B,gBAAgB,IAAI,cAAc;EAClC,iBAAiB;EAClB,EAAE,EACL,CAAC,WAAW,CACb;CAGD,MAAM,kBAAkB,cAAc,kBAAkB,MAAM,QAAQ,EAAE,CAAC,MAAM,QAAQ,CAAC;CACxF,MAAM,uBAAuB,cACrB,uBAAuB,MAAM,UAAU,YAAY,EACzD,CAAC,MAAM,UAAU,YAAY,CAC9B;CAED,MAAM,wBAAwB,eACrB;EAAE,MAAM,CAAC,GAAG,MAAM,cAAc,KAAK;EAAE,OAAO,CAAC,GAAG,MAAM,cAAc,MAAM;EAAE,GACrF,CAAC,MAAM,cAAc,CACtB;CACD,MAAM,sBAAsB,cACpB,CAAC,GAAG,MAAM,YAAY,EAC5B,CAAC,MAAM,YAAY,CACpB;CAED,MAAM,gBAAgB,OAAO,WAAW;AACxC,eAAc,UAAU;CAExB,MAAM,sBAAsB,aACzB,YAAmC;EAElC,MAAM,OAA2B,oBADpB,eAAe,SAAS,kBAAkB,MAAM,QAAQ,CAAC,CACZ,CAAC,KAAK,OAAO,EAAE,GAAG,GAAG,EAAE;AAIjF,YAAU,OAAO;GACf,GAAG;GACH,SAAS;GACT,YAAY;IAAE,GAAG,EAAE;IAAY,WAAW;IAAG;GAC9C,EAAE;AACH,iBAAe,KAAK;IAEtB;EAAC;EAAU;EAAc,MAAM;EAAQ,CACxC;CAED,MAAM,2BAA2B,aAC9B,YAAwC;EACvC,MAAM,OAAO,eAAe,SAAS,MAAM,aAAa;EAExD,MAAM,UAAkC,EAAE;AAC1C,OAAK,MAAM,CAAC,IAAI,MAAM,OAAO,QAAQ,KAAK,EAAE;GAC1C,MAAM,MAAM,cAAc,QAAQ,MAAM,MAAM,EAAE,OAAO,GAAG;AAC1D,WAAQ,MAAM,MAAM,iBAAiB,KAAK,EAAE,GAAG;;AAEjD,YAAU,OAAO;GAAE,GAAG;GAAG,cAAc;GAAS,EAAE;AAElD,MAAI,gBACF;QAAK,MAAM,CAAC,IAAI,MAAM,OAAO,QAAQ,QAAQ,CAC3C,KAAI,MAAM,aAAa,QAAQ,EAAG,gBAAe,IAAI,EAAE;;IAI7D;EAAC;EAAU;EAAgB,MAAM;EAAa,CAC/C;CAED,MAAM,yBAAyB,aAC5B,YAAsC;EACrC,MAAM,OAAO,eAAe,SAAS,MAAM,iBAAiB;AAC5D,YAAU,OAAO;GAAE,GAAG;GAAG,kBAAkB;GAAM,EAAE;AACnD,6BAA2B,KAAK;IAElC;EAAC;EAAU;EAA0B,MAAM;EAAiB,CAC7D;CAED,MAAM,0BAA0B,aAC7B,YAAuC;EACtC,MAAM,OAAO,eAAe,SAAS,CAAC,GAAG,MAAM,YAAY,CAAC;AAC5D,YAAU,OAAO;GAAE,GAAG;GAAG,aAAa;GAAM,EAAE;IAEhD,CAAC,UAAU,MAAM,YAAY,CAC9B;CAED,MAAM,4BAA4B,aAC/B,YAAyC;EAKxC,MAAM,OAAO,eAAe,SAJQ;GAClC,MAAM,CAAC,GAAG,MAAM,cAAc,KAAK;GACnC,OAAO,CAAC,GAAG,MAAM,cAAc,MAAM;GACtC,CAC4C;AAC7C,YAAU,OAAO;GACf,GAAG;GACH,eAAe;IAAE,MAAM,KAAK,QAAQ,EAAE;IAAE,OAAO,KAAK,SAAS,EAAE;IAAE;GAClE,EAAE;IAEL,CAAC,UAAU,MAAM,cAAc,CAChC;CAGD,MAAM,QAAQ,cAAoB;EAChC,MAAM;EACN,SAAS;EACT,WAAW,QAAQ,SAAS,IAAI;EAChC,iBAAiB,iBAAiB;EAClC,OAAO;GACL,SAAS;GACT,kBAAkB,MAAM;GACxB,cAAc,MAAM;GACpB,aAAa;GACb,eAAe;GACf,cAAc;GACf;EACD,iBAAiB;EACjB,sBAAsB;EACtB,0BAA0B;EAC1B,qBAAqB;EACrB,uBAAuB;EACvB,kBAAkB;EAClB,oBAAoB,kBAAkB;EACtC,yBAAyB,kBAAkB;EAC3C,sBAAsB;EACtB,eAAe;EACf,kBAAkB;EAClB,iBAAiB;EAClB,CAAC;CAGF,MAAM,iBAAiB,cAAc;EACnC,MAAM,SAAS,IAAI,IAAI,WAAW,KAAK,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;AACxD,SAAO,MACJ,uBAAuB,CACvB,KAAK,MAAM,OAAO,IAAI,EAAE,GAAG,CAAC,CAC5B,OAAO,QAAQ;IACjB;EAAC;EAAY;EAAO,MAAM;EAAa,MAAM;EAAiB,CAAC;CAGlE,MAAM,SAAS,cAAc,KAAK,IAAI,SAAS,EAAE,CAAC,MAAM,SAAS,CAAC;CAMlE,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,EAAE;AACvD,uBAAsB;EACpB,MAAM,OAAO,QAAQ;EACrB,MAAM,WAAW,mBAAmB;AACpC,MAAI,CAAC,KAAM;EACX,MAAM,eAAe;GACnB,MAAM,IAAI,UAAU,eAAe,KAAK;AACxC,OAAI,IAAI,EAAG,mBAAkB,EAAE;;AAEjC,UAAQ;EACR,MAAM,WAAW,IAAI,eAAe,OAAO;AAC3C,WAAS,QAAQ,KAAK;AACtB,MAAI,SAAU,UAAS,QAAQ,SAAS;AACxC,eAAa,SAAS,YAAY;IACjC,EAAE,CAAC;CAIN,MAAM,mBAAmB,MAAM,UAAU,CAAC;CAC1C,MAAM,cAAc,cAAsC;EACxD,MAAM,QAAgC,EAAE;EACxC,IAAI,YAAY,kBAAkB,SAAS,KAAK;EAChD,MAAM,aAAa,iBAAiB,oBAAoB;EACxD,MAAM,cAAc,iBAAiB,eAAe;AACpD,OAAK,MAAM,OAAO,gBAAgB;GAEhC,MAAM,WADQ,MAAM,UAAU,IAAI,GAAG,EACb,SAAS,IAAI,IAAI,SAAS;GAClD,MAAM,WAAW,eAAe,IAAI,KAChC,iBAAiB,KAAK,WAAW,YAAY,GAC7C;AACJ,SAAM,IAAI,MAAM;AAChB,gBAAa;;AAEf,uBAAqB,OAAO,gBAAgB,iBAAiB,UAAU;AACvE,SAAO;IACN;EAAC;EAAgB;EAAO;EAAkB,MAAM;EAAc;EAAgB;EAAc,CAAC;CAEhG,MAAM,oBAAoB,cAAc;EACtC,IAAI,QAAQ,kBAAkB,SAAS,KAAK;AAC5C,OAAK,MAAM,OAAO,eAAgB,UAAS,YAAY,IAAI,OAAO;AAClE,SAAO;IACN;EAAC;EAAgB;EAAa;EAAc,CAAC;CAEhD,MAAM,UAAU,cAA6B;EAC3C,MAAM,OAAwC,EAAE,kBAAkB,GAAG,kBAAkB,KAAK;AAC5F,OAAK,MAAM,OAAO,eAChB,MAAK,SAAS,IAAI,GAAG,UAAU,YAAY,IAAI;AAEjD,SAAO;IACN;EAAC;EAAgB;EAAa;EAAkB,CAAC;CAGpD,MAAM,gBAAgB,aACnB,SAAiC;AAChC,YAAU,OAAO;GAAE,GAAG;GAAG,WAAW;GAAM,EAAE;AAC5C,MAAI,mBAAmB;GACrB,MAAM,QAAQ,KAAK;AAEnB,qBAAkB,OADG,KAAK,QAAQ,MAAM,MAAM,IAAI,SAAS,EAAE,CAAC,CAAC,CACzB;;IAG1C;EAAC;EAAU;EAAmB;EAAM;EAAS,CAC9C;CAED,MAAM,iBAAiB,aACpB,KAAW,OAAc,UAA4B;AACpD,MAAI,kBAAkB,OAQpB,eAPa,cAAc;GACzB,SAAS,MAAM;GACf;GACA,MAAM;GACN,WAAW;IAAE,OAAO,MAAM;IAAU,MAAM,MAAM,WAAW,MAAM;IAAS;GAC1E,WAAW;GACZ,CAAC,CACiB;AAErB,eAAa,KAAK,OAAO,MAAM;IAEjC;EAAC;EAAe,MAAM;EAAW;EAAQ;EAAe;EAAW,CACpE;CAED,MAAM,kBAAkB,kBAAkB;AAExC,gBADuB,OAAO,SAAS,KAAK,OAAO,OAAO,OAAO,MAAM,UAAU,YAAY,IAAI,GAAG,CAAC,GAG/F;GAAE,6BAAa,IAAI,KAAK;GAAE,UAAU;GAAM,GAC1C;GAAE,aAAa,IAAI,IAAI,OAAO;GAAE,UAAU;GAAM,CACrD;IACA;EAAC;EAAQ,MAAM,UAAU;EAAa;EAAc,CAAC;CAQxD,MAAM,kBAAkB,kBAAkB;AACxC,MAAI,OAAO,WAAW,eAAe,KAAK,SAAS,GAAG;GACpD,MAAM,cAAc,iBAAiB,QAAQ,gBAAgB,KAAK,SAC9D,OAAO,cAAc,iDACrB;AAIJ,OAAI,CAHc,OAAO,QACvB,UAAU,KAAK,OAAO,gBAAgB,CAAC,aAAa,KAAK,WAAW,IAAI,KAAK,MAAM,YAAY,GAChG,CACe;;AAElB,cAAY,MAAM,gBAAgB,eAAe;IAChD;EAAC;EAAM;EAAgB;EAAgB;EAAc,CAAC;CAGzD,MAAM,qBAAqB,OAAuB,KAAK;CACvD,MAAM,kBAAkB,OAAuB,KAAK;CACpD,MAAM,kBAAkB,OAAuB,KAAK;CACpD,MAAM,cAAc,OAAuB,KAAK;CAChD,MAAM,UAAU,OAAuB,KAAK;CAC5C,MAAM,mBAAmB,aAAa,OAAgB,GAAG,uBAAuB,CAAC,QAAQ,EAAE,CAAC;CAE5F,MAAM,iBAAiB,eAAe;EACpC,OAAO,KAAK;EACZ,wBAAwB,mBAAmB;EAC3C,oBAAoB;EACpB;EACA,aAAa,UAAU;GACrB,MAAM,MAAM,KAAK;AACjB,UAAO,OAAO,OAAO,OAAO,SAAS,IAAI,CAAC,GAAG;;EAE/C,GAAI,qBAAqB,EAAE,gBAAgB,kBAAkB,GAAG,EAAE;EACnE,CAAC;AAGF,uBAAsB;EACpB,MAAM,SAAS,QAAQ;EACvB,MAAM,WAAW,gBAAgB;EACjC,MAAM,SAAS,mBAAmB;EAClC,MAAM,SAAS,YAAY;AAC3B,MAAI,CAAC,UAAU,CAAC,YAAY,CAAC,UAAU,CAAC,OAAQ;EAEhD,MAAM,mBAAmB;GACvB,MAAM,aAAa,SAAS,uBAAuB;GACnD,MAAM,WAAW,OAAO,uBAAuB;GAC/C,MAAM,UAAU,KAAK,IAAI,GAAG,WAAW,SAAS,SAAS,IAAI;AAC7D,UAAO,MAAM,YAAY,8BAA8B,GAAG,QAAQ,IAAI;;AAExE,cAAY;AACZ,SAAO,iBAAiB,UAAU,WAAW;AAC7C,SAAO,iBAAiB,UAAU,YAAY,KAAK;AACnD,SAAO,iBAAiB,UAAU,WAAW;EAC7C,MAAM,WAAW,IAAI,eAAe,WAAW;AAC/C,WAAS,QAAQ,OAAO;AACxB,WAAS,QAAQ,SAAS;AAC1B,WAAS,QAAQ,OAAO;AACxB,eAAa;AACX,UAAO,oBAAoB,UAAU,WAAW;AAChD,UAAO,oBAAoB,UAAU,YAAY,KAAK;AACtD,UAAO,oBAAoB,UAAU,WAAW;AAChD,YAAS,YAAY;;IAEtB,EAAE,CAAC;CAEN,MAAM,mBAAmB,kBAAkB;EACzC,MAAM,OAAO,mBAAmB;EAChC,MAAM,SAAS,gBAAgB;AAC/B,MAAI,QAAQ,OAAQ,QAAO,aAAa,KAAK;IAC5C,EAAE,CAAC;CAGN,MAAM,aAA2C,eACxC;EACL;EACA;EACA,SAAS;EACT;EACA;EACA,kBAAkB,MAAM,UAAU,YAAY;EAC9C;EACA,WAAW;EACZ,GACD;EAAC;EAAO;EAAU;EAAY;EAAgB;EAAe;EAAS;EAAgB,CACvF;CACD,MAAM,YAAyC,eACtC;EACL;EACA;EACA,iBAAiB,KAAK;EACtB,kBAAkB,MAAM,UAAU,YAAY;EAC9C;EACA;EACD,GACD;EAAC;EAAO;EAAe,KAAK;EAAQ;EAAgB;EAAQ,CAC7D;CAED,MAAM,cAAc,OAAO,SAAS,KAAK,OAAO,OAAO,OAAO,MAAM,UAAU,YAAY,IAAI,GAAG,CAAC;CAClG,MAAM,eAAe,CAAC,eAAe,OAAO,MAAM,OAAO,MAAM,UAAU,YAAY,IAAI,GAAG,CAAC;CAC7F,MAAM,wBACJ,mBAAmB,eAAe,cAAc,aAAa,QACzD,qBACA;CAGN,MAAM,UAAmC,cAErC,MACG,iBAAiB,CAAC,IAAI,QACtB,QAAQ,MAAM,eAAe,MAAM,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,CAAC,IAAI,EAAE,EAC1E,CAAC,OAAO,eAAe,CACxB;CACD,MAAM,gBAAgB,cAAc;EAClC,MAAM,oBAAI,IAAI,KAAoC;AAClD,OAAK,MAAM,KAAK,QAAS,GAAE,IAAI,EAAE,OAAO,IAAI,EAAE;AAC9C,SAAO;IACN,CAAC,QAAQ,CAAC;CAEb,MAAM,YAAY,cAAc,aAAa;AAE7C,QACE,qBAAC;EACC,KAAK;EACL,WAAW,GACT,kGACA,aAAa,mBAAmB,kBAChC,aAAa,mBACb,UACD;EACD,OAAO,aAAa,OAAO;GAAE,GAAG;GAAS;GAAW,GAAG;EACvD,MAAK;EACL,iBAAe,iBAAiB,KAAK;EACrC,iBAAe,eAAe;;GAE9B,qBAAC;IACC,KAAK;IACL,WAAU;IACV,OAAO,EAAE,KAAK,cAAc,aAAa,OAAO,IAAI,qCAAqC;eAExF,YAAY,SACX,oBAAC;KAAI,WAAU;eACZ,UACG,QAAQ,WAAW,GAEnB,oBAAC;MACC,KAAK;MACL,OAAO,OAAO,iBAAiB,aAAa,aAAa,WAAW,GAAG;OACvE;MAEF,EAGR,qBAAC;KAAI,WAAU;gBACZ,gBACC,oBAAC;MAAI,WAAU;gBACb,oBAAC,SAAI,WAAU,2DAA2D;OACtE,EAER,oBAAC;MACC,KAAK;MACL,WAAU;gBAEV,qBAAC;OACC,WAAU;OACV,OAAO;QAAE,QAAQ;QAAc,UAAU;QAAmB;OAC5D,MAAK;kBAEJ,kBAAkB,UACjB,oBAAC;QACC,WAAU;QACV,OAAO,EAAE,OAAO,IAAI;kBAEnB,kBAAkB,cACjB,oBAAC;SACC,SAAS;SACT,eAAe;SACf,UAAU;SACV,WAAU;SACV,OAAM;UACN;SAEA,EAEP,eAAe,KAAK,QAAQ;QAC3B,MAAM,SAAS,cAAc,IAAI,IAAI,GAAG;AACxC,YAAI,CAAC,OAAQ,QAAO;AACpB,eAAO,oBAAC;SAAgC;SAAa;SAAgB;WAA7C,IAAI,GAAsD;SAClF;QACE;OACF;MACF;KACF;GAEN,oBAAC;IACC,KAAK;IACL,WAAW,GACT,4DACA,YAAY,mBAAmB,aAC/B,6DACA,+CACA,+FACA,0DACD;IACD,UAAU;cAEV,qBAAC;KACC,KAAK;KACL,4BAAyB;KACzB,WAAU;KACV,OAAO;MACL,UAAU;MACV,UAAU;MACX;;MAEA,aACC,oBAAC;OAAI,OAAO,EAAE,UAAU,mBAAmB;iBACxC,gBAAgB,MAAM,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,GAAG,MACjD,oBAAC;QAEC,SAAS;QACT,QAAQ;QACR,cAAc,kBAAkB;UAH3B,EAIL,CACF;QACE;MAGP,CAAC,aAAa,KAAK,WAAW,KAC7B,oBAAC;OACC,WAAU;OACV,OAAO,EAAE,UAAU,mBAAmB;iBAErC,cAAc,QAAQ;QACnB;MAGP,CAAC,aAAa,KAAK,SAAS,KAC3B,oBAAC;OACC,OAAO;QACL,QAAQ,eAAe,cAAc;QACrC,OAAO;QACP,UAAU;QACV,UAAU;QACX;iBAEA,eAAe,iBAAiB,CAAC,KAAK,eAA4B;QACjE,MAAM,MAAM,KAAK,WAAW,UAAU,SACpC,mCAAmC,WAAW,MAAM,6BAA6B,KAAK,OAAO,GAC9F;QACD,MAAM,QAAQ,SAAS,IAAI;QAC3B,MAAM,aAAa,MAAM,UAAU,YAAY,IAAI,MAAM;QACzD,MAAM,WAAW,WAAW,QAAQ,MAAM;AAC1C,eACE,qBAAC;SAEC,KAAK,qBAAqB,eAAe,iBAAiB;SAC1D,cAAY,WAAW;SACvB,WAAW,GACT,+BACA,yDACA,iCACA,aACI,qGACA,WACE,8GACA,gEACL,kBAAkB,UAAU,eAAe,iBAC7C;SACD,OAAO;UACL,GAAI,qBACA,EAAE,WAAW,oBAAoB,GACjC,EAAE,QAAQ,gBAAgB;UAC9B,WAAW,cAAc,WAAW,MAAM;UAC3C;SACD,UAAU,MAAM;AAAE,cAAI,CAAC,qBAAqB,EAAE,CAAE,gBAAe,KAAK,OAAO,EAAE;;SAC7E,gBAAgB,MAAM;AAAE,cAAI,CAAC,qBAAqB,EAAE,CAAE,oBAAmB,KAAK,OAAO,EAAE;;SACvF,MAAK;SACL,iBAAe,WAAW,QAAQ;SAClC,iBAAe;SACf,eAAa;SACb,cAAY,aAAa,aAAa;oBAErC,kBAAkB,UACjB,oBAAC;UACC,WAAU;UACV,OAAO,EAAE,OAAO,IAAI;oBAEpB,oBAAC;WACC,SAAS;WACT,WAAW,UAAU,eAAe,KAAK,OAAO,MAAM;WACtD,WAAW,cAAc;YACzB;WACE,EAEP,eAAe,KAAK,QACnB,oBAAC;UAEM;UACA;UACE;UACP,UAAU,WAAW;UACT;UACZ,aAAa,MAAM;YANd,IAAI,GAOT,CACF;WAlDG,MAmDD;SAER;QACE;MAGP,mBAAmB,cAAc,WAAW,CAAC,aAC5C,oBAAC;OACC,aAAa,cAAc;OAC3B,WAAW;OACX,SAAS;OACA;QACT;;MAEA;KACF;GAEL,WAAW,SACV,qBAAC;IAAI,WAAU;eACZ,SAAS,OAAO,UAAU,GAAG,oBAAC;KAAc,KAAK;KAAW,YAAY;KAA0B;MAAY,EAC9G,gBAAgB,OAAO,gBAAgB,aAAa,YAAY,UAAU,GAAG;KAC1E;;GAEJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","names":[],"sources":["../../../../src/components/data-grid/state.ts"],"sourcesContent":["import { stringCompare } from \"@stackframe/stack-shared/dist/utils/strings\";\nimport { clampColumnWidth, DEFAULT_COL_WIDTH } from \"./data-grid-sizing\";\nimport type {\n DataGridColumnDef,\n DataGridDateDisplay,\n DataGridDateFormat,\n DataGridPaginationModel,\n DataGridSortModel,\n DataGridState,\n} from \"./types\";\n\n/**\n * Build the initial `DataGridState` for a set of columns. Pass this as the\n * lazy initializer to `useState` — never hand-assemble the state object.\n *\n * ```tsx\n * const [gridState, setGridState] = React.useState(() =>\n * createDefaultDataGridState(columns)\n * );\n * ```\n */\nexport function createDefaultDataGridState(\n columns: readonly DataGridColumnDef<any>[],\n): DataGridState {\n const columnWidths: Record<string, number> = {};\n const columnOrder: string[] = [];\n\n for (const col of columns) {\n columnWidths[col.id] = clampColumnWidth(col, col.width ?? DEFAULT_COL_WIDTH);\n columnOrder.push(col.id);\n }\n\n return {\n sorting: [],\n columnVisibility: {},\n columnWidths,\n columnPinning: { left: [], right: [] },\n columnOrder,\n pagination: { pageIndex: 0, pageSize: 50 },\n selection: { selectedIds: new Set(), anchorId: null },\n dateDisplay: \"relative\",\n quickSearch: \"\",\n };\n}\n\n// ─── Column value resolution ─────────────────────────────────────────\n\nexport function resolveColumnValue<TRow>(\n col: DataGridColumnDef<TRow>,\n row: TRow,\n): unknown {\n if (typeof col.accessor === \"function\") return col.accessor(row);\n const key = (col.accessor ?? col.id) as keyof TRow;\n return row[key];\n}\n\n// ─── Default sort comparator (used by client-mode useDataSource) ────\n\nfunction defaultComparator(a: unknown, b: unknown): number {\n if (a == null && b == null) return 0;\n if (a == null) return -1;\n if (b == null) return 1;\n if (typeof a === \"number\" && typeof b === \"number\") return a - b;\n if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();\n return stringCompare(String(a), String(b));\n}\n\nexport function buildRowComparator<TRow>(\n sortModel: DataGridSortModel,\n columns: readonly DataGridColumnDef<TRow>[],\n): ((a: TRow, b: TRow) => number) | null {\n if (sortModel.length === 0) return null;\n const colMap = new Map(columns.map((c) => [c.id, c]));\n return (a, b) => {\n for (const { columnId, direction } of sortModel) {\n const col = colMap.get(columnId);\n if (!col) continue;\n const va = resolveColumnValue(col, a);\n const vb = resolveColumnValue(col, b);\n const cmp = col.sortComparator ? col.sortComparator(va, vb) : defaultComparator(va, vb);\n if (cmp !== 0) return direction === \"asc\" ? cmp : -cmp;\n }\n return 0;\n };\n}\n\n// ─── Pagination ──────────────────────────────────────────────────────\n\nexport function paginateRows<TRow>(\n rows: readonly TRow[],\n pagination: DataGridPaginationModel,\n): TRow[] {\n const start = pagination.pageIndex * pagination.pageSize;\n return rows.slice(start, start + pagination.pageSize) as TRow[];\n}\n\n// ─── Quick search ────────────────────────────────────────────────────\n\n/** Default row matcher: case-insensitive substring across every column. */\nexport function defaultMatchRow<TRow>(\n row: TRow,\n query: string,\n columns: readonly DataGridColumnDef<TRow>[],\n): boolean {\n for (const col of columns) {\n const v = resolveColumnValue(col, row);\n if (v == null) continue;\n if (String(v).toLowerCase().includes(query)) return true;\n }\n return false;\n}\n\nexport function applyQuickSearch<TRow>(\n rows: readonly TRow[],\n query: string,\n columns: readonly DataGridColumnDef<TRow>[],\n matchRow: (\n row: TRow,\n query: string,\n columns: readonly DataGridColumnDef<TRow>[],\n ) => boolean = defaultMatchRow,\n): readonly TRow[] {\n const trimmed = query.trim().toLowerCase();\n if (!trimmed) return rows;\n return rows.filter((r) => matchRow(r, trimmed, columns));\n}\n\n// ─── Date helpers ────────────────────────────────────────────────────\n\nexport function defaultParseDate(value: unknown): Date | null {\n if (value == null) return null;\n if (value instanceof Date) return isNaN(value.getTime()) ? null : value;\n if (typeof value === \"number\" || typeof value === \"string\") {\n const d = new Date(value);\n return isNaN(d.getTime()) ? null : d;\n }\n return null;\n}\n\nconst DIVISIONS: Array<{ amount: number; unit: Intl.RelativeTimeFormatUnit }> = [\n { amount: 60, unit: \"second\" },\n { amount: 60, unit: \"minute\" },\n { amount: 24, unit: \"hour\" },\n { amount: 7, unit: \"day\" },\n { amount: 4.34524, unit: \"week\" },\n { amount: 12, unit: \"month\" },\n { amount: Number.POSITIVE_INFINITY, unit: \"year\" },\n];\n\nconst relativeTimeFormatterCache = new Map<string, Intl.RelativeTimeFormat>();\nfunction getRelativeTimeFormatter(locale?: string): Intl.RelativeTimeFormat {\n const key = locale ?? \"__default__\";\n let cached = relativeTimeFormatterCache.get(key);\n if (cached == null) {\n cached = new Intl.RelativeTimeFormat(locale, { numeric: \"auto\" });\n relativeTimeFormatterCache.set(key, cached);\n }\n return cached;\n}\n\nexport function defaultFormatRelative(date: Date): string {\n const rtf = getRelativeTimeFormatter();\n // Wall-clock comparison to \"now\" (e.g. \"5 minutes ago\"). performance.now()\n // would be wrong here — it's a monotonic timer, not a wall-clock instant.\n let duration = (date.getTime() - Date.now()) / 1000;\n for (const div of DIVISIONS) {\n if (Math.abs(duration) < div.amount) return rtf.format(Math.round(duration), div.unit);\n duration /= div.amount;\n }\n return rtf.format(Math.round(duration), \"year\");\n}\n\nexport function defaultFormatAbsolute(date: Date): string {\n return date.toLocaleString();\n}\n\nexport function formatGridDate(\n value: unknown,\n mode: DataGridDateDisplay,\n opts?: {\n parseValue?: (value: unknown) => Date | null;\n dateFormat?: DataGridDateFormat;\n },\n): { display: string | null; tooltip: string | null } {\n const parse = opts?.parseValue ?? defaultParseDate;\n const date = parse(value);\n if (!date) return { display: null, tooltip: null };\n const relative = opts?.dateFormat?.relative ?? defaultFormatRelative;\n const absolute = opts?.dateFormat?.absolute ?? defaultFormatAbsolute;\n const tooltip = absolute(date);\n const display = mode === \"relative\" ? relative(date) : tooltip;\n return { display, tooltip };\n}\n\n// ─── CSV Export ──────────────────────────────────────────────────────\n\nexport function exportToCsv<TRow>(\n rows: readonly TRow[],\n columns: readonly DataGridColumnDef<TRow>[],\n filename: string,\n): void {\n const header = columns.map((col) =>\n typeof col.header === \"string\" ? col.header : col.id,\n );\n const csvRows = rows.map((row) =>\n columns.map((col) => {\n const val = resolveColumnValue(col, row);\n const formatted = col.formatValue\n ? String((col.formatValue(val, row) as string | null | undefined) ?? \"\")\n : String(val ?? \"\");\n if (formatted.includes(\",\") || formatted.includes('\"') || formatted.includes(\"\\n\")) {\n return `\"${formatted.replace(/\"/g, '\"\"')}\"`;\n }\n return formatted;\n }),\n );\n // UTF-8 BOM so Excel opens the CSV as UTF-8.\n const csvContent = \"\\uFEFF\" + [\n header.join(\",\"),\n ...csvRows.map((row) => row.join(\",\")),\n ].join(\"\\n\");\n\n const blob = new Blob([csvContent], { type: \"text/csv;charset=utf-8;\" });\n const url = URL.createObjectURL(blob);\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = `${filename}.csv`;\n document.body.appendChild(link);\n try {\n link.click();\n } finally {\n link.remove();\n URL.revokeObjectURL(url);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"state.js","names":[],"sources":["../../../../src/components/data-grid/state.ts"],"sourcesContent":["import { stringCompare } from \"@hexclave/shared/dist/utils/strings\";\nimport { clampColumnWidth, DEFAULT_COL_WIDTH } from \"./data-grid-sizing\";\nimport type {\n DataGridColumnDef,\n DataGridDateDisplay,\n DataGridDateFormat,\n DataGridPaginationModel,\n DataGridSortModel,\n DataGridState,\n} from \"./types\";\n\n/**\n * Build the initial `DataGridState` for a set of columns. Pass this as the\n * lazy initializer to `useState` — never hand-assemble the state object.\n *\n * ```tsx\n * const [gridState, setGridState] = React.useState(() =>\n * createDefaultDataGridState(columns)\n * );\n * ```\n */\nexport function createDefaultDataGridState(\n columns: readonly DataGridColumnDef<any>[],\n): DataGridState {\n const columnWidths: Record<string, number> = {};\n const columnOrder: string[] = [];\n\n for (const col of columns) {\n columnWidths[col.id] = clampColumnWidth(col, col.width ?? DEFAULT_COL_WIDTH);\n columnOrder.push(col.id);\n }\n\n return {\n sorting: [],\n columnVisibility: {},\n columnWidths,\n columnPinning: { left: [], right: [] },\n columnOrder,\n pagination: { pageIndex: 0, pageSize: 50 },\n selection: { selectedIds: new Set(), anchorId: null },\n dateDisplay: \"relative\",\n quickSearch: \"\",\n };\n}\n\n// ─── Column value resolution ─────────────────────────────────────────\n\nexport function resolveColumnValue<TRow>(\n col: DataGridColumnDef<TRow>,\n row: TRow,\n): unknown {\n if (typeof col.accessor === \"function\") return col.accessor(row);\n const key = (col.accessor ?? col.id) as keyof TRow;\n return row[key];\n}\n\n// ─── Default sort comparator (used by client-mode useDataSource) ────\n\nfunction defaultComparator(a: unknown, b: unknown): number {\n if (a == null && b == null) return 0;\n if (a == null) return -1;\n if (b == null) return 1;\n if (typeof a === \"number\" && typeof b === \"number\") return a - b;\n if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();\n return stringCompare(String(a), String(b));\n}\n\nexport function buildRowComparator<TRow>(\n sortModel: DataGridSortModel,\n columns: readonly DataGridColumnDef<TRow>[],\n): ((a: TRow, b: TRow) => number) | null {\n if (sortModel.length === 0) return null;\n const colMap = new Map(columns.map((c) => [c.id, c]));\n return (a, b) => {\n for (const { columnId, direction } of sortModel) {\n const col = colMap.get(columnId);\n if (!col) continue;\n const va = resolveColumnValue(col, a);\n const vb = resolveColumnValue(col, b);\n const cmp = col.sortComparator ? col.sortComparator(va, vb) : defaultComparator(va, vb);\n if (cmp !== 0) return direction === \"asc\" ? cmp : -cmp;\n }\n return 0;\n };\n}\n\n// ─── Pagination ──────────────────────────────────────────────────────\n\nexport function paginateRows<TRow>(\n rows: readonly TRow[],\n pagination: DataGridPaginationModel,\n): TRow[] {\n const start = pagination.pageIndex * pagination.pageSize;\n return rows.slice(start, start + pagination.pageSize) as TRow[];\n}\n\n// ─── Quick search ────────────────────────────────────────────────────\n\n/** Default row matcher: case-insensitive substring across every column. */\nexport function defaultMatchRow<TRow>(\n row: TRow,\n query: string,\n columns: readonly DataGridColumnDef<TRow>[],\n): boolean {\n for (const col of columns) {\n const v = resolveColumnValue(col, row);\n if (v == null) continue;\n if (String(v).toLowerCase().includes(query)) return true;\n }\n return false;\n}\n\nexport function applyQuickSearch<TRow>(\n rows: readonly TRow[],\n query: string,\n columns: readonly DataGridColumnDef<TRow>[],\n matchRow: (\n row: TRow,\n query: string,\n columns: readonly DataGridColumnDef<TRow>[],\n ) => boolean = defaultMatchRow,\n): readonly TRow[] {\n const trimmed = query.trim().toLowerCase();\n if (!trimmed) return rows;\n return rows.filter((r) => matchRow(r, trimmed, columns));\n}\n\n// ─── Date helpers ────────────────────────────────────────────────────\n\nexport function defaultParseDate(value: unknown): Date | null {\n if (value == null) return null;\n if (value instanceof Date) return isNaN(value.getTime()) ? null : value;\n if (typeof value === \"number\" || typeof value === \"string\") {\n const d = new Date(value);\n return isNaN(d.getTime()) ? null : d;\n }\n return null;\n}\n\nconst DIVISIONS: Array<{ amount: number; unit: Intl.RelativeTimeFormatUnit }> = [\n { amount: 60, unit: \"second\" },\n { amount: 60, unit: \"minute\" },\n { amount: 24, unit: \"hour\" },\n { amount: 7, unit: \"day\" },\n { amount: 4.34524, unit: \"week\" },\n { amount: 12, unit: \"month\" },\n { amount: Number.POSITIVE_INFINITY, unit: \"year\" },\n];\n\nconst relativeTimeFormatterCache = new Map<string, Intl.RelativeTimeFormat>();\nfunction getRelativeTimeFormatter(locale?: string): Intl.RelativeTimeFormat {\n const key = locale ?? \"__default__\";\n let cached = relativeTimeFormatterCache.get(key);\n if (cached == null) {\n cached = new Intl.RelativeTimeFormat(locale, { numeric: \"auto\" });\n relativeTimeFormatterCache.set(key, cached);\n }\n return cached;\n}\n\nexport function defaultFormatRelative(date: Date): string {\n const rtf = getRelativeTimeFormatter();\n // Wall-clock comparison to \"now\" (e.g. \"5 minutes ago\"). performance.now()\n // would be wrong here — it's a monotonic timer, not a wall-clock instant.\n let duration = (date.getTime() - Date.now()) / 1000;\n for (const div of DIVISIONS) {\n if (Math.abs(duration) < div.amount) return rtf.format(Math.round(duration), div.unit);\n duration /= div.amount;\n }\n return rtf.format(Math.round(duration), \"year\");\n}\n\nexport function defaultFormatAbsolute(date: Date): string {\n return date.toLocaleString();\n}\n\nexport function formatGridDate(\n value: unknown,\n mode: DataGridDateDisplay,\n opts?: {\n parseValue?: (value: unknown) => Date | null;\n dateFormat?: DataGridDateFormat;\n },\n): { display: string | null; tooltip: string | null } {\n const parse = opts?.parseValue ?? defaultParseDate;\n const date = parse(value);\n if (!date) return { display: null, tooltip: null };\n const relative = opts?.dateFormat?.relative ?? defaultFormatRelative;\n const absolute = opts?.dateFormat?.absolute ?? defaultFormatAbsolute;\n const tooltip = absolute(date);\n const display = mode === \"relative\" ? relative(date) : tooltip;\n return { display, tooltip };\n}\n\n// ─── CSV Export ──────────────────────────────────────────────────────\n\nexport function exportToCsv<TRow>(\n rows: readonly TRow[],\n columns: readonly DataGridColumnDef<TRow>[],\n filename: string,\n): void {\n const header = columns.map((col) =>\n typeof col.header === \"string\" ? col.header : col.id,\n );\n const csvRows = rows.map((row) =>\n columns.map((col) => {\n const val = resolveColumnValue(col, row);\n const formatted = col.formatValue\n ? String((col.formatValue(val, row) as string | null | undefined) ?? \"\")\n : String(val ?? \"\");\n if (formatted.includes(\",\") || formatted.includes('\"') || formatted.includes(\"\\n\")) {\n return `\"${formatted.replace(/\"/g, '\"\"')}\"`;\n }\n return formatted;\n }),\n );\n // UTF-8 BOM so Excel opens the CSV as UTF-8.\n const csvContent = \"\\uFEFF\" + [\n header.join(\",\"),\n ...csvRows.map((row) => row.join(\",\")),\n ].join(\"\\n\");\n\n const blob = new Blob([csvContent], { type: \"text/csv;charset=utf-8;\" });\n const url = URL.createObjectURL(blob);\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = `${filename}.csv`;\n document.body.appendChild(link);\n try {\n link.click();\n } finally {\n link.remove();\n URL.revokeObjectURL(url);\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog.js","names":[],"sources":["../../../src/components/dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n Dialog,\n DialogBody,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n cn,\n} from \"@
|
|
1
|
+
{"version":3,"file":"dialog.js","names":[],"sources":["../../../src/components/dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n Dialog,\n DialogBody,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n cn,\n} from \"@hexclave/ui\";\nimport React from \"react\";\n\nexport type DesignDialogSize =\n | \"sm\"\n | \"md\"\n | \"lg\"\n | \"xl\"\n | \"2xl\"\n | \"3xl\"\n | \"4xl\"\n | \"5xl\"\n | \"6xl\"\n | \"7xl\"\n | \"full\";\n\nexport type DesignDialogVariant = \"glassmorphic\" | \"plain\";\n\nconst dialogSizeClasses = new Map<DesignDialogSize, string>([\n [\"sm\", \"max-w-sm\"],\n [\"md\", \"max-w-md\"],\n [\"lg\", \"max-w-lg\"],\n [\"xl\", \"max-w-xl\"],\n [\"2xl\", \"max-w-2xl\"],\n [\"3xl\", \"max-w-3xl\"],\n [\"4xl\", \"max-w-4xl\"],\n [\"5xl\", \"max-w-5xl\"],\n [\"6xl\", \"max-w-6xl\"],\n [\"7xl\", \"max-w-7xl\"],\n [\"full\", \"max-w-[calc(100vw-2rem)]\"],\n]);\n\nconst dialogSurfaceClasses = new Map<DesignDialogVariant, string>([\n [\"glassmorphic\", \"border-0 sm:rounded-2xl bg-background/85 backdrop-blur-2xl ring-1 ring-foreground/[0.06] shadow-[0_24px_48px_-12px_rgba(0,0,0,0.25),0_4px_24px_-8px_rgba(0,0,0,0.12)] dark:bg-background/80 dark:ring-white/[0.06]\"],\n [\"plain\", \"border bg-background shadow-lg sm:rounded-lg\"],\n]);\n\nconst dialogOverlayClasses = new Map<DesignDialogVariant, string | undefined>([\n [\"glassmorphic\", \"bg-black/50 backdrop-blur-sm\"],\n [\"plain\", undefined],\n]);\n\ntype DesignDialogIcon = React.ElementType<{ className?: string }>;\n\ntype DesignDialogRootProps = Omit<React.ComponentProps<typeof Dialog>, \"children\">;\n\nexport type DesignDialogProps = {\n trigger?: React.ReactElement,\n size?: DesignDialogSize,\n variant?: DesignDialogVariant,\n icon?: DesignDialogIcon | null,\n title?: React.ReactNode,\n description?: React.ReactNode,\n headerContent?: React.ReactNode,\n customHeader?: React.ReactNode,\n footer?: React.ReactNode,\n noBodyPadding?: boolean,\n hideTopCloseButton?: boolean,\n className?: string,\n overlayClassName?: string,\n headerClassName?: string,\n bodyClassName?: string,\n footerClassName?: string,\n children?: React.ReactNode,\n} & DesignDialogRootProps;\n\n/**\n * Canonical dashboard modal surface. This wraps the base dialog primitives with\n * a reusable glassmorphic shell and consistent header/body/footer regions.\n */\nexport function DesignDialog({\n trigger,\n size = \"lg\",\n variant = \"glassmorphic\",\n icon: Icon = null,\n title,\n description,\n headerContent,\n customHeader,\n footer,\n noBodyPadding = false,\n hideTopCloseButton = false,\n className,\n overlayClassName,\n headerClassName,\n bodyClassName,\n footerClassName,\n children,\n ...dialogRootProps\n}: DesignDialogProps) {\n const resolvedSizeClass = dialogSizeClasses.get(size) ?? \"max-w-lg\";\n const resolvedSurfaceClass = dialogSurfaceClasses.get(variant) ?? dialogSurfaceClasses.get(\"glassmorphic\");\n const resolvedOverlayClass = cn(dialogOverlayClasses.get(variant), overlayClassName);\n const shouldRenderTopHeaderRow = Icon != null || title != null || description != null;\n const shouldRenderHeader = customHeader != null || shouldRenderTopHeaderRow || headerContent != null;\n // Use toArray + filter(Boolean) instead of Children.count so that\n // expressions like `{condition && <Foo/>}` resolving to `false` don't\n // produce an empty DialogBody (which would still render padding/borders).\n const shouldRenderBody = React.Children.toArray(children).filter(Boolean).length > 0;\n const hasStandardTitle = title != null;\n const needsAccessibleTitleFallback = !hasStandardTitle && customHeader == null;\n\n if (process.env.NODE_ENV !== \"production\" && needsAccessibleTitleFallback) {\n console.warn(\n \"[DesignDialog] Rendered without a `title` or `customHeader`. Every dialog needs an accessible name — pass `title`, or render a `DialogTitle` inside `customHeader`.\",\n );\n }\n\n return (\n <Dialog {...dialogRootProps}>\n {trigger != null && (\n <DialogTrigger asChild>\n {trigger}\n </DialogTrigger>\n )}\n\n <DialogContent\n className={cn(\n \"gap-0 p-0 overflow-hidden\",\n resolvedSizeClass,\n resolvedSurfaceClass,\n className\n )}\n overlayProps={resolvedOverlayClass ? { className: resolvedOverlayClass } : undefined}\n noCloseButton={hideTopCloseButton}\n >\n {needsAccessibleTitleFallback && (\n <DialogTitle className=\"sr-only\">Dialog</DialogTitle>\n )}\n {shouldRenderHeader && (\n <DialogHeader className={cn(\"px-6 pt-6 pb-4 border-b border-foreground/[0.06]\", headerClassName)}>\n {customHeader ?? (\n <>\n {shouldRenderTopHeaderRow && (\n <div className={cn(\"flex items-start gap-3\", Icon == null && \"gap-0\")}>\n {Icon != null && (\n <div className=\"h-9 w-9 rounded-xl bg-primary/10 ring-1 ring-primary/15 flex items-center justify-center shrink-0\">\n <Icon className=\"h-4 w-4 text-primary\" />\n </div>\n )}\n {(title != null || description != null) && (\n <div className=\"flex-1 min-w-0 space-y-1\">\n {title != null ? (\n <DialogTitle className=\"text-base\">\n {title}\n </DialogTitle>\n ) : null}\n {description != null ? (\n <DialogDescription className=\"text-xs\">\n {description}\n </DialogDescription>\n ) : null}\n </div>\n )}\n </div>\n )}\n\n {headerContent != null ? (\n <div className={cn(shouldRenderTopHeaderRow && \"mt-4\")}>\n {headerContent}\n </div>\n ) : null}\n </>\n )}\n </DialogHeader>\n )}\n\n {shouldRenderBody && (\n <DialogBody\n className={cn(\n \"mx-0 my-0 w-auto\",\n noBodyPadding ? \"px-0 py-0\" : \"px-6 py-4\",\n bodyClassName\n )}\n >\n {children}\n </DialogBody>\n )}\n\n {footer != null ? (\n <DialogFooter className={cn(\"px-6 py-3 border-t border-foreground/[0.06] bg-foreground/[0.02]\", footerClassName)}>\n {footer}\n </DialogFooter>\n ) : null}\n </DialogContent>\n </Dialog>\n );\n}\n\nexport const DesignDialogRoot = Dialog;\nexport const DesignDialogTrigger = DialogTrigger;\nexport const DesignDialogClose = DialogClose;\nexport const DesignDialogTitle = DialogTitle;\nexport const DesignDialogDescription = DialogDescription;\n"],"mappings":";;;;;;;AA+BA,MAAM,oBAAoB,IAAI,IAA8B;CAC1D,CAAC,MAAM,WAAW;CAClB,CAAC,MAAM,WAAW;CAClB,CAAC,MAAM,WAAW;CAClB,CAAC,MAAM,WAAW;CAClB,CAAC,OAAO,YAAY;CACpB,CAAC,OAAO,YAAY;CACpB,CAAC,OAAO,YAAY;CACpB,CAAC,OAAO,YAAY;CACpB,CAAC,OAAO,YAAY;CACpB,CAAC,OAAO,YAAY;CACpB,CAAC,QAAQ,2BAA2B;CACrC,CAAC;AAEF,MAAM,uBAAuB,IAAI,IAAiC,CAChE,CAAC,gBAAgB,qNAAqN,EACtO,CAAC,SAAS,+CAA+C,CAC1D,CAAC;AAEF,MAAM,uBAAuB,IAAI,IAA6C,CAC5E,CAAC,gBAAgB,+BAA+B,EAChD,CAAC,SAAS,OAAU,CACrB,CAAC;;;;;AA8BF,SAAgB,aAAa,EAC3B,SACA,OAAO,MACP,UAAU,gBACV,MAAM,OAAO,MACb,OACA,aACA,eACA,cACA,QACA,gBAAgB,OAChB,qBAAqB,OACrB,WACA,kBACA,iBACA,eACA,iBACA,UACA,GAAG,mBACiB;CACpB,MAAM,oBAAoB,kBAAkB,IAAI,KAAK,IAAI;CACzD,MAAM,uBAAuB,qBAAqB,IAAI,QAAQ,IAAI,qBAAqB,IAAI,eAAe;CAC1G,MAAM,uBAAuB,GAAG,qBAAqB,IAAI,QAAQ,EAAE,iBAAiB;CACpF,MAAM,2BAA2B,QAAQ,QAAQ,SAAS,QAAQ,eAAe;CACjF,MAAM,qBAAqB,gBAAgB,QAAQ,4BAA4B,iBAAiB;CAIhG,MAAM,mBAAmB,MAAM,SAAS,QAAQ,SAAS,CAAC,OAAO,QAAQ,CAAC,SAAS;CAEnF,MAAM,+BAA+B,EADZ,SAAS,SACwB,gBAAgB;AAE1E,KAAI,QAAQ,IAAI,aAAa,gBAAgB,6BAC3C,SAAQ,KACN,sKACD;AAGH,QACE,qBAAC;EAAO,GAAI;aACT,WAAW,QACV,oBAAC;GAAc;aACZ;IACa,EAGlB,qBAAC;GACC,WAAW,GACT,6BACA,mBACA,sBACA,UACD;GACD,cAAc,uBAAuB,EAAE,WAAW,sBAAsB,GAAG;GAC3E,eAAe;;IAEd,gCACC,oBAAC;KAAY,WAAU;eAAU;MAAoB;IAEtD,sBACC,oBAAC;KAAa,WAAW,GAAG,oDAAoD,gBAAgB;eAC7F,gBACC,4CACG,4BACC,qBAAC;MAAI,WAAW,GAAG,0BAA0B,QAAQ,QAAQ,QAAQ;iBAClE,QAAQ,QACP,oBAAC;OAAI,WAAU;iBACb,oBAAC,QAAK,WAAU,yBAAyB;QACrC,GAEN,SAAS,QAAQ,eAAe,SAChC,qBAAC;OAAI,WAAU;kBACZ,SAAS,OACR,oBAAC;QAAY,WAAU;kBACpB;SACW,GACZ,MACH,eAAe,OACd,oBAAC;QAAkB,WAAU;kBAC1B;SACiB,GAClB;QACA;OAEJ,EAGP,iBAAiB,OAChB,oBAAC;MAAI,WAAW,GAAG,4BAA4B,OAAO;gBACnD;OACG,GACJ,QACH;MAEQ;IAGhB,oBACC,oBAAC;KACC,WAAW,GACT,oBACA,gBAAgB,cAAc,aAC9B,cACD;KAEA;MACU;IAGd,UAAU,OACT,oBAAC;KAAa,WAAW,GAAG,oEAAoE,gBAAgB;eAC7G;MACY,GACb;;IACU;GACT;;AAIb,MAAa,mBAAmB;AAChC,MAAa,sBAAsB;AACnC,MAAa,oBAAoB;AACjC,MAAa,oBAAoB;AACjC,MAAa,0BAA0B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"empty-state.js","names":[],"sources":["../../../src/components/empty-state.tsx"],"sourcesContent":["\"use client\";\n\nimport { cn } from \"@
|
|
1
|
+
{"version":3,"file":"empty-state.js","names":[],"sources":["../../../src/components/empty-state.tsx"],"sourcesContent":["\"use client\";\n\nimport { cn } from \"@hexclave/ui\";\nimport React from \"react\";\n\nexport type DesignEmptyStateProps = {\n icon?: React.ElementType,\n title?: string,\n description?: string,\n children?: React.ReactNode,\n className?: string,\n};\n\n/**\n * Centered \"no data\" placeholder. Show this inside a `DataGrid` via the\n * `emptyState` prop, inside a chart when a query returns zero rows, or\n * inside a card when a section has nothing to display.\n *\n * ```tsx\n * <DesignEmptyState\n * icon={SearchIcon}\n * title=\"No results\"\n * description=\"Try adjusting your filters.\"\n * />\n * ```\n *\n * Prefer this over a raw \"No data\" div — it handles spacing, typography,\n * and the optional icon for you. `icon` is a component type, not a rendered node.\n */\nexport function DesignEmptyState({\n icon: Icon,\n title = \"No data available\",\n description,\n children,\n className,\n}: DesignEmptyStateProps) {\n return (\n <div\n className={cn(\n \"flex flex-col items-center justify-center py-12 px-6 text-center\",\n className\n )}\n >\n {Icon && (\n <div className=\"mb-4\">\n <Icon className=\"h-10 w-10 text-muted-foreground/30\" />\n </div>\n )}\n <h3 className=\"text-sm font-medium text-foreground\">\n {title}\n </h3>\n {description && (\n <p className=\"mt-1 text-xs text-muted-foreground max-w-[280px]\">\n {description}\n </p>\n )}\n {children && (\n <div className=\"mt-4\">\n {children}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAgB,iBAAiB,EAC/B,MAAM,MACN,QAAQ,qBACR,aACA,UACA,aACwB;AACxB,QACE,qBAAC;EACC,WAAW,GACT,oEACA,UACD;;GAEA,QACC,oBAAC;IAAI,WAAU;cACb,oBAAC,QAAK,WAAU,uCAAuC;KACnD;GAER,oBAAC;IAAG,WAAU;cACX;KACE;GACJ,eACC,oBAAC;IAAE,WAAU;cACV;KACC;GAEL,YACC,oBAAC;IAAI,WAAU;IACZ;KACG;;GAEJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.js","names":[],"sources":["../../../src/components/input.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRefIfNeeded } from \"@
|
|
1
|
+
{"version":3,"file":"input.js","names":[],"sources":["../../../src/components/input.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRefIfNeeded } from \"@hexclave/shared/dist/utils/react\";\nimport React from \"react\";\n\nimport { cn } from \"@hexclave/ui\";\n\nexport type DesignInputProps = {\n prefixItem?: React.ReactNode,\n leadingIcon?: React.ReactNode,\n size?: \"sm\" | \"md\" | \"lg\",\n} & Omit<React.InputHTMLAttributes<HTMLInputElement>, \"size\">;\n\nexport const DesignInput = forwardRefIfNeeded<HTMLInputElement, DesignInputProps>(\n ({ className, type, prefixItem, leadingIcon, size = \"md\", ...props }, ref) => {\n const sizeClasses = size === \"sm\"\n ? \"h-7 px-2 text-xs\"\n : size === \"lg\"\n ? \"h-10 px-4 text-sm\"\n : \"h-9 px-3 text-sm\";\n const baseClasses = cn(\n \"stack-scope flex w-full rounded-xl border border-black/[0.08] dark:border-white/[0.06] bg-white/80 dark:bg-foreground/[0.03] shadow-sm ring-1 ring-black/[0.08] dark:ring-white/[0.06]\",\n \"file:border-0 file:bg-transparent file:text-sm file:font-medium\",\n \"placeholder:text-muted-foreground/50 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-foreground/[0.1]\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n \"transition-all duration-150 hover:transition-none hover:bg-white dark:hover:bg-foreground/[0.06]\",\n sizeClasses\n );\n\n if (prefixItem) {\n return (\n <div className=\"flex flex-row items-center flex-1 rounded-xl border border-black/[0.08] dark:border-white/[0.06] bg-white/80 dark:bg-foreground/[0.03] shadow-sm ring-1 ring-black/[0.08] dark:ring-white/[0.06] overflow-hidden transition-all duration-150 hover:transition-none hover:bg-white dark:hover:bg-foreground/[0.06] focus-within:ring-1 focus-within:ring-foreground/[0.1]\">\n <div className={cn(\n \"flex self-stretch items-center justify-center select-none text-muted-foreground/70 border-r border-black/[0.06] dark:border-white/[0.06] bg-black/[0.03] dark:bg-white/[0.02]\",\n size === \"sm\" ? \"px-2.5 text-xs\" : size === \"lg\" ? \"px-3.5 text-sm\" : \"px-3 text-sm\"\n )}>\n {prefixItem}\n </div>\n <input\n type={type}\n className={cn(\n \"stack-scope flex w-full bg-transparent\",\n \"file:border-0 file:bg-transparent file:text-sm file:font-medium\",\n \"placeholder:text-muted-foreground/50 focus-visible:outline-none\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n sizeClasses,\n \"rounded-none border-0 shadow-none ring-0 focus-visible:ring-0\",\n className\n )}\n ref={ref}\n {...props}\n />\n </div>\n );\n }\n\n if (leadingIcon) {\n return (\n <div className=\"relative flex flex-row items-center flex-1\">\n <div className=\"pointer-events-none absolute left-2.5 flex items-center text-muted-foreground\">\n {leadingIcon}\n </div>\n <input\n type={type}\n className={cn(baseClasses, \"pl-8\", className)}\n ref={ref}\n {...props}\n />\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-row items-center flex-1\">\n <input\n type={type}\n className={cn(baseClasses, className)}\n ref={ref}\n {...props}\n />\n </div>\n );\n }\n);\nDesignInput.displayName = \"DesignInput\";\n"],"mappings":";;;;;;;;AAaA,MAAa,cAAc,oBACxB,EAAE,WAAW,MAAM,YAAY,aAAa,OAAO,MAAM,GAAG,SAAS,QAAQ;CAC5E,MAAM,cAAc,SAAS,OACzB,qBACA,SAAS,OACP,sBACA;CACN,MAAM,cAAc,GAClB,0LACA,mEACA,4HACA,mDACA,oGACA,YACD;AAED,KAAI,WACF,QACE,qBAAC;EAAI,WAAU;aACb,oBAAC;GAAI,WAAW,GACd,iLACA,SAAS,OAAO,mBAAmB,SAAS,OAAO,mBAAmB,eACvE;aACE;IACG,EACN,oBAAC;GACO;GACN,WAAW,GACT,0CACA,mEACA,mEACA,mDACA,aACA,iEACA,UACD;GACI;GACL,GAAI;IACJ;GACE;AAIV,KAAI,YACF,QACE,qBAAC;EAAI,WAAU;aACb,oBAAC;GAAI,WAAU;aACZ;IACG,EACN,oBAAC;GACO;GACN,WAAW,GAAG,aAAa,QAAQ,UAAU;GACxC;GACL,GAAI;IACJ;GACE;AAIV,QACE,oBAAC;EAAI,WAAU;YACb,oBAAC;GACO;GACN,WAAW,GAAG,aAAa,UAAU;GAChC;GACL,GAAI;IACJ;GACE;EAGX;AACD,YAAY,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metric-card.js","names":[],"sources":["../../../src/components/metric-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { cn } from \"@
|
|
1
|
+
{"version":3,"file":"metric-card.js","names":[],"sources":["../../../src/components/metric-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { cn } from \"@hexclave/ui\";\nimport React from \"react\";\n\ntype DesignMetricCardGradient = \"blue\" | \"cyan\" | \"purple\" | \"green\" | \"orange\" | \"default\";\n\nconst hoverTintClasses = new Map<DesignMetricCardGradient, string>([\n [\"blue\", \"group-hover:bg-blue-500/[0.03]\"],\n [\"cyan\", \"group-hover:bg-cyan-500/[0.03]\"],\n [\"purple\", \"group-hover:bg-purple-500/[0.03]\"],\n [\"green\", \"group-hover:bg-emerald-500/[0.03]\"],\n [\"orange\", \"group-hover:bg-orange-500/[0.03]\"],\n [\"default\", \"group-hover:bg-slate-500/[0.02]\"],\n]);\n\nexport type DesignMetricCardTrend = {\n value: number,\n direction: \"up\" | \"down\",\n label?: string,\n};\n\nexport type DesignMetricCardProps = {\n label: string,\n value: string | number,\n description?: string,\n trend?: DesignMetricCardTrend,\n icon?: React.ElementType,\n gradient?: DesignMetricCardGradient,\n} & Omit<React.ComponentProps<\"div\">, \"children\">;\n\n/**\n * KPI card for big-number metrics (users, revenue, signups, etc.).\n * Use this instead of a plain `DesignCard` when the value is the focal point.\n *\n * ```tsx\n * <DesignMetricCard\n * label=\"Total Users\"\n * value=\"1,234\"\n * description=\"+12% from last month\"\n * trend={{ direction: \"up\", value: 12, label: \"vs. last period\" }}\n * icon={UsersIcon}\n * gradient=\"blue\"\n * />\n * ```\n *\n * Notes:\n * - `label` is the short caption (NOT `title`).\n * - `value` can be a pre-formatted string (\"1,234\") or a number — prefer\n * strings so you control the format.\n * - `description` is the subline (NOT `subtitle`).\n * - `trend.value` is a NUMBER, not a pre-formatted \"12%\" string. The card\n * renders the arrow and formatting.\n * - `icon` is a component type (e.g. `UsersIcon`), not a rendered node.\n */\nexport function DesignMetricCard({\n label,\n value,\n description,\n trend,\n icon: Icon,\n gradient = \"default\",\n className,\n ...props\n}: DesignMetricCardProps) {\n const hoverTintClass = hoverTintClasses.get(gradient) ?? \"group-hover:bg-slate-500/[0.02]\";\n\n return (\n <div\n className={cn(\n \"group relative rounded-2xl bg-white/90 dark:bg-background/60 backdrop-blur-xl transition-all duration-150 hover:transition-none overflow-hidden\",\n \"ring-1 ring-black/[0.06] hover:ring-black/[0.1] dark:ring-white/[0.06] dark:hover:ring-white/[0.1]\",\n \"shadow-sm hover:shadow-md\",\n className\n )}\n {...props}\n >\n <div className=\"absolute inset-0 bg-gradient-to-br from-foreground/[0.04] dark:from-foreground/[0.02] to-transparent pointer-events-none rounded-2xl\" />\n <div\n className={cn(\n \"absolute inset-0 transition-colors duration-150 group-hover:transition-none pointer-events-none rounded-2xl\",\n hoverTintClass\n )}\n />\n <div className=\"relative p-5\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n {Icon && (\n <div className=\"p-1.5 rounded-lg bg-foreground/[0.06] dark:bg-foreground/[0.04]\">\n <Icon className=\"h-3.5 w-3.5 text-foreground/70 dark:text-muted-foreground\" />\n </div>\n )}\n <span className=\"text-xs font-semibold text-muted-foreground uppercase tracking-wider\">\n {label}\n </span>\n </div>\n <div className=\"mt-3 flex items-baseline gap-2\">\n <span className=\"text-3xl font-bold tabular-nums text-foreground\">\n {typeof value === \"number\" ? value.toLocaleString() : value}\n </span>\n {trend && (\n <span\n className={cn(\n \"inline-flex items-center gap-0.5 text-xs font-medium\",\n trend.direction === \"up\"\n ? \"text-emerald-600 dark:text-emerald-400\"\n : \"text-red-600 dark:text-red-400\"\n )}\n >\n <svg\n className={cn(\"h-3 w-3\", trend.direction === \"down\" && \"rotate-180\")}\n viewBox=\"0 0 12 12\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M6 2.5V9.5M6 2.5L3 5.5M6 2.5L9 5.5\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n {trend.value}%\n {trend.label && (\n <span className=\"text-muted-foreground ml-0.5\">{trend.label}</span>\n )}\n </span>\n )}\n </div>\n {description && (\n <p className=\"mt-1 text-xs text-muted-foreground\">\n {description}\n </p>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;AAOA,MAAM,mBAAmB,IAAI,IAAsC;CACjE,CAAC,QAAQ,iCAAiC;CAC1C,CAAC,QAAQ,iCAAiC;CAC1C,CAAC,UAAU,mCAAmC;CAC9C,CAAC,SAAS,oCAAoC;CAC9C,CAAC,UAAU,mCAAmC;CAC9C,CAAC,WAAW,kCAAkC;CAC/C,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;AAyCF,SAAgB,iBAAiB,EAC/B,OACA,OACA,aACA,OACA,MAAM,MACN,WAAW,WACX,WACA,GAAG,SACqB;CACxB,MAAM,iBAAiB,iBAAiB,IAAI,SAAS,IAAI;AAEzD,QACE,qBAAC;EACC,WAAW,GACT,mJACA,sGACA,6BACA,UACD;EACD,GAAI;;GAEJ,oBAAC,SAAI,WAAU,yIAAyI;GACxJ,oBAAC,SACC,WAAW,GACT,+GACA,eACD,GACD;GACF,oBAAC;IAAI,WAAU;cACb,oBAAC;KAAI,WAAU;eACb,qBAAC;MAAI,WAAU;;OACb,qBAAC;QAAI,WAAU;mBACZ,QACC,oBAAC;SAAI,WAAU;mBACb,oBAAC,QAAK,WAAU,8DAA8D;UAC1E,EAER,oBAAC;SAAK,WAAU;mBACb;UACI;SACH;OACN,qBAAC;QAAI,WAAU;mBACb,oBAAC;SAAK,WAAU;mBACb,OAAO,UAAU,WAAW,MAAM,gBAAgB,GAAG;UACjD,EACN,SACC,qBAAC;SACC,WAAW,GACT,wDACA,MAAM,cAAc,OAChB,2CACA,iCACL;;UAED,oBAAC;WACC,WAAW,GAAG,WAAW,MAAM,cAAc,UAAU,aAAa;WACpE,SAAQ;WACR,MAAK;WACL,OAAM;qBAEN,oBAAC;YACC,GAAE;YACF,QAAO;YACP,aAAY;YACZ,eAAc;YACd,gBAAe;aACf;YACE;UACL,MAAM;UAAM;UACZ,MAAM,SACL,oBAAC;WAAK,WAAU;qBAAgC,MAAM;YAAa;;UAEhE;SAEL;OACL,eACC,oBAAC;QAAE,WAAU;kBACV;SACC;;OAEF;MACF;KACF;;GACF"}
|
|
@@ -53,6 +53,7 @@ function DesignPillToggle({ options, selected, onSelect, size = "md", glassmorph
|
|
|
53
53
|
const isActive = selected === option.id;
|
|
54
54
|
const Icon = option.icon;
|
|
55
55
|
const pill = /* @__PURE__ */ jsxs("button", {
|
|
56
|
+
type: "button",
|
|
56
57
|
onClick: () => handleClick(option.id),
|
|
57
58
|
disabled: loadingOptionId !== null,
|
|
58
59
|
className: cn("relative flex items-center gap-2 font-medium rounded-lg transition-all duration-150 hover:transition-none", sizeClass.button, isActive ? cn("bg-background text-foreground shadow-sm ring-1", glassmorphic ? "ring-foreground/[0.06] dark:bg-[hsl(240,71%,70%)]/10 dark:text-[hsl(240,71%,90%)] dark:ring-[hsl(240,71%,70%)]/20" : activeRingClass) : cn("text-muted-foreground hover:text-foreground", glassmorphic ? "hover:bg-background/50" : "hover:bg-black/[0.06] dark:hover:bg-white/[0.04]")),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pill-toggle.js","names":[],"sources":["../../../src/components/pill-toggle.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport { cn, Spinner, Tooltip, TooltipContent, TooltipTrigger } from \"@
|
|
1
|
+
{"version":3,"file":"pill-toggle.js","names":[],"sources":["../../../src/components/pill-toggle.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport { cn, Spinner, Tooltip, TooltipContent, TooltipTrigger } from \"@hexclave/ui\";\nimport { TooltipPortal } from \"@radix-ui/react-tooltip\";\nimport { runAsynchronouslyWithAlert } from \"@hexclave/shared/dist/utils/promises\";\nimport { useGlassmorphicDefault } from \"./card\";\n\ntype DesignPillToggleSize = \"sm\" | \"md\" | \"lg\";\ntype DesignPillToggleGradient = \"blue\" | \"cyan\" | \"purple\" | \"green\" | \"orange\" | \"default\";\n\nexport type DesignPillToggleOption = {\n id: string,\n label: string,\n icon?: React.ElementType,\n};\n\nexport type DesignPillToggleProps = {\n options: DesignPillToggleOption[],\n selected: string,\n onSelect: (id: string) => void | Promise<void>,\n size?: DesignPillToggleSize,\n glassmorphic?: boolean,\n gradient?: DesignPillToggleGradient,\n /** When false, hides labels and shows a tooltip on hover instead. Defaults to true. */\n showLabels?: boolean,\n className?: string,\n};\n\ntype SizeClass = {\n button: string,\n icon: string,\n};\n\nconst sizeClasses = new Map<DesignPillToggleSize, SizeClass>([\n [\"sm\", { button: \"px-3 py-1.5 text-xs\", icon: \"h-3.5 w-3.5\" }],\n [\"md\", { button: \"px-4 py-2 text-sm\", icon: \"h-4 w-4\" }],\n [\"lg\", { button: \"px-5 py-2.5 text-sm\", icon: \"h-4 w-4\" }],\n]);\n\nconst gradientClasses = new Map<DesignPillToggleGradient, string>([\n [\"blue\", \"ring-blue-500/20 dark:ring-blue-400/20\"],\n [\"cyan\", \"ring-cyan-500/20 dark:ring-cyan-400/20\"],\n [\"purple\", \"ring-purple-500/20 dark:ring-purple-400/20\"],\n [\"green\", \"ring-emerald-500/20 dark:ring-emerald-400/20\"],\n [\"orange\", \"ring-amber-500/20 dark:ring-amber-400/20\"],\n [\"default\", \"ring-black/[0.12] dark:ring-white/[0.06]\"],\n]);\n\nfunction getMapValueOrThrow<TKey, TValue>(map: Map<TKey, TValue>, key: TKey, mapName: string) {\n const value = map.get(key);\n if (!value) {\n throw new Error(`Missing ${mapName} entry for key \"${String(key)}\"`);\n }\n return value;\n}\n\nexport function DesignPillToggle({\n options,\n selected,\n onSelect,\n size = \"md\",\n glassmorphic: glassmorphicProp,\n gradient = \"default\",\n showLabels = true,\n className,\n}: DesignPillToggleProps) {\n const glassmorphic = useGlassmorphicDefault(glassmorphicProp);\n const sizeClass = getMapValueOrThrow(sizeClasses, size, \"sizeClasses\");\n const activeRingClass = getMapValueOrThrow(gradientClasses, gradient, \"gradientClasses\");\n\n const [loadingOptionId, setLoadingOptionId] = useState<string | null>(null);\n\n const handleClick = (optionId: string) => {\n const result = onSelect(optionId);\n if (result && typeof (result as Promise<void>).then === \"function\") {\n setLoadingOptionId(optionId);\n runAsynchronouslyWithAlert(\n Promise.resolve(result).finally(() => setLoadingOptionId(null))\n );\n }\n };\n\n return (\n <div\n className={cn(\n \"inline-flex items-center gap-1 p-1 rounded-xl\",\n glassmorphic\n ? \"bg-foreground/[0.04] backdrop-blur-sm\"\n : \"bg-black/[0.08] dark:bg-white/[0.04]\",\n className\n )}\n >\n {options.map((option) => {\n const isActive = selected === option.id;\n const Icon = option.icon;\n\n const pill = (\n <button\n type=\"button\"\n key={option.id}\n onClick={() => handleClick(option.id)}\n disabled={loadingOptionId !== null}\n className={cn(\n \"relative flex items-center gap-2 font-medium rounded-lg transition-all duration-150 hover:transition-none\",\n sizeClass.button,\n isActive\n ? cn(\n \"bg-background text-foreground shadow-sm ring-1\",\n glassmorphic\n ? \"ring-foreground/[0.06] dark:bg-[hsl(240,71%,70%)]/10 dark:text-[hsl(240,71%,90%)] dark:ring-[hsl(240,71%,70%)]/20\"\n : activeRingClass\n )\n : cn(\n \"text-muted-foreground hover:text-foreground\",\n glassmorphic\n ? \"hover:bg-background/50\"\n : \"hover:bg-black/[0.06] dark:hover:bg-white/[0.04]\"\n )\n )}\n >\n {loadingOptionId === option.id && (\n <Spinner\n size={12}\n className=\"absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none\"\n />\n )}\n <span className={cn(\n \"flex items-center gap-2\",\n loadingOptionId === option.id && \"invisible\"\n )}>\n {Icon && <Icon className={sizeClass.icon} />}\n {showLabels && option.label}\n </span>\n </button>\n );\n\n if (!showLabels) {\n return (\n <Tooltip key={option.id} delayDuration={0}>\n <TooltipTrigger asChild>\n {pill}\n </TooltipTrigger>\n <TooltipPortal>\n <TooltipContent side=\"top\">\n {option.label}\n </TooltipContent>\n </TooltipPortal>\n </Tooltip>\n );\n }\n\n return pill;\n })}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;AAkCA,MAAM,cAAc,IAAI,IAAqC;CAC3D,CAAC,MAAM;EAAE,QAAQ;EAAuB,MAAM;EAAe,CAAC;CAC9D,CAAC,MAAM;EAAE,QAAQ;EAAqB,MAAM;EAAW,CAAC;CACxD,CAAC,MAAM;EAAE,QAAQ;EAAuB,MAAM;EAAW,CAAC;CAC3D,CAAC;AAEF,MAAM,kBAAkB,IAAI,IAAsC;CAChE,CAAC,QAAQ,yCAAyC;CAClD,CAAC,QAAQ,yCAAyC;CAClD,CAAC,UAAU,6CAA6C;CACxD,CAAC,SAAS,+CAA+C;CACzD,CAAC,UAAU,2CAA2C;CACtD,CAAC,WAAW,2CAA2C;CACxD,CAAC;AAEF,SAAS,mBAAiC,KAAwB,KAAW,SAAiB;CAC5F,MAAM,QAAQ,IAAI,IAAI,IAAI;AAC1B,KAAI,CAAC,MACH,OAAM,IAAI,MAAM,WAAW,QAAQ,kBAAkB,OAAO,IAAI,CAAC,GAAG;AAEtE,QAAO;;AAGT,SAAgB,iBAAiB,EAC/B,SACA,UACA,UACA,OAAO,MACP,cAAc,kBACd,WAAW,WACX,aAAa,MACb,aACwB;CACxB,MAAM,eAAe,uBAAuB,iBAAiB;CAC7D,MAAM,YAAY,mBAAmB,aAAa,MAAM,cAAc;CACtE,MAAM,kBAAkB,mBAAmB,iBAAiB,UAAU,kBAAkB;CAExF,MAAM,CAAC,iBAAiB,sBAAsB,SAAwB,KAAK;CAE3E,MAAM,eAAe,aAAqB;EACxC,MAAM,SAAS,SAAS,SAAS;AACjC,MAAI,UAAU,OAAQ,OAAyB,SAAS,YAAY;AAClE,sBAAmB,SAAS;AAC5B,8BACE,QAAQ,QAAQ,OAAO,CAAC,cAAc,mBAAmB,KAAK,CAAC,CAChE;;;AAIL,QACE,oBAAC;EACC,WAAW,GACT,iDACA,eACI,0CACA,wCACJ,UACD;YAEA,QAAQ,KAAK,WAAW;GACvB,MAAM,WAAW,aAAa,OAAO;GACrC,MAAM,OAAO,OAAO;GAEpB,MAAM,OACJ,qBAAC;IACC,MAAK;IAEL,eAAe,YAAY,OAAO,GAAG;IACrC,UAAU,oBAAoB;IAC9B,WAAW,GACT,6GACA,UAAU,QACV,WACI,GACA,kDACA,eACI,sHACA,gBACL,GACC,GACA,+CACA,eACI,2BACA,mDACL,CACJ;eAEA,oBAAoB,OAAO,MAC1B,oBAAC;KACC,MAAM;KACN,WAAU;MACV,EAEJ,qBAAC;KAAK,WAAW,GACf,2BACA,oBAAoB,OAAO,MAAM,YAClC;gBACE,QAAQ,oBAAC,QAAK,WAAW,UAAU,OAAQ,EAC3C,cAAc,OAAO;MACjB;MAjCF,OAAO,GAkCL;AAGX,OAAI,CAAC,WACH,QACE,qBAAC;IAAwB,eAAe;eACtC,oBAAC;KAAe;eACb;MACc,EACjB,oBAAC,2BACC,oBAAC;KAAe,MAAK;eAClB,OAAO;MACO,GACH;MARJ,OAAO,GASX;AAId,UAAO;IACP;GACE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress-bar.js","names":[],"sources":["../../../src/components/progress-bar.tsx"],"sourcesContent":["\"use client\";\n\nimport { cn } from \"@
|
|
1
|
+
{"version":3,"file":"progress-bar.js","names":[],"sources":["../../../src/components/progress-bar.tsx"],"sourcesContent":["\"use client\";\n\nimport { cn } from \"@hexclave/ui\";\n\ntype DesignProgressBarGradient = \"blue\" | \"cyan\" | \"purple\" | \"green\" | \"orange\" | \"default\";\n\nconst fillClasses = new Map<DesignProgressBarGradient, string>([\n [\"blue\", \"bg-blue-500 dark:bg-blue-400\"],\n [\"cyan\", \"bg-cyan-500 dark:bg-cyan-400\"],\n [\"purple\", \"bg-purple-500 dark:bg-purple-400\"],\n [\"green\", \"bg-emerald-500 dark:bg-emerald-400\"],\n [\"orange\", \"bg-amber-500 dark:bg-amber-400\"],\n [\"default\", \"bg-foreground/60\"],\n]);\n\nexport type DesignProgressBarProps = {\n value: number,\n max?: number,\n gradient?: DesignProgressBarGradient,\n label?: string,\n showPercentage?: boolean,\n size?: \"sm\" | \"md\" | \"lg\",\n className?: string,\n};\n\n/**\n * Horizontal progress bar for quota / fill-level indicators. Takes a\n * `value` and optional `max` (defaults to 100 — pass raw values, the\n * component computes the percentage). Optional label + percentage display.\n *\n * ```tsx\n * <DesignProgressBar value={usedSeats} max={totalSeats} label=\"Seats used\" showPercentage />\n * <DesignProgressBar value={75} gradient=\"green\" />\n * ```\n */\nexport function DesignProgressBar({\n value,\n max = 100,\n gradient = \"default\",\n label,\n showPercentage = false,\n size = \"md\",\n className,\n}: DesignProgressBarProps) {\n const percentage = max > 0 ? Math.min(Math.max((value / max) * 100, 0), 100) : 0;\n const fillClass = fillClasses.get(gradient) ?? \"bg-foreground/60\";\n\n const trackHeight = size === \"sm\" ? \"h-1.5\" : size === \"lg\" ? \"h-3\" : \"h-2\";\n\n return (\n <div className={cn(\"w-full\", className)}>\n {(label || showPercentage) && (\n <div className=\"flex items-center justify-between mb-2\">\n {label && (\n <span className=\"text-xs font-medium text-muted-foreground\">\n {label}\n </span>\n )}\n {showPercentage && (\n <span className=\"text-xs font-medium tabular-nums text-foreground\">\n {Math.round(percentage)}%\n </span>\n )}\n </div>\n )}\n <div\n className={cn(\n \"w-full rounded-full bg-black/[0.06] dark:bg-white/[0.06] overflow-hidden\",\n trackHeight\n )}\n >\n <div\n className={cn(\n \"h-full rounded-full transition-all duration-300 ease-out\",\n fillClass\n )}\n style={{ width: `${percentage}%` }}\n />\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;AAMA,MAAM,cAAc,IAAI,IAAuC;CAC7D,CAAC,QAAQ,+BAA+B;CACxC,CAAC,QAAQ,+BAA+B;CACxC,CAAC,UAAU,mCAAmC;CAC9C,CAAC,SAAS,qCAAqC;CAC/C,CAAC,UAAU,iCAAiC;CAC5C,CAAC,WAAW,mBAAmB;CAChC,CAAC;;;;;;;;;;;AAsBF,SAAgB,kBAAkB,EAChC,OACA,MAAM,KACN,WAAW,WACX,OACA,iBAAiB,OACjB,OAAO,MACP,aACyB;CACzB,MAAM,aAAa,MAAM,IAAI,KAAK,IAAI,KAAK,IAAK,QAAQ,MAAO,KAAK,EAAE,EAAE,IAAI,GAAG;CAC/E,MAAM,YAAY,YAAY,IAAI,SAAS,IAAI;CAE/C,MAAM,cAAc,SAAS,OAAO,UAAU,SAAS,OAAO,QAAQ;AAEtE,QACE,qBAAC;EAAI,WAAW,GAAG,UAAU,UAAU;cACnC,SAAS,mBACT,qBAAC;GAAI,WAAU;cACZ,SACC,oBAAC;IAAK,WAAU;cACb;KACI,EAER,kBACC,qBAAC;IAAK,WAAU;eACb,KAAK,MAAM,WAAW,EAAC;KACnB;IAEL,EAER,oBAAC;GACC,WAAW,GACT,4EACA,YACD;aAED,oBAAC;IACC,WAAW,GACT,4DACA,UACD;IACD,OAAO,EAAE,OAAO,GAAG,WAAW,IAAI;KAClC;IACE;GACF"}
|