import React, { useEffect, useRef } from 'react';
import { Button, Checkbox, ScrollView, Separator, Text, XStack, YStack } from 'tamagui';
import { getIcon } from '../IconMapper.jsx';
import { PageNavBar } from '../PageNavBar.jsx';
import { useGridView } from './context.js';
import { getTypographyRoleProps } from '../../styles/index.js';
import {
formatValueByColumn,
getColumnJustify,
getColumnLayoutStyle,
resolveCellAlignment,
resolveCellValue
} from './utils.js';
function DefaultGridCellRenderer({ value, column }) {
return (
{formatValueByColumn(value, column)}
);
}
function UtilityCell({ rowId }) {
const grid = useGridView();
const ChevronRightIcon = getIcon('chevron-right');
if (!grid.selectable && !grid.nested) {
return null;
}
if (grid.nested) {
return (
{ChevronRightIcon ? : {'>'}}
);
}
return (
grid.toggleSelectRow(rowId)}
borderColor="$lineStrong"
backgroundColor="$bgPanel"
focusStyle={{ borderColor: '$accent' }}
>
{(() => {
const Check = getIcon('check');
return Check ? : null;
})()}
);
}
function useViewportTracking(enabled = true) {
const grid = useGridView();
const bodyRef = useRef(null);
useEffect(() => {
if (!enabled || typeof window === 'undefined') {
return undefined;
}
const updateViewportWidth = () => {
const element = bodyRef.current;
if (!element) {
return;
}
const nextWidth = Math.max(0, Math.round(element.getBoundingClientRect().width));
grid.setTableViewportWidth((current) => (current === nextWidth ? current : nextWidth));
};
updateViewportWidth();
window.addEventListener('resize', updateViewportWidth);
return () => {
window.removeEventListener('resize', updateViewportWidth);
};
}, [enabled, grid]);
return bodyRef;
}
export function TableHeader({ visible = true, showTopBorder = true }) {
const grid = useGridView();
if (visible === false) {
return null;
}
const activeColumns = grid.visibleColumns?.length ? grid.visibleColumns : grid.resolvedColumns;
const CaretUp = getIcon('caret-up');
const CaretDown = getIcon('caret-down');
const allIds = (grid.rows || []).map((row) => row?.id).filter((id) => id != null);
const selectedCount = allIds.filter((id) => grid.selectedIds.has(id)).length;
const allSelected = allIds.length > 0 && selectedCount === allIds.length;
const someSelected = selectedCount > 0 && !allSelected;
const getNextSortDirection = (column) => {
const activeSort = grid.sortBy.find((entry) => entry.field === column.field);
if (!activeSort) {
return 'ascending';
}
if (activeSort.direction === 'asc') {
return 'descending';
}
return 'none';
};
return (
{grid.selectable || grid.nested ? (
{grid.selectable ? (
grid.toggleSelectAll?.()}
borderColor="$lineStrong"
backgroundColor="$bgPanel"
focusStyle={{ borderColor: '$accent' }}
>
{(() => {
const Check = getIcon('check');
return Check ? : null;
})()}
) : null}
) : null}
{activeColumns.map((column) => {
const activeSort = grid.sortBy.find((entry) => entry.field === column.field);
const isActive = Boolean(activeSort);
const direction = activeSort?.direction;
const sortButtonLabel = `Sort ${column.label} ${getNextSortDirection(column)}`;
const ChevronIcon = isActive
? (direction === 'asc' ? CaretUp : CaretDown)
: CaretDown;
const iconColor = isActive ? '$textSecondary' : '$textMuted';
return (
{column.label}
{column.sortable ? (
);
})}
);
}
export function TableBodyView({ visible = true }) {
const grid = useGridView();
const bodyRef = useViewportTracking(visible);
if (visible === false) {
return null;
}
const activeColumns = grid.visibleColumns?.length ? grid.visibleColumns : grid.resolvedColumns;
if (grid.error) {
return (
{grid.error}
);
}
return (
{grid.rows.map((row, index) => (
{grid.selectable || grid.nested ? : null}
{activeColumns.map((column) => {
const Renderer = column.renderer || DefaultGridCellRenderer;
const cellValue = resolveCellValue(row, column);
return (
);
})}
))}
{!grid.rows.length && !grid.isLoading ? (
No records available
There's nothing to show here yet.
) : null}
{grid.isLoading && !grid.rows.length ? (
{[0, 1, 2].map((i) => (
))}
) : null}
);
}
export function TableFooter({ visible = true }) {
const grid = useGridView();
if (visible === false) {
return null;
}
return (
{grid.total} records
grid.setPage(1)}
onPreviousPage={() => grid.setPage(grid.currentPage - 1)}
onNextPage={() => grid.setPage(grid.currentPage + 1)}
onLastPage={() => grid.setPage(grid.pageCount)}
firstDisabled={grid.currentPage <= 1}
previousDisabled={grid.currentPage <= 1}
nextDisabled={grid.currentPage >= grid.pageCount}
lastDisabled={grid.currentPage >= grid.pageCount}
/>
);
}