eidos-ui 1.2.0 → 2.0.0

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.
Files changed (35) hide show
  1. package/dist/Table.types-Cl5gR8r9.d.cts +183 -0
  2. package/dist/Table.types-jhPb7JNU.d.ts +183 -0
  3. package/dist/{chunk-6XB3QTHO.cjs → chunk-2ZENLZEQ.cjs} +55 -51
  4. package/dist/chunk-2ZENLZEQ.cjs.map +1 -0
  5. package/dist/{chunk-6DBCT5MK.js → chunk-AJZTVCST.js} +1 -1
  6. package/dist/chunk-AJZTVCST.js.map +1 -0
  7. package/dist/{chunk-P2ZHWFK4.js → chunk-MH2CD2QT.js} +29 -25
  8. package/dist/chunk-MH2CD2QT.js.map +1 -0
  9. package/dist/{chunk-NUK4NT5Q.cjs → chunk-QONDJ5VM.cjs} +11 -11
  10. package/dist/chunk-QONDJ5VM.cjs.map +1 -0
  11. package/dist/{chunk-2KO5ZCZC.js → chunk-VSFKM7FE.js} +2 -2
  12. package/dist/chunk-VSFKM7FE.js.map +1 -0
  13. package/dist/{chunk-TKLWQWMT.cjs → chunk-XVSODE4K.cjs} +1 -1
  14. package/dist/chunk-XVSODE4K.cjs.map +1 -0
  15. package/dist/data-grid/index.cjs +3 -3
  16. package/dist/data-grid/index.d.cts +123 -51
  17. package/dist/data-grid/index.d.ts +123 -51
  18. package/dist/data-grid/index.js +2 -2
  19. package/dist/index.cjs +4 -4
  20. package/dist/index.d.cts +2 -2
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.js +3 -3
  23. package/dist/table/index.cjs +3 -3
  24. package/dist/table/index.d.cts +5 -5
  25. package/dist/table/index.d.ts +5 -5
  26. package/dist/table/index.js +2 -2
  27. package/package.json +1 -1
  28. package/dist/Table.types-Bv9l7qxd.d.ts +0 -103
  29. package/dist/Table.types-DQ0ReOLj.d.cts +0 -103
  30. package/dist/chunk-2KO5ZCZC.js.map +0 -1
  31. package/dist/chunk-6DBCT5MK.js.map +0 -1
  32. package/dist/chunk-6XB3QTHO.cjs.map +0 -1
  33. package/dist/chunk-NUK4NT5Q.cjs.map +0 -1
  34. package/dist/chunk-P2ZHWFK4.js.map +0 -1
  35. package/dist/chunk-TKLWQWMT.cjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/eidos-ui/eidos-ui/dist/chunk-2ZENLZEQ.cjs","../src/components/DataGrid/DataGrid.component.tsx"],"names":[],"mappings":"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","file":"/home/runner/work/eidos-ui/eidos-ui/dist/chunk-2ZENLZEQ.cjs","sourcesContent":[null,"import React, { useState, useEffect, useRef, useCallback, useMemo, useLayoutEffect } from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n GripVertical,\n ArrowUp,\n ArrowDown,\n ChevronsUpDown,\n ChevronRight,\n X,\n Plus,\n AlignJustify,\n Check,\n FolderOpen,\n SearchX,\n MoreVertical,\n} from 'lucide-react';\nimport {\n DndContext,\n PointerSensor,\n KeyboardSensor,\n useSensor,\n useSensors,\n closestCenter,\n} from '@dnd-kit/core';\nimport type { DragEndEvent } from '@dnd-kit/core';\nimport {\n SortableContext,\n verticalListSortingStrategy,\n useSortable,\n arrayMove,\n} from '@dnd-kit/sortable';\nimport { CSS } from '@dnd-kit/utilities';\nimport { Button } from '../Button';\nimport { SplitButton } from '../SplitButton';\nimport { Input } from '../Input/Input.component';\nimport { Select } from '../Select/Select.component';\nimport { Combobox } from '../Combobox';\nimport { SegmentedControl } from '../SegmentedControl';\nimport { Checkbox } from '../Checkbox/Checkbox.component';\nimport { Spinner } from '../Spinner/Spinner.component';\nimport { Pagination } from '../Pagination';\nimport { EmptyState } from '../EmptyState';\nimport { Dropdown } from '../Dropdown';\nimport { Menu } from '../Menu';\nimport type { MenuItemType } from '../Menu';\nimport { TableFiltersDropdown } from '../Table/TableFiltersDropdown.component';\nimport type { FilterValue, TableColumn, TableFilters } from '../Table/Table.types';\nimport type {\n DataGridProps,\n DataGridActionsColumn,\n DataGridCellType,\n DataGridFilterField,\n DataGridQuickFilter,\n DataGridSelectOption,\n EditingCell,\n} from './DataGrid.types';\nimport { renderIcon } from '../../utils';\nimport type { RowKey } from '../../utils';\nimport './DataGrid.scss';\n\n// Minimum usable width (px) for a single table column - used to derive an\n// automatic card-view threshold from the number of displayed columns, so a\n// grid with many columns doesn't have to overflow horizontally before\n// `cardViewBreakpoint` (a single, hand-tuned number) is reached. See\n// `isCardView` below.\nconst MIN_COLUMN_WIDTH_PX = 100;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Internal column + row access\n//\n// The public `DataGridColumn<T>` is a discriminated union, which is what lets\n// a consumer's `key` be checked against `keyof T` and gives `renderCell` /\n// `renderEditor` / `validate` the field's own value type instead of `unknown`.\n//\n// Internally the grid walks `columns` generically: it probes members that\n// exist on only one variant (`validate` on value columns, `actions` on the\n// actions column) and looks row fields up by a runtime string key. Narrowing\n// the union at each of those ~30 sites would add a lot of noise for no added\n// safety - the union is enforced where it matters, at the consumer's call\n// site - so the grid widens it once, here, and the render code below is\n// unchanged from when `DataGridColumn` was a single flat interface.\n// ─────────────────────────────────────────────────────────────────────────────\ntype InternalColumn<T extends object> = Omit<DataGridActionsColumn<T>, 'type' | 'key'> & {\n /**\n * Widened to a required plain string. Only the actions variant may omit\n * `key`, and it never reaches a path that consumes one: it renders in its\n * own dedicated slot with a hard-coded `data-col-key=\"__actions__\"`, and is\n * filtered out of `dataColumns` / `sortedColumns`.\n */\n key: string;\n type?: DataGridCellType;\n editable?: boolean;\n required?: boolean;\n options?: DataGridSelectOption[];\n validate?: (value: unknown) => string | true;\n renderCell?: (value: unknown, row: T, rowIndex: number) => React.ReactNode;\n renderEditor?: (value: unknown, onChange: (v: unknown) => void, row: T) => React.ReactNode;\n};\n\n/** Reads a row field by a runtime string key - see `InternalColumn` above. */\nconst cellOf = <T extends object>(row: T, key: string): unknown =>\n (row as Record<string, unknown>)[key];\n\n// Value of the reset (\"All\") segment a quick filter's SegmentedControl always\n// prepends - a SegmentedControl has no empty state of its own, so it needs an\n// explicit segment standing for \"no filter\".\nconst QUICK_FILTER_RESET_VALUE = '';\n\n// Whether a filter value is meaningful enough to keep in the filter state.\n// Empty values are *deleted* from it rather than stored as '' / [], so the\n// key count stays an accurate \"are any filters active?\" signal - that's what\n// drives the \"No results found\" empty state and the dropdown's active\n// indicator.\nconst hasFilterValue = (value: FilterValue): boolean => {\n if (value === undefined || value === null || value === '') return false;\n if (Array.isArray(value)) return value.length > 0;\n if (typeof value === 'object' && 'start' in value) return !!(value.start || value.end);\n return true;\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// SortableTableRow - wraps a <tr> with dnd-kit sortable behaviour.\n//\n// Render-prop pattern: passes `dragHandleProps` down to whichever child element\n// should be the drag handle (typically the grip icon's <span>), so the <tr>\n// itself owns the DOM ref + layout-transform while the handle owns the listeners.\n// Pass `disabled={true}` when draggableRows is off - useSortable is always\n// called unconditionally to satisfy the Rules of Hooks.\n// ─────────────────────────────────────────────────────────────────────────────\nfunction SortableTableRow({\n id,\n disabled,\n className,\n children,\n}: {\n id: string;\n disabled?: boolean;\n className?: string;\n children: (dragHandleProps: object, isDragging: boolean) => React.ReactNode;\n}): React.ReactElement {\n const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({\n id,\n disabled,\n });\n\n return (\n <tr\n ref={setNodeRef}\n className={[className, isDragging ? 'eidos-datagrid-row--dragging' : '']\n .filter(Boolean)\n .join(' ')}\n style={{\n transform: CSS.Transform.toString(transform),\n transition: transition ?? undefined,\n opacity: isDragging ? 0.4 : 1,\n }}\n {...attributes}\n >\n {children(listeners ?? {}, isDragging)}\n </tr>\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// useExpandAnimation - drives a row's expanded-content open/close transition.\n// Mirrors the max-height technique AccordionItem uses, so row expansion\n// animates consistently with Accordion elsewhere in the library - but also\n// keeps tracking the content's height for as long as the panel stays open\n// (via ResizeObserver on the unclipped inner node), not just once at the\n// moment it opens. Content whose height changes after opening - e.g. an\n// async fetch resolving into more/less content than a loading placeholder -\n// would otherwise stay clipped at whatever height was measured on open.\n//\n// Content stays mounted through the close animation (so it can transition\n// shut instead of vanishing instantly), but is never mounted before the row\n// is actually opened for the first time - so `renderExpandedContent` (which\n// may kick off a data fetch) never runs for a row nobody has expanded.\n//\n// Must be called from its own per-row component (not inline in a .map()\n// callback) so the number of hook calls stays constant across renders\n// regardless of how many rows are currently displayed.\n// ─────────────────────────────────────────────────────────────────────────────\nfunction useExpandAnimation(isOpen: boolean): {\n mounted: boolean;\n maxHeight: number;\n outerRef: React.RefObject<HTMLDivElement | null>;\n innerRef: React.RefObject<HTMLDivElement | null>;\n handleTransitionEnd: () => void;\n} {\n // outerRef: the clipped, max-height/overflow-hidden wrapper actually\n // animated - its own rendered height is pinned by `maxHeight` below, so it\n // can't be the thing observed for size changes (see innerRef).\n const outerRef = useRef<HTMLDivElement>(null);\n // innerRef: the unconstrained content itself - always laid out at its\n // natural height regardless of the outer wrapper's clipping, so its size\n // genuinely changes when its content does. That's what ResizeObserver\n // needs to be watching.\n const innerRef = useRef<HTMLDivElement>(null);\n const [maxHeight, setMaxHeight] = useState(0);\n const [mounted, setMounted] = useState(isOpen);\n\n useEffect(() => {\n if (isOpen) setMounted(true);\n }, [isOpen]);\n\n useEffect(() => {\n if (!mounted) return;\n\n if (!isOpen) {\n setMaxHeight(0);\n return;\n }\n\n const node = innerRef.current;\n if (!node) return;\n\n const update = () => setMaxHeight(node.scrollHeight);\n update();\n\n const observer = new ResizeObserver(update);\n observer.observe(node);\n return () => observer.disconnect();\n }, [isOpen, mounted]);\n\n const handleTransitionEnd = useCallback(() => {\n if (!isOpen) setMounted(false);\n }, [isOpen]);\n\n return { mounted, maxHeight, outerRef, innerRef, handleTransitionEnd };\n}\n\n// Table mode: one extra full-width <tr> rendered directly after an expanded\n// row - see `expandable`/`renderExpandedContent` on DataGridProps.\nfunction ExpandedTableRow({\n isOpen,\n colSpan,\n render,\n}: {\n isOpen: boolean;\n colSpan: number;\n render: () => React.ReactNode;\n}): React.ReactElement | null {\n const { mounted, maxHeight, outerRef, innerRef, handleTransitionEnd } =\n useExpandAnimation(isOpen);\n if (!mounted) return null;\n\n return (\n <tr className=\"eidos-datagrid-expanded-row\">\n <td colSpan={colSpan} className=\"eidos-datagrid-expanded-cell\">\n <div\n ref={outerRef}\n className=\"eidos-datagrid-expand-panel\"\n style={{ maxHeight: `${maxHeight}px` }}\n onTransitionEnd={handleTransitionEnd}\n >\n <div ref={innerRef} className=\"eidos-datagrid-expand-panel-inner\">\n {render()}\n </div>\n </div>\n </td>\n </tr>\n );\n}\n\n// Card mode: the same animated panel, appended inside the card itself below\n// its fields.\nfunction ExpandedCardPanel({\n isOpen,\n render,\n}: {\n isOpen: boolean;\n render: () => React.ReactNode;\n}): React.ReactElement | null {\n const { mounted, maxHeight, outerRef, innerRef, handleTransitionEnd } =\n useExpandAnimation(isOpen);\n if (!mounted) return null;\n\n return (\n <div\n ref={outerRef}\n className=\"eidos-datagrid-expand-panel\"\n style={{ maxHeight: `${maxHeight}px` }}\n onTransitionEnd={handleTransitionEnd}\n >\n <div ref={innerRef} className=\"eidos-datagrid-expand-panel-inner\">\n {render()}\n </div>\n </div>\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Inner implementation (generic function so we can assign .displayName cleanly)\n// ─────────────────────────────────────────────────────────────────────────────\nfunction DataGridInner<T extends object>({\n // ── Existing props ─────────────────────────────────────────────────────────\n columns: columnsProp,\n data,\n rowKey,\n onChange,\n onRowAdd,\n editable = false,\n loading = false,\n emptyText = 'No data available',\n className,\n stickyHeader = false,\n maxHeight,\n showRowNumbers = false,\n draggableRows = false,\n onRowReorder,\n // ── Sorting ────────────────────────────────────────────────────────────────\n currentSort,\n onSortChange,\n // ── Filtering ──────────────────────────────────────────────────────────────\n showFilters = false,\n filterConfig = [],\n filters,\n onFiltersChange,\n // ── Quick filters ──────────────────────────────────────────────────────────\n quickFilters = [],\n // ── Pagination ─────────────────────────────────────────────────────────────\n showPagination = false,\n pageSize = 10,\n pageSizeOptions,\n totalRows: totalRowsProp,\n onPageChange,\n // ── Row selection + bulk actions ───────────────────────────────────────────\n selectable = false,\n selectedRows: controlledSelectedRows,\n defaultSelectedRows,\n onSelectionChange,\n bulkActions,\n // ── Row expansion ────────────────────────────────────────────────────────────\n expandable = false,\n renderExpandedContent,\n isRowExpandable,\n expandMultiple = true,\n expandedRows: controlledExpandedRows,\n defaultExpandedRows,\n onExpandedRowsChange,\n // ── Display ────────────────────────────────────────────────────────────────\n density = 'comfortable',\n showDensity = false,\n // ── Card view ────────────────────────────────────────────────────────────────\n hasCardView = true,\n cardViewBreakpoint = 640,\n cardMinWidth = 280,\n}: DataGridProps<T>): React.ReactElement {\n // See `InternalColumn` above - a cast, not a copy, so the identity\n // comparisons below (`c !== actionsColumn`) still hold.\n const columns = columnsProp as InternalColumn<T>[];\n // `rowKey` is `RowKey<T>` publicly, so the `'id'` default can't live in the\n // destructuring: `'id'` isn't a field of every `T`.\n const rowKeyField: string = rowKey ?? 'id';\n // ── State + paired refs (editing - preserved exactly) ─────────────────────\n // We keep a ref alongside each piece of mutable state so that event\n // handlers registered in effects can always read the *latest* value without\n // stale-closure bugs, while the state variables drive re-renders normally.\n\n const [localData, _setLocalData] = useState<T[]>(data);\n const localDataRef = useRef<T[]>(data);\n\n const [editingCell, _setEditingCell] = useState<EditingCell>(null);\n const editingCellRef = useRef<EditingCell>(null);\n\n const [editValue, _setEditValue] = useState<unknown>(null);\n const editValueRef = useRef<unknown>(null);\n\n const [editError, setEditError] = useState<string | null>(null);\n\n // ── Validation error tooltip (portaled) ─────────────────────────────────────\n // Positioned via a portal into document.body rather than `position: absolute`\n // inside the cell - a cell on the last visible row would otherwise place the\n // tooltip past the scroll container's bottom edge, which still counts toward\n // that container's scrollable content size and forces an unwanted scrollbar\n // just to reveal a tooltip nobody asked to scroll to. Portaling escapes the\n // scroll container's box entirely, the same way Dropdown's own content does.\n // HTMLElement (not HTMLTableCellElement) so the same ref can anchor either\n // a <td> in table mode or a card field <div> in card view - only\n // `getBoundingClientRect()` is ever called on it.\n const errorTooltipAnchorRef = useRef<HTMLElement | null>(null);\n const [errorTooltipPosition, setErrorTooltipPosition] = useState<{\n top: number;\n left: number;\n } | null>(null);\n const activeCellError = editingCell && editError ? editError : null;\n\n useLayoutEffect(() => {\n if (!activeCellError) {\n setErrorTooltipPosition(null);\n return;\n }\n\n const updatePosition = () => {\n const node = errorTooltipAnchorRef.current;\n if (!node) return;\n const rect = node.getBoundingClientRect();\n setErrorTooltipPosition({ top: rect.bottom + 4, left: rect.left });\n };\n\n updatePosition();\n\n // Capture phase catches scroll events from any scrollable ancestor\n // (e.g. this grid's own scroll container), not just window/document -\n // `scroll` doesn't bubble, so a plain (bubble-phase) listener on window\n // would miss it entirely.\n window.addEventListener('resize', updatePosition);\n window.addEventListener('scroll', updatePosition, true);\n return () => {\n window.removeEventListener('resize', updatePosition);\n window.removeEventListener('scroll', updatePosition, true);\n };\n }, [activeCellError]);\n\n // ── New state ──────────────────────────────────────────────────────────────\n // Density (internal when showDensity=true, otherwise respects the prop)\n const [internalDensity, setInternalDensity] = useState<'compact' | 'comfortable' | 'spacious'>(\n density,\n );\n const effectiveDensity = showDensity ? internalDensity : density;\n\n // Sorting (client-side internal state)\n const [internalSort, setInternalSort] = useState<{\n key: string;\n direction: 'asc' | 'desc';\n } | null>(null);\n\n // Filtering (client-side internal state), seeded from any quick filter\n // `defaultValue`s. Lazy, so it only reads `quickFilters` on mount - later\n // edits to the array's default values don't yank a filter the user has\n // since changed. In server-side mode this state is unused entirely, which\n // is why `defaultValue` is documented as client-side only.\n const [internalFilters, setInternalFilters] = useState<TableFilters>(() => {\n const seeded: TableFilters = {};\n quickFilters.forEach(({ key, defaultValue }) => {\n if (hasFilterValue(defaultValue)) seeded[key] = defaultValue;\n });\n return seeded;\n });\n\n // Pagination\n const [currentPage, setCurrentPage] = useState(1);\n const [currentPageSize, setCurrentPageSize] = useState(pageSize);\n\n // Selection\n const isControlledSelection = controlledSelectedRows !== undefined;\n const [internalSelectedKeys, setInternalSelectedKeys] = useState<Set<string>>(\n () => new Set(defaultSelectedRows ?? []),\n );\n\n // Row expansion\n const isControlledExpansion = controlledExpandedRows !== undefined;\n const [internalExpandedKeys, setInternalExpandedKeys] = useState<Set<string>>(\n () => new Set(defaultExpandedRows ?? []),\n );\n\n // Card view: measured off the grid's own container width (not the\n // viewport) via ResizeObserver, so it responds correctly even when the\n // grid sits in a narrow sidebar/split-pane on an otherwise wide screen.\n // Starts `null` (unmeasured) rather than 0, so the table is what renders\n // for one frame on mount instead of briefly flashing cards.\n const [containerWidth, setContainerWidth] = useState<number | null>(null);\n const resizeObserverRef = useRef<ResizeObserver | null>(null);\n // A callback ref (not `useRef` + a `useLayoutEffect` keyed on `hasCardView`)\n // so this fires whenever the container DOM node actually becomes\n // available, not just when `hasCardView` changes. A plain effect only\n // reruns on its own dependency changing - it does NOT rerun just because\n // a *different* conditional branch attaches the ref for the first time.\n // The `loading` early-return below renders its own container div; any\n // consumer whose `loading` prop starts `true` on mount (e.g. fetching\n // data asynchronously, so it can never resolve before the first render)\n // would mount that div first, find `containerRef.current` still null, and\n // then never re-run the effect once `loading` flips to `false` and the\n // real container mounts - `hasCardView` itself never changed, so nothing\n // triggered a re-run, leaving `containerWidth` (and therefore card view)\n // permanently stuck. A callback ref sidesteps that entirely: whichever\n // container div actually mounts, this runs for it.\n const setContainerRef = useCallback(\n (el: HTMLDivElement | null) => {\n resizeObserverRef.current?.disconnect();\n resizeObserverRef.current = null;\n\n if (!el || !hasCardView) return;\n\n const observer = new ResizeObserver((entries) => {\n const width = entries[0]?.contentRect.width;\n if (width != null) setContainerWidth(width);\n });\n observer.observe(el);\n resizeObserverRef.current = observer;\n },\n [hasCardView],\n );\n // Displayed column count, computed directly from props (available here\n // without waiting on the `dataColumns`/`actionsColumn` memos below) -\n // `columns.length` already includes the actions column if any, matching\n // how `totalCols` is derived further down from `dataColumns.length +\n // (actionsColumn ? 1 : 0)`.\n const columnCountForCardView =\n columns.length +\n (showRowNumbers ? 1 : 0) +\n (draggableRows ? 1 : 0) +\n (selectable ? 1 : 0) +\n (expandable ? 1 : 0);\n\n // Card view kicks in below the explicit `cardViewBreakpoint` OR once the\n // container is too narrow to fit every column at a reasonable minimum\n // width - the latter means a wide/many-column grid auto-switches without\n // the consumer having to hand-calculate a breakpoint for it.\n const isCardView =\n hasCardView &&\n containerWidth !== null &&\n (containerWidth < cardViewBreakpoint ||\n containerWidth < columnCountForCardView * MIN_COLUMN_WIDTH_PX);\n\n // Card view header/subheader: only the first matching column is honored\n // (see DataGridColumn.cardHeader/cardSubheader).\n const cardHeaderColumn = useMemo(() => columns.find((c) => c.cardHeader), [columns]);\n const cardSubheaderColumn = useMemo(() => columns.find((c) => c.cardSubheader), [columns]);\n // The row-actions column, if any - only the first `type: 'actions'` column\n // is honored (see DataGridColumn.actions). Rendered in its own dedicated\n // slot (far right in table mode, top-right of the card in card view),\n // never as a normal data column.\n const actionsColumn = useMemo(() => columns.find((c) => c.type === 'actions'), [columns]);\n // Every column that actually renders as data - excludes the actions\n // column entirely (it's never a normal cell) from both table columns and\n // pinning/filtering/validation logic.\n const dataColumns = useMemo(\n () => columns.filter((c) => c !== actionsColumn),\n [columns, actionsColumn],\n );\n // Remaining columns render as label:value field rows, in declaration order.\n const cardFieldColumns = useMemo(\n () => dataColumns.filter((c) => c !== cardHeaderColumn && c !== cardSubheaderColumn),\n [dataColumns, cardHeaderColumn, cardSubheaderColumn],\n );\n\n // Pinned columns offset map (computed in useLayoutEffect)\n const theadRef = useRef<HTMLTableSectionElement>(null);\n const [pinnedOffsets, setPinnedOffsets] = useState<\n Map<string, { side: 'left' | 'right'; offset: number }>\n >(new Map());\n\n // ── Synced setters (preserved exactly) ────────────────────────────────────\n const setLocalData = useCallback((v: T[]) => {\n localDataRef.current = v;\n _setLocalData(v);\n }, []);\n\n const setEditingCell = useCallback((v: EditingCell) => {\n editingCellRef.current = v;\n _setEditingCell(v);\n }, []);\n\n const setEditValue = useCallback((v: unknown) => {\n editValueRef.current = v;\n _setEditValue(v);\n }, []);\n\n // ── Sync when the data prop changes externally (preserved) ─────────────────\n useEffect(() => {\n setLocalData(data);\n }, [data, setLocalData]);\n\n // ── Active sort / filters (derived) ───────────────────────────────────────\n // Server-side sort: caller controls currentSort + onSortChange.\n // Client-side sort: we manage internalSort.\n const activeSort = currentSort ?? internalSort;\n\n // Server-side filter: caller controls filters + onFiltersChange.\n // Client-side filter: we manage internalFilters.\n const activeFilters: TableFilters = useMemo(\n () => (onFiltersChange ? (filters ?? {}) : internalFilters),\n [onFiltersChange, filters, internalFilters],\n );\n\n // ── Selection derived state ────────────────────────────────────────────────\n const selectedSet = useMemo(\n () =>\n new Set(isControlledSelection ? (controlledSelectedRows ?? []) : [...internalSelectedKeys]),\n [isControlledSelection, controlledSelectedRows, internalSelectedKeys],\n );\n\n const allKeys = useMemo(\n () => localData.map((row, idx) => String(cellOf(row, rowKeyField) ?? idx)),\n [localData, rowKeyField],\n );\n\n const allSelected = allKeys.length > 0 && allKeys.every((k) => selectedSet.has(k));\n const someSelected = !allSelected && allKeys.some((k) => selectedSet.has(k));\n const hasSelection = selectedSet.size > 0;\n\n const selectedItems = useMemo(\n () => localData.filter((row, idx) => selectedSet.has(String(cellOf(row, rowKeyField) ?? idx))),\n [localData, selectedSet, rowKeyField],\n );\n\n // ── Row expansion derived state ─────────────────────────────────────────────\n const expandedSet = useMemo(\n () =>\n new Set(isControlledExpansion ? (controlledExpandedRows ?? []) : [...internalExpandedKeys]),\n [isControlledExpansion, controlledExpandedRows, internalExpandedKeys],\n );\n\n // ── Quick filters: key ownership ───────────────────────────────────────────\n // Quick filters and the filter dropdown share one TableFilters object, so\n // each side has to own a disjoint set of keys - see `dropdownFilters` below\n // for why the dropdown can't just be handed the whole thing.\n const quickFilterKeys = useMemo(() => new Set(quickFilters.map((f) => f.key)), [quickFilters]);\n\n // ── Filter dropdown adapter: DataGridFilterField → TableColumn ─────────────\n // TableFiltersDropdown expects TableColumn<T>[]; we project our (decoupled,\n // column-independent) filter schema onto that shape. A key already claimed\n // by a quick filter is dropped: the quick filter is the always-visible\n // control, and two controls writing one key would each clobber the other.\n const filterDropdownColumns = useMemo((): TableColumn<T>[] => {\n return filterConfig\n .filter((field) => !quickFilterKeys.has(field.key))\n .map(\n (field: DataGridFilterField<T>) =>\n ({\n key: field.key,\n label: field.label,\n filterable: true,\n filterType: field.filterType,\n filterOptions: field.filterOptions,\n dateFilterMode: field.dateFilterMode,\n }) as TableColumn<T>,\n );\n }, [filterConfig, quickFilterKeys]);\n\n const dropdownFilterKeys = useMemo(\n () => new Set(filterDropdownColumns.map((col) => String(col.key))),\n [filterDropdownColumns],\n );\n\n // The dropdown gets only the keys it owns, never the whole filter object.\n // It mirrors every key it's handed into a filter row, so a key it has no\n // matching column for (a quick filter's, or a server-side caller's filter\n // on an unlisted field) would render as a blank, un-removable \"Select\n // column\" row - and its Apply, which emits its complete set, would drop\n // that value on the floor.\n const dropdownFilters = useMemo(\n () =>\n Object.fromEntries(\n Object.entries(activeFilters).filter(([key]) => dropdownFilterKeys.has(key)),\n ),\n [activeFilters, dropdownFilterKeys],\n );\n\n // ── Client-side filtering ──────────────────────────────────────────────────\n const filteredData = useMemo(() => {\n // Server-side mode: parent is responsible for filtering; use data as-is.\n if (onFiltersChange) return localData;\n\n const filterEntries = Object.entries(activeFilters).filter(([, val]) => {\n if (val === undefined || val === null || val === '') return false;\n if (Array.isArray(val) && val.length === 0) return false;\n return true;\n });\n\n if (filterEntries.length === 0) return localData;\n\n return localData.filter((row) =>\n filterEntries.every(([key, filterValue]) => {\n const cellValue = cellOf(row, key);\n\n if (Array.isArray(filterValue)) {\n return filterValue.includes(String(cellValue ?? ''));\n }\n if (typeof filterValue === 'boolean') {\n return Boolean(cellValue) === filterValue;\n }\n if (typeof filterValue === 'object' && filterValue !== null && 'start' in filterValue) {\n const { start, end } = filterValue as { start: string | null; end: string | null };\n const dateStr = String(cellValue ?? '');\n if (start && dateStr < start) return false;\n if (end && dateStr > end) return false;\n return true;\n }\n // Default: case-insensitive text contains\n return String(cellValue ?? '')\n .toLowerCase()\n .includes(String(filterValue).toLowerCase());\n }),\n );\n }, [localData, activeFilters, onFiltersChange]);\n\n // ── Client-side sorting ────────────────────────────────────────────────────\n const sortedData = useMemo(() => {\n // Server-side mode or no active sort: use data as-is.\n if (onSortChange || !activeSort) return filteredData;\n\n return [...filteredData].sort((a, b) => {\n const aVal = cellOf(a, activeSort.key);\n const bVal = cellOf(b, activeSort.key);\n\n if (aVal == null && bVal == null) return 0;\n if (aVal == null) return 1;\n if (bVal == null) return -1;\n\n const compare =\n typeof aVal === 'number' && typeof bVal === 'number'\n ? aVal - bVal\n : String(aVal).localeCompare(String(bVal));\n\n return activeSort.direction === 'asc' ? compare : -compare;\n });\n }, [filteredData, activeSort, onSortChange]);\n\n // ── Client-side pagination ─────────────────────────────────────────────────\n const displayData = useMemo(() => {\n if (!showPagination) return sortedData;\n if (onPageChange) return sortedData; // Server-side: parent slices the data\n const start = (currentPage - 1) * currentPageSize;\n return sortedData.slice(start, start + currentPageSize);\n }, [sortedData, showPagination, currentPage, currentPageSize, onPageChange]);\n\n const totalItemCount = totalRowsProp ?? sortedData.length;\n const totalPages = Math.max(1, Math.ceil(totalItemCount / currentPageSize));\n\n // Reset to page 1 when filters change (client-side and server-side).\n // Depend on the serialized *value* of activeFilters, not the object\n // reference: in server-side mode activeFilters is derived straight from\n // the caller's `filters` prop, and callers commonly build that object\n // inline in render (a new reference every render) whether or not its\n // contents actually changed. Comparing by reference made this effect -\n // and the page reset it causes - fire on every unrelated parent\n // re-render (e.g. new page data arriving), not just on real filter\n // changes.\n const activeFiltersKey = useMemo(() => JSON.stringify(activeFilters), [activeFilters]);\n useEffect(() => {\n setCurrentPage(1);\n }, [activeFiltersKey]);\n\n // Reset to page 1 when client-side sort changes\n useEffect(() => {\n setCurrentPage(1);\n }, [internalSort]);\n\n // ── Pinned columns: measure th widths and compute sticky offsets ───────────\n // Mirrors the exact pattern used in Table.component.tsx.\n // System columns (sel, drag, rownum, actions) contribute to the offset\n // accumulator but are not themselves sticky-positioned.\n useLayoutEffect(() => {\n const hasPinned = columns.some((c) => c.pin);\n if (!hasPinned || !theadRef.current) {\n setPinnedOffsets((prev) => (prev.size > 0 ? new Map() : prev));\n return;\n }\n\n const headerRow = theadRef.current.querySelector('tr');\n if (!headerRow) return;\n\n const cells = Array.from(headerRow.querySelectorAll<HTMLTableCellElement>('th[data-col-key]'));\n const newOffsets = new Map<string, { side: 'left' | 'right'; offset: number }>();\n\n // Left-pinned: scan left-to-right\n // When any data column is pin:'left', auto-pin the system columns too so\n // the entire frozen-left group moves as a coherent unit.\n const hasLeftPin = columns.some((c) => c.pin === 'left');\n let leftAccum = 0;\n for (const cell of cells) {\n const key = cell.dataset.colKey!;\n if (key === '__expand__' || key === '__sel__' || key === '__drag__' || key === '__rownum__') {\n if (hasLeftPin) {\n newOffsets.set(key, { side: 'left', offset: leftAccum });\n }\n leftAccum += cell.offsetWidth;\n } else {\n const col = columns.find((c) => c.key === key);\n if (col?.pin === 'left') {\n newOffsets.set(key, { side: 'left', offset: leftAccum });\n leftAccum += cell.offsetWidth;\n }\n }\n }\n\n // Right-pinned: scan right-to-left\n let rightAccum = 0;\n for (let i = cells.length - 1; i >= 0; i--) {\n const cell = cells[i];\n const key = cell.dataset.colKey!;\n if (key === '__actions__') {\n rightAccum += cell.offsetWidth;\n } else {\n const col = columns.find((c) => c.key === key);\n if (col?.pin === 'right') {\n newOffsets.set(key, { side: 'right', offset: rightAccum });\n rightAccum += cell.offsetWidth;\n }\n }\n }\n\n setPinnedOffsets(newOffsets);\n }, [columns, selectable, draggableRows, showRowNumbers, expandable]);\n\n // ── Sticky-column helpers ──────────────────────────────────────────────────\n const getCellPinnedProps = useCallback(\n (colKey: string): { style: React.CSSProperties; className: string } => {\n const info = pinnedOffsets.get(colKey);\n if (!info) return { style: {}, className: '' };\n return {\n style: { position: 'sticky', [info.side]: info.offset, zIndex: 1 },\n className:\n info.side === 'left'\n ? 'eidos-datagrid-cell--pinned-left'\n : 'eidos-datagrid-cell--pinned-right',\n };\n },\n [pinnedOffsets],\n );\n\n const getHeaderPinnedProps = useCallback(\n (colKey: string): { style: React.CSSProperties; className: string } => {\n const info = pinnedOffsets.get(colKey);\n if (!info) return { style: {}, className: '' };\n return {\n // z-index 3: above both body sticky cells (1) and non-sticky header (1 from SCSS)\n style: { position: 'sticky', [info.side]: info.offset, zIndex: 3 },\n className:\n info.side === 'left'\n ? 'eidos-datagrid-header-cell--pinned-left'\n : 'eidos-datagrid-header-cell--pinned-right',\n };\n },\n [pinnedOffsets],\n );\n\n // ── Editing helpers (preserved exactly) ────────────────────────────────────\n const isCellEditable = useCallback(\n (col: InternalColumn<T>): boolean => {\n if (!editable) return false;\n if (col.type === 'readonly' || col.type === 'actions') return false;\n if (col.editable === false) return false;\n return true;\n },\n [editable],\n );\n\n const validateCell = useCallback((col: InternalColumn<T>, value: unknown): string | null => {\n if (col.required && (value === '' || value === null || value === undefined)) {\n return 'This field is required';\n }\n if (col.validate) {\n const result = col.validate(value);\n if (result !== true) return result as string;\n }\n return null;\n }, []);\n\n const cellErrorKey = (rowIndex: number, colKey: string) => `${rowIndex}:${colKey}`;\n\n // Cells with a validation error, keyed by `${rowIndex}:${colKey}` - derived\n // directly from the actual data rather than tracked as its own state, so a\n // cell that starts out invalid (e.g. seeded with an empty required field)\n // is flagged immediately on render instead of only after the user has\n // focused and committed it at least once. Recomputes whenever the data\n // actually changes, which already covers every case that needs it: typing\n // a fix and committing, an external `data` prop update, a row being added,\n // etc. Painted as a background tint on every render (see\n // `eidos-data-grid-cell--has-error` below); the more prominent border +\n // text (`eidos-data-grid-cell--error`) only shows while that specific cell\n // is the one being edited, via `editError` (see `startEdit`).\n const cellErrors = useMemo(() => {\n const errors: Record<string, string> = {};\n localData.forEach((row, rowIndex) => {\n for (const col of columns) {\n if (!isCellEditable(col)) continue;\n const error = validateCell(col, cellOf(row, col.key));\n if (error) errors[cellErrorKey(rowIndex, col.key)] = error;\n }\n });\n return errors;\n }, [localData, columns, isCellEditable, validateCell]);\n\n // ── Commit / discard ────────────────────────────────────────────────────────\n /**\n * Validates a cell's value and always persists it (even if invalid - see\n * `commitEdit` below for why). `cellErrors` above picks the result up\n * automatically once `localData` updates. Returns the validation error, if any.\n */\n const commitValue = useCallback(\n (rowIndex: number, colKey: string, value: unknown): string | null => {\n const col = columns.find((c) => c.key === colKey);\n if (!col) return null;\n\n const error = validateCell(col, value);\n\n const newData = localDataRef.current.map<T>((row, idx) =>\n idx === rowIndex ? ({ ...row, [colKey]: value } as T) : row,\n );\n setLocalData(newData);\n onChange?.(newData);\n\n return error;\n },\n [columns, onChange, setLocalData, validateCell],\n );\n\n /**\n * Commits the currently-editing cell and exits edit mode. Never blocks: an\n * invalid value is still saved and flagged via `cellErrors` (see above)\n * rather than trapping the user in the cell - a required field going empty\n * is exactly the case validation is there to catch and surface, not a\n * reason to freeze every other interaction with the grid until it's fixed.\n */\n const commitEdit = useCallback(() => {\n const cell = editingCellRef.current;\n if (!cell) return;\n\n const col = columns.find((c) => c.key === cell.colKey);\n if (!col) {\n setEditingCell(null);\n return;\n }\n\n commitValue(cell.rowIndex, cell.colKey, editValueRef.current);\n setEditingCell(null);\n setEditError(null);\n }, [columns, commitValue, setEditingCell]);\n\n const discardEdit = useCallback(() => {\n setEditingCell(null);\n setEditError(null);\n }, [setEditingCell]);\n\n // Keep a ref to the latest commit so the global handler never captures\n // a stale version, without having the effect re-run on every re-render.\n const commitEditRef = useRef(commitEdit);\n useEffect(() => {\n commitEditRef.current = commitEdit;\n }, [commitEdit]);\n\n // ── Global mousedown: commit on blur - i.e. any click that isn't on the\n // active editor itself ──────────────────────────────────────────────────\n // Using mousedown (fires before blur/click) lets us finalize the active\n // cell before a click elsewhere starts a new edit or does anything else.\n //\n // Deliberately NOT scoped to \"outside the grid container\" - a click on\n // another cell already commits via that cell's own `handleCellClick`\n // (which commits any in-flight edit before starting its own), but a grid\n // has plenty of space that isn't a cell at all: toolbar buttons, sort\n // headers, pagination, row actions, and - especially in card view - the\n // card's own padding, gaps, and labels. None of those have a handler that\n // commits the edit, so scoping this to \"outside the grid\" left the editor\n // stuck open for any click that landed inside the grid but outside an\n // actual cell. The only clicks that should NOT commit are ones still\n // interacting with the editor being edited (identified by its own\n // `--editing` class, shared by both the <td> and card versions) or its\n // portaled dropdown content (eidos-dropdown-content) - e.g. opening a\n // Select's own options list.\n useEffect(() => {\n if (!editingCell) return;\n\n const handleMouseDown = (e: MouseEvent) => {\n const target = e.target as Element;\n\n if (\n target.closest?.(\n '.eidos-data-grid-cell--editing, .eidos-datagrid-card-value--editing, .eidos-dropdown-content',\n )\n ) {\n return;\n }\n\n commitEditRef.current();\n };\n\n document.addEventListener('mousedown', handleMouseDown);\n return () => document.removeEventListener('mousedown', handleMouseDown);\n }, [editingCell]);\n\n // ── Start editing ────────────────────────────────────────────────────────\n const startEdit = useCallback(\n (rowIndex: number, colKey: string, currentValue: unknown) => {\n const col = columns.find((c) => c.key === colKey);\n if (!col || !isCellEditable(col)) return;\n setEditingCell({ rowIndex, colKey });\n setEditValue(currentValue);\n // Re-focusing a cell that's already flagged shows its error\n // immediately, without needing to retype/re-trigger validation first.\n setEditError(cellErrors[cellErrorKey(rowIndex, colKey)] ?? null);\n },\n [columns, isCellEditable, setEditingCell, setEditValue, cellErrors],\n );\n\n // ── Cell click handler (preserved) ─────────────────────────────────────────\n const handleCellClick = useCallback(\n (rowIndex: number, col: InternalColumn<T>) => {\n const row = localDataRef.current[rowIndex];\n const value = row === undefined ? undefined : cellOf(row, col.key);\n\n // Checkbox cells: toggle immediately - no \"edit mode\" UI needed\n if (col.type === 'checkbox') {\n if (!editable || col.editable === false) return;\n const newData = localDataRef.current.map<T>((row, idx) =>\n idx === rowIndex ? ({ ...row, [col.key]: !value } as T) : row,\n );\n setLocalData(newData);\n onChange?.(newData);\n return;\n }\n\n if (!isCellEditable(col)) return;\n\n // Already editing this exact cell - do nothing\n const cell = editingCellRef.current;\n if (cell?.rowIndex === rowIndex && cell?.colKey === col.key) return;\n\n // Commit any in-flight edit first - never blocks navigation, see commitEdit.\n if (cell) commitEdit();\n\n startEdit(rowIndex, col.key, value);\n },\n [editable, isCellEditable, commitEdit, startEdit, setLocalData, onChange],\n );\n\n // ── Keyboard navigation inside an editing cell (preserved) ─────────────────\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent, rowIndex: number, colKey: string) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n discardEdit();\n return;\n }\n\n if (e.key === 'Enter') {\n e.preventDefault();\n commitEdit();\n return;\n }\n\n if (e.key === 'Tab') {\n e.preventDefault();\n\n // Flat list of editable cells in DOM order (over the full dataset)\n const editableCells: Array<{ rowIndex: number; colKey: string }> = [];\n for (let ri = 0; ri < localDataRef.current.length; ri++) {\n for (const c of columns) {\n if (isCellEditable(c)) {\n editableCells.push({ rowIndex: ri, colKey: c.key });\n }\n }\n }\n\n const idx = editableCells.findIndex((c) => c.rowIndex === rowIndex && c.colKey === colKey);\n const next = e.shiftKey ? editableCells[idx - 1] : editableCells[idx + 1];\n\n // Commit the current cell - never blocks navigation, see commitEdit.\n commitValue(rowIndex, colKey, editValueRef.current);\n\n if (next) {\n setEditingCell(next);\n setEditValue(cellOf(localDataRef.current[next.rowIndex], next.colKey));\n setEditError(cellErrors[cellErrorKey(next.rowIndex, next.colKey)] ?? null);\n } else {\n setEditingCell(null);\n setEditError(null);\n }\n }\n },\n [\n columns,\n isCellEditable,\n commitEdit,\n commitValue,\n discardEdit,\n cellErrors,\n setEditingCell,\n setEditValue,\n ],\n );\n\n const handleRowAdd = useCallback(() => {\n if (!onRowAdd) return;\n const newRow = onRowAdd();\n const newData = [newRow, ...localDataRef.current];\n setLocalData(newData);\n onChange?.(newData);\n setCurrentPage(1);\n }, [onRowAdd, onChange, setLocalData]);\n\n // ── Selection handlers ──────────────────────────────────────────────────────\n const commitSelection = useCallback(\n (keys: string[]) => {\n if (!isControlledSelection) setInternalSelectedKeys(new Set(keys));\n const rows = localDataRef.current.filter((row, idx) =>\n keys.includes(String(cellOf(row, rowKeyField) ?? idx)),\n );\n onSelectionChange?.(keys, rows);\n },\n [isControlledSelection, rowKeyField, onSelectionChange],\n );\n\n const toggleRow = useCallback(\n (key: string) => {\n const next = selectedSet.has(key)\n ? [...selectedSet].filter((k) => k !== key)\n : [...selectedSet, key];\n commitSelection(next);\n },\n [selectedSet, commitSelection],\n );\n\n const toggleAll = useCallback(() => {\n commitSelection(allSelected ? [] : allKeys);\n }, [allSelected, allKeys, commitSelection]);\n\n const clearSelection = useCallback(() => commitSelection([]), [commitSelection]);\n\n // ── Row expansion handlers ───────────────────────────────────────────────────\n const commitExpanded = useCallback(\n (keys: string[]) => {\n if (!isControlledExpansion) setInternalExpandedKeys(new Set(keys));\n onExpandedRowsChange?.(keys);\n },\n [isControlledExpansion, onExpandedRowsChange],\n );\n\n const toggleExpanded = useCallback(\n (key: string) => {\n const isOpen = expandedSet.has(key);\n const next = expandMultiple\n ? isOpen\n ? [...expandedSet].filter((k) => k !== key)\n : [...expandedSet, key]\n : isOpen\n ? []\n : [key];\n commitExpanded(next);\n },\n [expandedSet, expandMultiple, commitExpanded],\n );\n\n // ── Sort handler ────────────────────────────────────────────────────────────\n const handleSortClick = useCallback(\n (colKey: string) => {\n if (onSortChange) {\n // Server-side: delegate to parent\n const newDir =\n currentSort?.key === colKey && currentSort?.direction === 'asc' ? 'desc' : 'asc';\n // `colKey` came from a rendered column, so it is a field of `T` by\n // construction - the public callback is narrowed for the caller's\n // benefit, which this internal string can't prove on its own.\n onSortChange(colKey as RowKey<T>, newDir);\n } else {\n // Client-side: cycle asc → desc → unsorted (null)\n setInternalSort((prev) => {\n if (prev?.key === colKey) {\n return prev.direction === 'asc' ? { key: colKey, direction: 'desc' } : null;\n }\n return { key: colKey, direction: 'asc' };\n });\n }\n },\n [onSortChange, currentSort],\n );\n\n // ── Filter handlers ──────────────────────────────────────────────────────────\n const commitFilters = useCallback(\n (newFilters: TableFilters) => {\n setCurrentPage(1);\n if (onFiltersChange) {\n onFiltersChange(newFilters);\n } else {\n setInternalFilters(newFilters);\n }\n },\n [onFiltersChange],\n );\n\n // The dropdown's writer. It emits its complete cleaned set, covering only\n // the keys it owns, so everything else in the filter state - quick filters\n // included - has to be merged back in rather than replaced. This is also\n // what makes its \"Clear All\" clear only the dropdown's own filters.\n const handleFiltersChange = useCallback(\n (newFilters: TableFilters) => {\n const preserved = Object.entries(activeFilters).filter(\n ([key]) => !dropdownFilterKeys.has(key),\n );\n commitFilters({ ...Object.fromEntries(preserved), ...newFilters });\n },\n [activeFilters, dropdownFilterKeys, commitFilters],\n );\n\n // A quick filter's writer. Empty values delete the key instead of storing\n // '' / [] - see `hasFilterValue`.\n const handleQuickFilterChange = useCallback(\n (key: string, value: FilterValue) => {\n const next = { ...activeFilters };\n if (hasFilterValue(value)) {\n next[key] = value;\n } else {\n delete next[key];\n }\n commitFilters(next);\n },\n [activeFilters, commitFilters],\n );\n\n // Resets every filter, whichever control owns it - what the \"no results\"\n // empty state offers, unlike the dropdown's own scoped \"Clear All\".\n const clearAllFilters = useCallback(() => commitFilters({}), [commitFilters]);\n\n // ── Page handlers ────────────────────────────────────────────────────────────\n const handlePageChange = useCallback(\n (page: number) => {\n setCurrentPage(page);\n onPageChange?.(page, currentPageSize);\n },\n [currentPageSize, onPageChange],\n );\n\n const handlePageSizeChange = useCallback(\n (newPageSize: number) => {\n setCurrentPageSize(newPageSize);\n setCurrentPage(1);\n onPageChange?.(1, newPageSize);\n },\n [onPageChange],\n );\n\n // ── dnd-kit: sensors + drag-end handler (preserved) ───────────────────────\n // Sensors are always initialised (hook rules) - PointerSensor's distance\n // constraint prevents accidental drags on normal cell clicks.\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),\n useSensor(KeyboardSensor),\n );\n\n const handleRowDragEnd = useCallback(\n (event: DragEndEvent) => {\n if (!draggableRows) return; // belt-and-suspenders\n const { active, over } = event;\n if (!over || active.id === over.id) return;\n\n const oldIndex = localDataRef.current.findIndex(\n (row, i) => String(cellOf(row, rowKeyField) ?? i) === String(active.id),\n );\n const newIndex = localDataRef.current.findIndex(\n (row, i) => String(cellOf(row, rowKeyField) ?? i) === String(over.id),\n );\n\n if (oldIndex === -1 || newIndex === -1) return;\n\n const newData = arrayMove(localDataRef.current, oldIndex, newIndex);\n setLocalData(newData);\n onChange?.(newData);\n onRowReorder?.(newData);\n },\n [draggableRows, rowKeyField, setLocalData, onChange, onRowReorder],\n );\n\n // ── Cell content renderers (preserved exactly) ─────────────────────────────\n const renderViewCell = (\n col: InternalColumn<T>,\n value: unknown,\n row: T,\n rowIndex: number,\n ): React.ReactNode => {\n if (col.renderCell) return col.renderCell(value, row, rowIndex);\n\n switch (col.type) {\n case 'checkbox':\n // The <td> onClick handles toggling; onChange is a no-op here so that\n // the controlled Checkbox doesn't re-toggle via its own handler.\n return (\n <Checkbox\n checked={Boolean(value)}\n onChange={() => {\n /* handled by td onClick */\n }}\n size=\"sm\"\n />\n );\n\n case 'select': {\n const option = col.options?.find((o) => o.value === String(value ?? ''));\n return <span>{option ? option.label : String(value ?? '')}</span>;\n }\n\n case 'readonly':\n case 'text':\n case 'number':\n case 'date':\n default:\n return <span>{String(value ?? '')}</span>;\n }\n };\n\n // `variant`/`size` default to the table cell's look: 'bare' has no border/\n // background/radius of its own so the surrounding <td>'s own inset ring\n // (`.eidos-data-grid-cell--editing`) reads as the field's border, and\n // 'sm' keeps the editor from growing the row's height. A card field isn't\n // sitting inside that ring - it's a standalone field in a stack, so it\n // should just look like any other standalone Input/Select in the design\n // system (their own component defaults), not the table's cell-shaped\n // variant. Passing `undefined` here (rather than omitting the prop) is\n // equivalent to omitting it - falls back to each component's own default.\n const renderEditCell = (\n col: InternalColumn<T>,\n value: unknown,\n row: T,\n rowIndex: number,\n context: 'table' | 'card' = 'table',\n ): React.ReactNode => {\n if (col.renderEditor) {\n return col.renderEditor(value, (v) => setEditValue(v), row);\n }\n\n const variant = context === 'table' ? 'bare' : undefined;\n const size = context === 'table' ? 'sm' : undefined;\n\n switch (col.type) {\n case 'number':\n return (\n <Input\n type=\"number\"\n variant={variant}\n size={size}\n value={String(value ?? '')}\n onChange={(e) => setEditValue(e.target.value)}\n autoFocus\n fullWidth\n clearable={false}\n />\n );\n\n case 'date':\n return (\n <Input\n type=\"date\"\n variant={variant}\n size={size}\n value={String(value ?? '')}\n onChange={(e) => setEditValue(e.target.value)}\n autoFocus\n fullWidth\n clearable={false}\n />\n );\n\n case 'select': {\n // Auto-commit as soon as the user picks an option; the Dropdown's portal\n // means we can't rely on a simple onBlur for this cell type.\n // autoOpen=true so the dropdown opens on the same click that entered\n // edit mode - no second click needed.\n const selectOptions = (col.options ?? []).map((o) => ({\n id: o.value,\n value: o.value,\n label: o.label,\n }));\n\n return (\n <Select\n options={selectOptions}\n value={String(value ?? '')}\n autoOpen\n fullWidth\n inputProps={{ variant, size }}\n onChange={(v) => {\n const selected = Array.isArray(v) ? (v[0] ?? '') : v;\n const newData = localDataRef.current.map<T>((r, i) =>\n i === rowIndex ? ({ ...r, [col.key]: selected } as T) : r,\n );\n setLocalData(newData);\n onChange?.(newData);\n setEditingCell(null);\n }}\n clearable={false}\n />\n );\n }\n\n default:\n return (\n <Input\n type=\"text\"\n variant={variant}\n size={size}\n value={String(value ?? '')}\n onChange={(e) => setEditValue(e.target.value)}\n autoFocus\n fullWidth\n clearable={false}\n />\n );\n }\n };\n\n // ── Card view cell value ─────────────────────────────────────────────────\n // Mirrors the <td> cell's click/edit/error-state wiring in `renderViewCell`/\n // `renderEditCell` above, just wrapped in a plain <div> so it can sit\n // either in a card's label:value field row or its header/subheader - both\n // stay just as editable as any table cell.\n const renderCardValue = (\n col: InternalColumn<T>,\n row: T,\n localIndex: number,\n baseClassName: string,\n ): React.ReactNode => {\n const value = cellOf(row, col.key);\n const isEditing = editingCell?.rowIndex === localIndex && editingCell?.colKey === col.key;\n const canEdit = isCellEditable(col);\n const isFlagged = cellErrorKey(localIndex, col.key) in cellErrors;\n const hasError = isEditing && Boolean(editError);\n\n const valueCls = [\n baseClassName,\n // Deliberately NOT `eidos-data-grid-cell--editing` (unlike the <td>\n // version of this same state) - that class's whole job is forcing the\n // editor into `position: absolute; inset: 0` so it fills a <td>\n // without changing the table row's height. A card field has no row\n // to preserve the height of, so it just needs its own \"is editing\"\n // hook for spacing - the editor itself renders normally, with each\n // component's own default (non-`bare`) look (see `renderEditCell`).\n isEditing && 'eidos-datagrid-card-value--editing',\n hasError && 'eidos-data-grid-cell--error',\n isFlagged && !hasError && 'eidos-data-grid-cell--has-error',\n canEdit && !isEditing && 'eidos-data-grid-cell--editable',\n col.type === 'readonly' && 'eidos-data-grid-cell--readonly',\n // Checkbox glyph is intrinsically narrow - without this the value\n // wrapper stretches to the card's full width (block layout), making\n // the whole row clickable/toggleable instead of just the checkbox.\n col.type === 'checkbox' && 'eidos-datagrid-card-value--checkbox',\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <div\n ref={\n hasError ? (errorTooltipAnchorRef as React.RefObject<HTMLDivElement | null>) : undefined\n }\n className={valueCls}\n onClick={() => handleCellClick(localIndex, col)}\n onKeyDown={isEditing ? (e) => handleKeyDown(e, localIndex, col.key) : undefined}\n tabIndex={isEditing ? -1 : undefined}\n >\n {isEditing\n ? renderEditCell(col, editValue, row, localIndex, 'card')\n : renderViewCell(col, value, row, localIndex)}\n </div>\n );\n };\n\n // Label-above-value field, stacked - used for every column that isn't the\n // card's header/subheader.\n const renderCardField = (col: InternalColumn<T>, row: T, localIndex: number): React.ReactNode => (\n <div key={col.key} className=\"eidos-datagrid-card-field\">\n <span className=\"eidos-datagrid-card-field-label\">{col.header}</span>\n {renderCardValue(col, row, localIndex, 'eidos-datagrid-card-field-value')}\n </div>\n );\n\n // ── Row-actions menu (type: 'actions' column) ──────────────────────────────\n // Shared between the table's dedicated actions <td> and the card's\n // top-right toolbar slot - see `actionsColumn`/`DataGridColumn.actions`.\n // Renders nothing if there's no actions column, or it has no actions.\n const renderActionsMenu = (row: T, localIndex: number): React.ReactNode => {\n if (!actionsColumn) return null;\n\n const items: MenuItemType[] = actionsColumn.renderActions\n ? actionsColumn.renderActions(row, localIndex)\n : (actionsColumn.actions ?? []).flatMap((action, index) => {\n const id = action.id ?? `${action.label}-${index}`;\n const isDisabled =\n typeof action.disabled === 'function' ? action.disabled(row) : Boolean(action.disabled);\n\n const item: MenuItemType = {\n id,\n type: 'item',\n label: action.label,\n icon: action.icon,\n disabled: isDisabled,\n color: action.danger ? 'danger' : undefined,\n onClick: () => action.onClick(row, localIndex),\n };\n\n return action.divider\n ? [{ id: `${id}-divider`, type: 'separator' as const }, item]\n : [item];\n });\n\n if (!items.length) return null;\n\n return (\n <Menu\n trigger={\n <button type=\"button\" className=\"eidos-datagrid-actions-trigger\" aria-label=\"Row actions\">\n {actionsColumn.actionsIcon ? (\n renderIcon(actionsColumn.actionsIcon, 'eidos-datagrid-actions-trigger-icon')\n ) : (\n <MoreVertical size={16} />\n )}\n </button>\n }\n items={items}\n />\n );\n };\n\n // Columns sorted for rendering: pin:left → unpinned → pin:right. Excludes\n // the actions column (see `dataColumns`) - it's never a normal, sortable/\n // pinnable data column, always rendered in its own dedicated slot instead.\n // This ensures a pinned column defined in the middle of the columns array\n // is always displayed at the correct edge regardless of its original position.\n // The original `columns` prop is still used for key-based lookups above.\n const sortedColumns = useMemo(\n () => [\n ...dataColumns.filter((c) => c.pin === 'left'),\n ...dataColumns.filter((c) => !c.pin),\n ...dataColumns.filter((c) => c.pin === 'right'),\n ],\n [dataColumns],\n );\n\n const totalCols =\n dataColumns.length +\n (showRowNumbers ? 1 : 0) +\n (actionsColumn ? 1 : 0) +\n (draggableRows ? 1 : 0) +\n (selectable ? 1 : 0) +\n (expandable ? 1 : 0);\n\n // Row IDs for SortableContext - must be the displayed rows (not full dataset)\n // so dnd-kit knows which items are currently rendered.\n const rowIds = displayData.map((row, i) => String(cellOf(row, rowKeyField) ?? i));\n\n const containerStyle: React.CSSProperties = {};\n if (stickyHeader && maxHeight) {\n containerStyle.maxHeight = typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight;\n }\n\n // Toolbar visibility: show whenever filters, quick filters, density picker,\n // selectable, or add-row are active.\n const showToolbar =\n !!showFilters || quickFilters.length > 0 || !!showDensity || !!selectable || !!onRowAdd;\n\n // Quick filters share the toolbar's left zone with the selection count and\n // bulk actions, which take it over entirely while rows are selected.\n const showQuickFilters = quickFilters.length > 0 && !(selectable && hasSelection);\n\n const renderQuickFilter = (quickFilter: DataGridQuickFilter): React.ReactElement => {\n const { key, label, disabled, width } = quickFilter;\n const value = activeFilters[key];\n // Only set inline when supplied - the default lives in the stylesheet, so\n // the common case emits no style attribute at all.\n const style =\n width === undefined\n ? undefined\n : ({\n '--eidos-datagrid-quick-filter-width': typeof width === 'number' ? `${width}px` : width,\n } as React.CSSProperties);\n\n const control = (() => {\n switch (quickFilter.type) {\n case 'segmented':\n return (\n <SegmentedControl\n size=\"sm\"\n color={quickFilter.color}\n disabled={disabled}\n // The reset segment is always prepended: a SegmentedControl\n // always has exactly one segment selected, so without it the\n // filter could never be cleared.\n options={[\n { value: QUICK_FILTER_RESET_VALUE, label: quickFilter.allLabel ?? 'All' },\n ...quickFilter.options,\n ]}\n value={typeof value === 'string' ? value : QUICK_FILTER_RESET_VALUE}\n onChange={(next) => handleQuickFilterChange(key, next)}\n />\n );\n case 'combobox':\n return (\n <Combobox\n size=\"sm\"\n fullWidth\n disabled={disabled}\n placeholder={quickFilter.placeholder ?? label}\n clearable={quickFilter.clearable ?? true}\n emptyText={quickFilter.emptyText}\n loading={quickFilter.loading}\n loadingText={quickFilter.loadingText}\n onSearch={quickFilter.onSearch}\n options={(quickFilter.options ?? []).map((option) => ({\n ...option,\n id: option.value,\n }))}\n value={typeof value === 'string' ? value : ''}\n onChange={(next) => handleQuickFilterChange(key, next)}\n />\n );\n case 'select':\n default:\n return (\n <Select\n fullWidth\n inputProps={{ size: 'sm' }}\n disabled={disabled}\n placeholder={quickFilter.placeholder ?? label}\n clearable={quickFilter.clearable ?? true}\n multiple={quickFilter.multiple}\n options={quickFilter.options.map((option) => ({ ...option, id: option.value }))}\n // Never `undefined` - Select and Combobox both treat that as\n // \"uncontrolled\" and would then keep showing a value the\n // filter state no longer holds after a clear.\n value={\n quickFilter.multiple\n ? Array.isArray(value)\n ? value\n : []\n : typeof value === 'string'\n ? value\n : ''\n }\n onChange={(next) => handleQuickFilterChange(key, next)}\n />\n );\n }\n })();\n\n return (\n <div\n key={key}\n className={`eidos-datagrid-quick-filter eidos-datagrid-quick-filter--${quickFilter.type}`}\n style={style}\n // No visible field label fits in the toolbar, so the group carries the\n // accessible name for whichever control it wraps.\n role=\"group\"\n aria-label={label}\n >\n {control}\n </div>\n );\n };\n\n // Density modifier class (comfortable = default = no extra class)\n const densityClass =\n effectiveDensity !== 'comfortable' ? `eidos-datagrid--${effectiveDensity}` : '';\n\n // Always include the current page size in the options so the Select is never blank.\n const effectivePageSizeOptions = useMemo(() => {\n const opts = pageSizeOptions ?? [10, 25, 50, 100];\n return opts.includes(pageSize) ? opts : [...opts, pageSize].sort((a, b) => a - b);\n }, [pageSizeOptions, pageSize]);\n\n // ── Loading state (preserved) ───────────────────────────────────────────────\n // `ref={setContainerRef}` here too (not just the main return below) - a\n // consumer whose `loading` prop starts `true` on mount renders this\n // branch first, and without it `hasCardView` would never get a container\n // to measure until some unrelated re-render happened to remount it - see\n // `setContainerRef`'s comment above for the full explanation.\n if (loading) {\n return (\n <div\n ref={setContainerRef}\n className={['eidos-data-grid-container', className].filter(Boolean).join(' ')}\n >\n <div className=\"eidos-data-grid-loading\">\n <Spinner size=\"md\" />\n <p>Loading…</p>\n </div>\n </div>\n );\n }\n\n // ── Render ─────────────────────────────────────────────────────────────────\n // Pre-compute sticky props for system columns so we can spread them on\n // header <th>s and body <td>s without calling the helpers multiple times.\n const expandHeaderPin = getHeaderPinnedProps('__expand__');\n const selHeaderPin = getHeaderPinnedProps('__sel__');\n const dragHeaderPin = getHeaderPinnedProps('__drag__');\n const rownumHeaderPin = getHeaderPinnedProps('__rownum__');\n const expandCellPin = getCellPinnedProps('__expand__');\n const selCellPin = getCellPinnedProps('__sel__');\n const dragCellPin = getCellPinnedProps('__drag__');\n const rownumCellPin = getCellPinnedProps('__rownum__');\n\n return (\n <div\n ref={setContainerRef}\n className={[\n 'eidos-data-grid-container',\n stickyHeader && 'eidos-data-grid-container--sticky-header',\n densityClass,\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={containerStyle}\n >\n {/* ── Toolbar ─────────────────────────────────────────────────────── */}\n {showToolbar && (\n <div className=\"eidos-datagrid-toolbar\">\n {/* Left zone: quick filters, or selection count + bulk actions */}\n <div className=\"eidos-datagrid-toolbar-left\">\n {showQuickFilters && (\n <div className=\"eidos-datagrid-quick-filters\">\n {quickFilters.map(renderQuickFilter)}\n </div>\n )}\n\n {selectable && hasSelection && (\n <>\n <span className=\"eidos-datagrid-selection-count\">{selectedSet.size} selected</span>\n\n {bulkActions && bulkActions.length > 0 && (\n <div className=\"eidos-datagrid-bulk-actions\">\n {bulkActions.map((action) => {\n const isDisabled =\n typeof action.disabled === 'function'\n ? action.disabled(selectedItems)\n : (action.disabled ?? false);\n\n if (action.type === 'split-button') {\n return (\n <SplitButton\n key={action.id}\n size=\"sm\"\n variant={action.variant ?? 'outlined'}\n color={action.color ?? 'secondary'}\n preIcon={action.icon}\n disabled={isDisabled}\n label={action.label}\n onClick={() => action.onClick(selectedItems)}\n options={action.options.map((option) => ({\n id: option.id,\n label: option.label,\n icon: option.icon,\n disabled: option.disabled,\n onClick: () => option.onClick(selectedItems),\n }))}\n />\n );\n }\n\n return action.label ? (\n <Button\n key={action.id}\n size=\"sm\"\n variant={action.variant ?? 'outlined'}\n color={action.color ?? 'secondary'}\n preIcon={action.icon}\n posIcon={action.posIcon}\n disabled={isDisabled}\n onClick={() => action.onClick(selectedItems)}\n >\n {action.label}\n </Button>\n ) : action.icon ? (\n <Button\n key={action.id}\n size=\"sm\"\n variant={action.variant ?? 'outlined'}\n color={action.color ?? 'secondary'}\n icon={action.icon}\n disabled={isDisabled}\n onClick={() => action.onClick(selectedItems)}\n />\n ) : null;\n })}\n </div>\n )}\n\n <button\n type=\"button\"\n className=\"eidos-datagrid-clear-selection\"\n onClick={clearSelection}\n aria-label=\"Clear selection\"\n >\n <X size={14} />\n </button>\n </>\n )}\n </div>\n\n {/* Right zone: density picker + filter button + add-row button */}\n <div className=\"eidos-datagrid-toolbar-right\">\n {showDensity && (\n <Dropdown\n placement=\"bottom\"\n align=\"end\"\n autoWidth={false}\n trigger={\n <Button variant=\"text\" size=\"sm\" preIcon={AlignJustify}>\n Density\n </Button>\n }\n content={\n <div className=\"eidos-table-menu-panel\">\n {(\n [\n { value: 'compact', label: 'Compact' },\n { value: 'comfortable', label: 'Comfortable' },\n { value: 'spacious', label: 'Spacious' },\n ] as const\n ).map((option) => (\n <button\n key={option.value}\n className={[\n 'eidos-table-menu-item',\n effectiveDensity === option.value ? 'eidos-table-menu-item--active' : '',\n ]\n .filter(Boolean)\n .join(' ')}\n onClick={() => setInternalDensity(option.value)}\n >\n <span className=\"eidos-table-menu-item-check\">\n {effectiveDensity === option.value && <Check size={14} />}\n </span>\n <span>{option.label}</span>\n </button>\n ))}\n </div>\n }\n />\n )}\n\n {showFilters && filterDropdownColumns.length > 0 && (\n <TableFiltersDropdown<T>\n columns={filterDropdownColumns}\n filters={dropdownFilters}\n onFiltersChange={handleFiltersChange}\n />\n )}\n\n {onRowAdd && (\n <Button variant=\"outlined\" size=\"sm\" preIcon={Plus} onClick={handleRowAdd}>\n Add row\n </Button>\n )}\n </div>\n </div>\n )}\n\n {isCardView ? (\n /* ── Card view ────────────────────────────────────────────────── */\n <div\n className=\"eidos-datagrid-cards\"\n style={{ '--eidos-datagrid-card-min-width': `${cardMinWidth}px` } as React.CSSProperties}\n >\n {displayData.length > 0 ? (\n displayData.map((row, displayIndex) => {\n const localIndex = localData.indexOf(row);\n const rowKeyValue = String(cellOf(row, rowKeyField) ?? displayIndex);\n const isRowSelected = selectable && selectedSet.has(rowKeyValue);\n const isRowExpanded = expandable && expandedSet.has(rowKeyValue);\n const canExpandRow = !isRowExpandable || isRowExpandable(row);\n\n return (\n <div\n key={rowKeyValue}\n className={[\n 'eidos-datagrid-card',\n isRowSelected && 'eidos-datagrid-card--selected',\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {/* stopPropagation so the checkbox/actions menu never bubble\n into a consumer's own row-click handler (e.g. opening a\n details drawer) - mirrors the selection/drag-handle <td>s'\n own `onClick={(e) => e.stopPropagation()}` in table mode. */}\n <div className=\"eidos-datagrid-card-toolbar\" onClick={(e) => e.stopPropagation()}>\n <div className=\"eidos-datagrid-card-toolbar-left\">\n {expandable && canExpandRow && (\n <button\n type=\"button\"\n className={[\n 'eidos-datagrid-expand-toggle',\n isRowExpanded && 'eidos-datagrid-expand-toggle--open',\n ]\n .filter(Boolean)\n .join(' ')}\n onClick={() => toggleExpanded(rowKeyValue)}\n aria-expanded={isRowExpanded}\n aria-label={isRowExpanded ? 'Collapse row' : 'Expand row'}\n >\n <ChevronRight size={14} />\n </button>\n )}\n {selectable && (\n <Checkbox\n checked={isRowSelected}\n onChange={() => toggleRow(rowKeyValue)}\n size=\"sm\"\n />\n )}\n {showRowNumbers && (\n <span className=\"eidos-datagrid-card-row-number\">#{localIndex + 1}</span>\n )}\n </div>\n\n {/* Always rendered (even empty) so it occupies the toolbar's\n middle grid column - conditionally omitting the element\n itself would shift `eidos-datagrid-card-toolbar-right`\n into this column instead of the third one. */}\n <div className=\"eidos-datagrid-card-heading\">\n {cardHeaderColumn &&\n renderCardValue(\n cardHeaderColumn,\n row,\n localIndex,\n 'eidos-datagrid-card-heading-title',\n )}\n {cardSubheaderColumn &&\n renderCardValue(\n cardSubheaderColumn,\n row,\n localIndex,\n 'eidos-datagrid-card-heading-subtitle',\n )}\n </div>\n\n <div className=\"eidos-datagrid-card-toolbar-right\">\n {renderActionsMenu(row, localIndex)}\n </div>\n </div>\n\n <div className=\"eidos-datagrid-card-fields\">\n {cardFieldColumns.map((col) => renderCardField(col, row, localIndex))}\n </div>\n\n {expandable && renderExpandedContent && (\n <ExpandedCardPanel\n isOpen={isRowExpanded}\n render={() => renderExpandedContent(row, localIndex)}\n />\n )}\n </div>\n );\n })\n ) : (\n <div className=\"eidos-datagrid-card-empty\">\n {Object.keys(activeFilters).length > 0 ? (\n <EmptyState\n icon={<SearchX />}\n title=\"No results found\"\n description=\"Try adjusting your filters or search terms.\"\n action={\n <Button size=\"sm\" variant=\"outlined\" color=\"primary\" onClick={clearAllFilters}>\n Clear filters\n </Button>\n }\n size=\"sm\"\n />\n ) : (\n <EmptyState\n icon={<FolderOpen />}\n title={emptyText}\n description={\n onRowAdd ? 'Add a row to get started.' : 'There are no records to display.'\n }\n size=\"sm\"\n />\n )}\n </div>\n )}\n </div>\n ) : (\n <>\n {/* ── Table (wrapped for horizontal scroll) ───────────────────────── */}\n <DndContext\n sensors={sensors}\n collisionDetection={closestCenter}\n onDragEnd={handleRowDragEnd}\n >\n <div className=\"eidos-data-grid-scroll\">\n <table className={['eidos-data-grid', densityClass].filter(Boolean).join(' ')}>\n <thead ref={theadRef}>\n <tr>\n {/* Drag-handle column - must come before every other system column */}\n {draggableRows && (\n <th\n className={[\n 'eidos-data-grid-header-cell',\n 'eidos-datagrid-drag-handle-cell',\n dragHeaderPin.className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={dragHeaderPin.style}\n data-col-key=\"__drag__\"\n />\n )}\n\n {/* Expand-toggle column */}\n {expandable && (\n <th\n className={[\n 'eidos-data-grid-header-cell',\n 'eidos-datagrid-expand-cell',\n expandHeaderPin.className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={expandHeaderPin.style}\n data-col-key=\"__expand__\"\n />\n )}\n\n {/* Selection checkbox column - must come before row numbers */}\n {selectable && (\n <th\n className={[\n 'eidos-data-grid-header-cell',\n 'eidos-datagrid-checkbox-cell',\n selHeaderPin.className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={selHeaderPin.style}\n data-col-key=\"__sel__\"\n >\n <Checkbox\n checked={allSelected}\n indeterminate={someSelected}\n onChange={toggleAll}\n size=\"sm\"\n />\n </th>\n )}\n\n {showRowNumbers && (\n <th\n className={[\n 'eidos-data-grid-header-cell',\n 'eidos-data-grid-row-number-col',\n rownumHeaderPin.className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={rownumHeaderPin.style}\n data-col-key=\"__rownum__\"\n >\n #\n </th>\n )}\n\n {sortedColumns.map((col) => {\n const colKey = col.key;\n const isSortable = !!col.sortable;\n const isCurrentlySorted = activeSort?.key === colKey;\n const sortDir = isCurrentlySorted ? activeSort!.direction : null;\n const alignClass = `eidos-datagrid-align-${col.align ?? 'left'}`;\n const { style: pinnedStyle, className: pinnedClass } =\n getHeaderPinnedProps(colKey);\n\n return (\n <th\n key={colKey}\n className={[\n 'eidos-data-grid-header-cell',\n isSortable && 'eidos-datagrid-header-cell--sortable',\n isCurrentlySorted && 'eidos-datagrid-header-cell--sorted',\n alignClass,\n pinnedClass,\n ]\n .filter(Boolean)\n .join(' ')}\n style={{\n width:\n col.width != null\n ? typeof col.width === 'number'\n ? `${col.width}px`\n : col.width\n : undefined,\n minWidth:\n col.minWidth != null\n ? typeof col.minWidth === 'number'\n ? `${col.minWidth}px`\n : col.minWidth\n : undefined,\n ...pinnedStyle,\n }}\n data-col-key={colKey}\n onClick={isSortable ? () => handleSortClick(colKey) : undefined}\n >\n <div className={`eidos-datagrid-header-content ${alignClass}`}>\n <span className=\"eidos-datagrid-header-label\">{col.header}</span>\n {isSortable && (\n <span className=\"eidos-datagrid-sort-icon\">\n {sortDir === 'asc' && <ArrowUp size={14} />}\n {sortDir === 'desc' && <ArrowDown size={14} />}\n {!sortDir && <ChevronsUpDown size={14} />}\n </span>\n )}\n </div>\n </th>\n );\n })}\n\n {actionsColumn && (\n <th\n className=\"eidos-data-grid-header-cell eidos-data-grid-actions-col\"\n data-col-key=\"__actions__\"\n />\n )}\n </tr>\n </thead>\n\n <tbody>\n <SortableContext items={rowIds} strategy={verticalListSortingStrategy}>\n {displayData.length > 0 ? (\n displayData.map((row, displayIndex) => {\n // Find this row's position in the full localData array.\n // displayData rows are the same object references as in localData\n // (filter/sort/slice never clone row objects), so indexOf is O(n)\n // but always accurate - even after edits that produce new row objects.\n const localIndex = localData.indexOf(row);\n const rowKeyValue = String(cellOf(row, rowKeyField) ?? displayIndex);\n const isRowSelected = selectable && selectedSet.has(rowKeyValue);\n const isRowExpanded = expandable && expandedSet.has(rowKeyValue);\n const canExpandRow = !isRowExpandable || isRowExpandable(row);\n\n return (\n // React.Fragment (rather than SortableTableRow taking the\n // `key` directly) so an expanded row's detail <tr> - see\n // ExpandedTableRow below - can sit right underneath it as\n // a sibling, keyed together as one reconciliation unit.\n <React.Fragment key={rowKeyValue}>\n {/* SortableTableRow is always rendered (hooks unconditional);\n disabled={true} when draggableRows is off so dnd-kit is a no-op. */}\n <SortableTableRow\n id={rowKeyValue}\n disabled={!draggableRows}\n className={[\n 'eidos-data-grid-row',\n isRowSelected && 'eidos-datagrid-row--selected',\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {(dragHandleProps, isDragging) => (\n <>\n {draggableRows && (\n <td\n className={[\n 'eidos-datagrid-drag-handle-cell',\n dragCellPin.className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={dragCellPin.style}\n // Prevent a click on the handle from triggering cell editing\n onClick={(e) => e.stopPropagation()}\n >\n <span\n {...(dragHandleProps as React.HTMLAttributes<HTMLSpanElement>)}\n className={[\n 'eidos-datagrid-drag-handle',\n isDragging && 'eidos-datagrid-drag-handle--dragging',\n ]\n .filter(Boolean)\n .join(' ')}\n title=\"Drag to reorder\"\n >\n <GripVertical size={14} />\n </span>\n </td>\n )}\n\n {expandable && (\n <td\n className={[\n 'eidos-data-grid-cell',\n 'eidos-datagrid-expand-cell',\n expandCellPin.className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={expandCellPin.style}\n // Prevent a click on the toggle from triggering cell editing\n onClick={(e) => e.stopPropagation()}\n >\n {canExpandRow && (\n <button\n type=\"button\"\n className={[\n 'eidos-datagrid-expand-toggle',\n isRowExpanded && 'eidos-datagrid-expand-toggle--open',\n ]\n .filter(Boolean)\n .join(' ')}\n onClick={() => toggleExpanded(rowKeyValue)}\n aria-expanded={isRowExpanded}\n aria-label={isRowExpanded ? 'Collapse row' : 'Expand row'}\n >\n <ChevronRight size={14} />\n </button>\n )}\n </td>\n )}\n\n {selectable && (\n <td\n className={[\n 'eidos-data-grid-cell',\n 'eidos-datagrid-checkbox-cell',\n selCellPin.className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={selCellPin.style}\n onClick={(e) => e.stopPropagation()}\n >\n <Checkbox\n checked={isRowSelected}\n onChange={() => toggleRow(rowKeyValue)}\n size=\"sm\"\n />\n </td>\n )}\n\n {showRowNumbers && (\n <td\n className={[\n 'eidos-data-grid-cell',\n 'eidos-data-grid-row-number',\n rownumCellPin.className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={rownumCellPin.style}\n >\n {localIndex + 1}\n </td>\n )}\n\n {sortedColumns.map((col) => {\n const value = cellOf(row, col.key);\n const isEditing =\n editingCell?.rowIndex === localIndex &&\n editingCell?.colKey === col.key;\n const canEdit = isCellEditable(col);\n // Persists for a cell regardless of focus (background tint only);\n // the border + message only show once this exact cell is\n // focused again, via `hasError` below.\n const isFlagged =\n cellErrorKey(localIndex, col.key) in cellErrors;\n const hasError = isEditing && Boolean(editError);\n const { style: pinnedStyle, className: pinnedClass } =\n getCellPinnedProps(col.key);\n\n const cellCls = [\n 'eidos-data-grid-cell',\n isEditing && 'eidos-data-grid-cell--editing',\n hasError && 'eidos-data-grid-cell--error',\n isFlagged && !hasError && 'eidos-data-grid-cell--has-error',\n canEdit && !isEditing && 'eidos-data-grid-cell--editable',\n col.type === 'readonly' && 'eidos-data-grid-cell--readonly',\n `eidos-datagrid-align-${col.align ?? 'left'}`,\n pinnedClass,\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <td\n key={col.key}\n ref={\n hasError\n ? (errorTooltipAnchorRef as React.RefObject<HTMLTableDataCellElement | null>)\n : undefined\n }\n className={cellCls}\n style={pinnedStyle}\n onClick={() => handleCellClick(localIndex, col)}\n onKeyDown={\n isEditing\n ? (e) => handleKeyDown(e, localIndex, col.key)\n : undefined\n }\n // Make editing cells focusable so keydown events register\n tabIndex={isEditing ? -1 : undefined}\n >\n {isEditing\n ? renderEditCell(col, editValue, row, localIndex)\n : renderViewCell(col, value, row, localIndex)}\n </td>\n );\n })}\n\n {actionsColumn && (\n <td\n className=\"eidos-data-grid-cell eidos-data-grid-actions-col\"\n // Prevent a click on the actions menu from bubbling into a\n // consumer's own row-click handler (e.g. opening a details\n // drawer) - mirrors the selection/drag-handle <td>s above.\n onClick={(e) => e.stopPropagation()}\n >\n {renderActionsMenu(row, localIndex)}\n </td>\n )}\n </>\n )}\n </SortableTableRow>\n\n {expandable && renderExpandedContent && (\n <ExpandedTableRow\n isOpen={isRowExpanded}\n colSpan={totalCols}\n render={() => renderExpandedContent(row, localIndex)}\n />\n )}\n </React.Fragment>\n );\n })\n ) : (\n <tr className=\"eidos-data-grid-empty-row\">\n <td\n colSpan={totalCols}\n className=\"eidos-data-grid-cell eidos-data-grid-empty-cell\"\n >\n {Object.keys(activeFilters).length > 0 ? (\n <EmptyState\n icon={<SearchX />}\n title=\"No results found\"\n description=\"Try adjusting your filters or search terms.\"\n action={\n <Button\n size=\"sm\"\n variant=\"outlined\"\n color=\"primary\"\n onClick={clearAllFilters}\n >\n Clear filters\n </Button>\n }\n size=\"sm\"\n />\n ) : (\n <EmptyState\n icon={<FolderOpen />}\n title={emptyText}\n description={\n onRowAdd\n ? 'Add a row to get started.'\n : 'There are no records to display.'\n }\n size=\"sm\"\n />\n )}\n </td>\n </tr>\n )}\n </SortableContext>\n </tbody>\n </table>\n </div>\n </DndContext>\n </>\n )}\n\n {/* ── Pagination footer ────────────────────────────────────────────── */}\n {showPagination && (\n <div className=\"eidos-datagrid-footer\">\n <Pagination\n page={currentPage}\n totalPages={totalPages}\n onChange={handlePageChange}\n totalItems={totalItemCount}\n pageSize={currentPageSize}\n onPageSizeChange={handlePageSizeChange}\n pageSizeOptions={effectivePageSizeOptions}\n />\n </div>\n )}\n\n {/* Portaled rather than a child of the erroring cell - see errorTooltipPosition above */}\n {activeCellError &&\n errorTooltipPosition &&\n createPortal(\n <div\n className=\"eidos-data-grid-cell-error\"\n style={{ top: errorTooltipPosition.top, left: errorTooltipPosition.left }}\n >\n {activeCellError}\n </div>,\n document.body,\n )}\n </div>\n );\n}\n\n// Assign displayName cleanly without losing the generic signature\nexport const DataGrid = Object.assign(DataGridInner, { displayName: 'DataGrid' });\n"]}
@@ -4418,4 +4418,4 @@ export {
4418
4418
  sortableKeyboardCoordinates,
4419
4419
  TableFiltersDropdown
4420
4420
  };
4421
- //# sourceMappingURL=chunk-6DBCT5MK.js.map
4421
+ //# sourceMappingURL=chunk-AJZTVCST.js.map