diff --git a/apps/client/src/features/base/components/grid/grid-container.tsx b/apps/client/src/features/base/components/grid/grid-container.tsx index 803cf161..3334a2e8 100644 --- a/apps/client/src/features/base/components/grid/grid-container.tsx +++ b/apps/client/src/features/base/components/grid/grid-container.tsx @@ -210,6 +210,7 @@ export function GridContainer({ table={table} baseId={baseId} columnOrder={table.getState().columnOrder} + loadedRowIds={rowIds} onPropertyCreated={handlePropertyCreated} /> diff --git a/apps/client/src/features/base/components/grid/grid-header-cell.tsx b/apps/client/src/features/base/components/grid/grid-header-cell.tsx index 9d54c80b..9fe7bebe 100644 --- a/apps/client/src/features/base/components/grid/grid-header-cell.tsx +++ b/apps/client/src/features/base/components/grid/grid-header-cell.tsx @@ -23,6 +23,8 @@ import { IconUserEdit, } from "@tabler/icons-react"; import { PropertyMenuContent } from "@/features/base/components/property/property-menu"; +import { RowNumberHeaderCell } from "./row-number-header-cell"; +import { useRowSelection } from "@/features/base/hooks/use-row-selection"; import classes from "@/features/base/styles/grid.module.css"; const typeIcons: Record = { @@ -44,10 +46,12 @@ const typeIcons: Record = { type GridHeaderCellProps = { header: Header; + loadedRowIds: string[]; }; export const GridHeaderCell = memo(function GridHeaderCell({ header, + loadedRowIds, }: GridHeaderCellProps) { const property = header.column.columnDef.meta?.property as | IBaseProperty @@ -55,6 +59,8 @@ export const GridHeaderCell = memo(function GridHeaderCell({ const isRowNumber = header.column.id === "__row_number"; const isPinned = header.column.getIsPinned(); const pinOffset = isPinned ? header.column.getStart("left") : undefined; + const { selectionCount } = useRowSelection(); + const hasSelection = selectionCount > 0; const [activePropertyMenu, setActivePropertyMenu] = useAtom(activePropertyMenuAtom) as unknown as [string | null, (val: string | null) => void]; const menuOpened = activePropertyMenu === header.column.id; @@ -118,7 +124,7 @@ export const GridHeaderCell = memo(function GridHeaderCell({ return (
{isRowNumber ? ( - flexRender(header.column.columnDef.header, header.getContext()) + ) : (
{TypeIcon && ( diff --git a/apps/client/src/features/base/components/grid/grid-header.tsx b/apps/client/src/features/base/components/grid/grid-header.tsx index add400fe..9c026bdd 100644 --- a/apps/client/src/features/base/components/grid/grid-header.tsx +++ b/apps/client/src/features/base/components/grid/grid-header.tsx @@ -11,6 +11,7 @@ type GridHeaderProps = { // Passed explicitly to break memo when columns change // (table ref is stable from useReactTable, so memo won't fire without this) columnOrder: ColumnOrderState; + loadedRowIds: string[]; onPropertyCreated?: () => void; }; @@ -19,6 +20,7 @@ export const GridHeader = memo(function GridHeader({ baseId, // eslint-disable-next-line @typescript-eslint/no-unused-vars columnOrder: _columnOrder, + loadedRowIds, onPropertyCreated, }: GridHeaderProps) { const headerGroups = table.getHeaderGroups(); @@ -26,7 +28,7 @@ export const GridHeader = memo(function GridHeader({ return (
{headerGroups[0]?.headers.map((header) => ( - + ))} {baseId && ( { + if (loadedRowIds.length === 0) { + return { checked: false, indeterminate: false }; + } + const selectedInLoaded = loadedRowIds.reduce( + (acc, id) => (selectedIds.has(id) ? acc + 1 : acc), + 0, + ); + return { + checked: selectedInLoaded === loadedRowIds.length, + indeterminate: + selectedInLoaded > 0 && selectedInLoaded < loadedRowIds.length, + }; + }, [loadedRowIds, selectedIds]); + + if (loadedRowIds.length === 0) return null; + + return ( +
+ # + + + toggleAll(loadedRowIds)} + aria-label="Select all loaded rows" + /> + + +
+ ); +}); diff --git a/apps/client/src/features/base/styles/grid.module.css b/apps/client/src/features/base/styles/grid.module.css index e66a8199..73466d68 100644 --- a/apps/client/src/features/base/styles/grid.module.css +++ b/apps/client/src/features/base/styles/grid.module.css @@ -347,3 +347,30 @@ .rowSelected .cell { background: light-dark(var(--mantine-color-blue-0), var(--mantine-color-dark-6)); } + +.rowNumberHeaderInner { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; +} + +.rowNumberHeaderHash { + color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3)); + font-size: var(--mantine-font-size-xs); +} + +.rowNumberHeaderCheckbox { + display: none; +} + +.headerCell:hover .rowNumberHeaderHash, +.hasSelection .rowNumberHeaderHash { + display: none; +} + +.headerCell:hover .rowNumberHeaderCheckbox, +.hasSelection .rowNumberHeaderCheckbox { + display: inline-flex; +}