React Example: Kitchen Sink Material Ui

'use client'

import * as React from 'react'
import * as ReactDOM from 'react-dom/client'
import { useDebouncedCallback } from '@tanstack/react-pacer/debouncer'
import {
  DndContext,
  PointerSensor,
  closestCenter,
  useSensor,
  useSensors,
} from '@dnd-kit/core'
import {
  SortableContext,
  arrayMove,
  useSortable,
  verticalListSortingStrategy,
} from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities'
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'
import CheckIcon from '@mui/icons-material/Check'
import CodeIcon from '@mui/icons-material/Code'
import CreditCardIcon from '@mui/icons-material/CreditCard'
import DarkModeIcon from '@mui/icons-material/DarkMode'
import DeleteIcon from '@mui/icons-material/Delete'
import DragIndicatorIcon from '@mui/icons-material/DragIndicator'
import ExpandLessIcon from '@mui/icons-material/ExpandLess'
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
import FilterListIcon from '@mui/icons-material/FilterList'
import GroupIcon from '@mui/icons-material/Group'
import LightModeIcon from '@mui/icons-material/LightMode'
import MoreVertIcon from '@mui/icons-material/MoreVert'
import PushPinIcon from '@mui/icons-material/PushPin'
import SearchIcon from '@mui/icons-material/Search'
import SettingsIcon from '@mui/icons-material/Settings'
import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'
import SortIcon from '@mui/icons-material/Sort'
import SystemUpdateAltIcon from '@mui/icons-material/SystemUpdateAlt'
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'
import {
  Autocomplete,
  Box,
  Button,
  Checkbox,
  Chip,
  Container,
  CssBaseline,
  Divider,
  FormControl,
  IconButton,
  InputAdornment,
  InputLabel,
  LinearProgress,
  List,
  ListItem,
  ListItemButton,
  ListItemIcon,
  ListItemText,
  Menu,
  MenuItem,
  Table as MuiTable,
  Paper,
  Popover,
  Select,
  Stack,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TablePagination,
  TableRow,
  TableSortLabel,
  TextField,
  ThemeProvider,
  Toolbar,
  Tooltip,
  Typography,
  createTheme,
  useMediaQuery,
} from '@mui/material'
import {
  aggregationFns,
  columnFacetingFeature,
  columnFilteringFeature,
  columnGroupingFeature,
  columnOrderingFeature,
  columnPinningFeature,
  columnResizingFeature,
  columnSizingFeature,
  columnVisibilityFeature,
  createColumnHelper,
  createExpandedRowModel,
  createFacetedRowModel,
  createFacetedUniqueValues,
  createFilteredRowModel,
  createGroupedRowModel,
  createPaginatedRowModel,
  createSortedRowModel,
  filterFns,
  globalFilteringFeature,
  metaHelper,
  rowExpandingFeature,
  rowPaginationFeature,
  rowSelectionFeature,
  rowSortingFeature,
  sortFns,
  tableFeatures,
  useTable,
} from '@tanstack/react-table'
import { useTanStackTableDevtools } from '@tanstack/react-table-devtools'
import type { Person } from '@/lib/make-data'
import type { DragEndEvent } from '@dnd-kit/core'
import type {
  Column,
  ColumnPinningState,
  ColumnSizingState,
  ExpandedState,
  GroupingState,
  Header,
  ReactTable,
  SortingState,
} from '@tanstack/react-table'
import type { ExtendedColumnFilter } from '@/types'

import { dynamicFilterFn, getFilterOperators } from '@/lib/data-table'
import { rankItem } from '@tanstack/match-sorter-utils'
import { departments, makeData, statuses } from '@/lib/make-data'
import './styles/globals.css'

interface MyColumnMeta {
  label?: string
  variant?: 'text' | 'number' | 'date' | 'boolean' | 'select' | 'multi-select'
  options?: Array<{ label: string; value: string; count?: number }>
}

// Local fuzzy filter implementation for the filterFns registry slot.
// Defined here to avoid a circular type dependency with data-table.ts.
const fuzzyFilterFn = (
  row: { getValue: (id: string) => unknown },
  columnId: string,
  value: unknown,
  addMeta?: (meta: object) => void,
) => {
  const itemRank = rankItem(row.getValue(columnId), value as string)
  addMeta?.({ itemRank })
  return itemRank.passed
}

export const features = tableFeatures({
  rowSortingFeature,
  rowPaginationFeature,
  rowSelectionFeature,
  rowExpandingFeature,
  columnFilteringFeature,
  columnFacetingFeature,
  columnOrderingFeature,
  columnVisibilityFeature,
  columnSizingFeature,
  columnResizingFeature,
  columnPinningFeature,
  columnGroupingFeature,
  globalFilteringFeature,
  columnMeta: metaHelper<MyColumnMeta>(),
  filteredRowModel: createFilteredRowModel(),
  facetedRowModel: createFacetedRowModel(),
  facetedUniqueValues: createFacetedUniqueValues(),
  paginatedRowModel: createPaginatedRowModel(),
  sortedRowModel: createSortedRowModel(),
  groupedRowModel: createGroupedRowModel(),
  expandedRowModel: createExpandedRowModel(),
  filterFns: { ...filterFns, fuzzy: fuzzyFilterFn },
  sortFns,
  aggregationFns,
})

const columnHelper = createColumnHelper<typeof features, Person>()
type AppTable = ReactTable<typeof features, Person>
type AppColumn = Column<typeof features, Person, any>

function SortableFrame({
  id,
  children,
}: {
  id: string
  children: React.ReactNode
}) {
  const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition,
    isDragging,
  } = useSortable({ id })

  return (
    <Box
      ref={setNodeRef}
      {...attributes}
      {...listeners}
      sx={{
        opacity: isDragging ? 0.6 : 1,
        transform: CSS.Transform.toString(transform),
        transition,
        cursor: 'grab',
        '&:active': {
          cursor: 'grabbing',
        },
      }}
    >
      {children}
    </Box>
  )
}

function toSentenceCase(value: string) {
  return value
    .replace(/[-_]/g, ' ')
    .replace(/\w\S*/g, (word) => word[0].toUpperCase() + word.slice(1))
}

function formatDate(value: string) {
  return new Intl.DateTimeFormat('en-US', {
    month: 'short',
    day: 'numeric',
    year: 'numeric',
  }).format(new Date(value))
}

function toDateInputValue(value: unknown) {
  if (!value) return ''
  const date = new Date(String(value))
  return Number.isNaN(date.getTime()) ? '' : date.toISOString().slice(0, 10)
}

function getAriaSort(sortDirection: false | 'asc' | 'desc') {
  if (sortDirection === 'asc') return 'ascending'
  if (sortDirection === 'desc') return 'descending'
  return 'none'
}

const SortingContext = React.createContext<SortingState>([])

function getSortDirection(sorting: SortingState, columnId: string) {
  const sort = sorting.find((sort) => sort.id === columnId)
  return sort ? (sort.desc ? 'desc' : 'asc') : undefined
}

function getCommonPinningStyles(
  column: AppColumn,
  isSelected = false,
): React.CSSProperties {
  const isPinned = column.getIsPinned()
  const isLastLeftPinnedColumn =
    isPinned === 'left' && column.getIsLastColumn('left')
  const isFirstRightPinnedColumn =
    isPinned === 'right' && column.getIsFirstColumn('right')

  return {
    boxShadow: isLastLeftPinnedColumn
      ? '-4px 0 4px -4px rgba(0, 0, 0, 0.3) inset'
      : isFirstRightPinnedColumn
        ? '4px 0 4px -4px rgba(0, 0, 0, 0.3) inset'
        : undefined,
    left: isPinned === 'left' ? `${column.getStart('left')}px` : undefined,
    right: isPinned === 'right' ? `${column.getAfter('right')}px` : undefined,
    position: isPinned ? 'sticky' : 'relative',
    background: isSelected
      ? 'rgba(var(--mui-palette-primary-mainChannel) / var(--mui-palette-action-selectedOpacity))'
      : isPinned
        ? 'var(--mui-palette-background-paper)'
        : undefined,
    zIndex: isPinned ? 2 : 0,
  }
}

function DepartmentIcon({ department }: { department: Person['department'] }) {
  const icons: Record<Person['department'], React.ReactElement> = {
    engineering: <CodeIcon fontSize="inherit" />,
    marketing: <SystemUpdateAltIcon fontSize="inherit" />,
    sales: <ShoppingCartIcon fontSize="inherit" />,
    hr: <GroupIcon fontSize="inherit" />,
    finance: <CreditCardIcon fontSize="inherit" />,
  }

  return icons[department]
}

function DepartmentChip({ department }: { department: Person['department'] }) {
  return (
    <Box
      component="span"
      sx={{
        display: 'inline-flex',
        maxWidth: '100%',
        height: 24,
        minWidth: 0,
        alignItems: 'center',
        gap: 0.75,
        px: 1,
        borderRadius: 13,
        border: 1,
        borderColor: 'divider',
        fontSize: '0.8125rem',
      }}
    >
      <Box
        component="span"
        sx={{
          display: 'inline-flex',
          width: 16,
          height: 16,
          flex: '0 0 16px',
          alignItems: 'center',
          justifyContent: 'center',
          overflow: 'hidden',
          fontSize: 16,
          lineHeight: 1,
        }}
      >
        <DepartmentIcon department={department} />
      </Box>
      <Box
        component="span"
        sx={{
          minWidth: 0,
          overflow: 'hidden',
          textOverflow: 'ellipsis',
          whiteSpace: 'nowrap',
        }}
      >
        {toSentenceCase(department)}
      </Box>
    </Box>
  )
}

function EllipsisText({ children }: { children: React.ReactNode }) {
  return (
    <Box
      component="span"
      sx={{
        display: 'block',
        minWidth: 0,
        overflow: 'hidden',
        textOverflow: 'ellipsis',
        whiteSpace: 'nowrap',
      }}
    >
      {children}
    </Box>
  )
}

function StatusChip({ status }: { status: Person['status'] }) {
  const color: Record<Person['status'], 'success' | 'error' | 'warning'> = {
    active: 'success',
    inactive: 'error',
    pending: 'warning',
  }

  return (
    <Chip
      icon={<CheckIcon fontSize="small" />}
      label={toSentenceCase(status)}
      color={color[status]}
      variant="outlined"
      size="small"
    />
  )
}

function RowActions({ person }: { person: Person }) {
  const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null)
  const open = Boolean(anchorEl)

  return (
    <>
      <IconButton
        size="small"
        aria-label="Open row actions"
        aria-controls={open ? `row-actions-${person.id}` : undefined}
        aria-haspopup="menu"
        onClick={(event) => setAnchorEl(event.currentTarget)}
      >
        <MoreVertIcon fontSize="small" />
      </IconButton>
      <Menu
        id={`row-actions-${person.id}`}
        anchorEl={anchorEl}
        open={open}
        onClose={() => setAnchorEl(null)}
      >
        <MenuItem
          onClick={() => {
            void navigator.clipboard.writeText(person.id)
            setAnchorEl(null)
          }}
        >
          <ListItemText>Copy ID</ListItemText>
        </MenuItem>
        <Divider />
        <MenuItem onClick={() => setAnchorEl(null)}>View details</MenuItem>
        <MenuItem onClick={() => setAnchorEl(null)}>View profile</MenuItem>
      </Menu>
    </>
  )
}

function ColumnHeaderMenu({
  column,
  title,
}: {
  column: AppColumn
  title: string
}) {
  const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null)
  const canSort = column.getCanSort()
  const canHide = column.getCanHide()
  const canPin = column.getCanPin()
  const canGroup = column.getCanGroup()
  const sorting = React.useContext(SortingContext)
  const direction = canSort ? getSortDirection(sorting, column.id) : undefined
  const isSorted = !!direction
  const pinned = canPin ? column.getIsPinned() : false
  const grouped = canGroup ? column.getIsGrouped() : false

  if (!canSort && !canHide && !canPin && !canGroup) {
    return <Typography variant="subtitle2">{title}</Typography>
  }

  return (
    <Stack direction="row" spacing={0.5} sx={{ alignItems: 'center' }}>
      {canSort ? (
        <TableSortLabel
          active={isSorted}
          direction={direction}
          IconComponent={ArrowDownwardIcon}
          onClick={column.getToggleSortingHandler()}
          sx={{
            '& .MuiTableSortLabel-icon': {
              opacity: isSorted ? 1 : 0,
              transition: 'opacity 120ms ease',
            },
            '&:hover .MuiTableSortLabel-icon, &:focus-visible .MuiTableSortLabel-icon':
              {
                opacity: 1,
              },
          }}
        >
          {title}
        </TableSortLabel>
      ) : (
        <Typography variant="subtitle2">{title}</Typography>
      )}
      <IconButton
        size="small"
        aria-label={`Open ${title} column menu`}
        onClick={(event) => setAnchorEl(event.currentTarget)}
      >
        <ArrowDropDownIcon fontSize="small" />
      </IconButton>
      <Menu
        anchorEl={anchorEl}
        open={Boolean(anchorEl)}
        onClose={() => setAnchorEl(null)}
      >
        {canSort && (
          <Box>
            <MenuItem onClick={() => column.toggleSorting(false)}>
              <ListItemIcon>
                <ArrowUpwardIcon fontSize="small" />
              </ListItemIcon>
              <ListItemText>Asc</ListItemText>
            </MenuItem>
            <MenuItem onClick={() => column.toggleSorting(true)}>
              <ListItemIcon>
                <ArrowDownwardIcon fontSize="small" />
              </ListItemIcon>
              <ListItemText>Desc</ListItemText>
            </MenuItem>
          </Box>
        )}
        {canGroup && (
          <MenuItem onClick={column.getToggleGroupingHandler()}>
            <ListItemIcon>
              <GroupIcon fontSize="small" />
            </ListItemIcon>
            <ListItemText>{grouped ? 'Ungroup' : 'Group by'}</ListItemText>
          </MenuItem>
        )}
        {canPin && (
          <Box>
            <Divider />
            <MenuItem
              disabled={pinned === 'left'}
              onClick={() => column.pin('left')}
            >
              <ListItemIcon>
                <PushPinIcon fontSize="small" />
              </ListItemIcon>
              <ListItemText>Pin left</ListItemText>
            </MenuItem>
            <MenuItem
              disabled={pinned === 'right'}
              onClick={() => column.pin('right')}
            >
              <ListItemIcon>
                <PushPinIcon fontSize="small" sx={{ rotate: '180deg' }} />
              </ListItemIcon>
              <ListItemText>Pin right</ListItemText>
            </MenuItem>
            {pinned ? (
              <MenuItem onClick={() => column.pin(false)}>
                <ListItemIcon>
                  <PushPinIcon fontSize="small" color="disabled" />
                </ListItemIcon>
                <ListItemText>Unpin</ListItemText>
              </MenuItem>
            ) : null}
          </Box>
        )}
        {canHide && (
          <Box>
            <Divider />
            <MenuItem onClick={() => column.toggleVisibility(false)}>
              <ListItemIcon>
                <VisibilityOffIcon fontSize="small" />
              </ListItemIcon>
              <ListItemText>Hide</ListItemText>
            </MenuItem>
          </Box>
        )}
      </Menu>
    </Stack>
  )
}

function ViewOptionsPopover({
  table,
  columnOrder,
  onColumnOrderChange,
}: {
  table: AppTable
  columnOrder: Array<string>
  onColumnOrderChange: React.Dispatch<React.SetStateAction<Array<string>>>
}) {
  const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null)
  const [query, setQuery] = React.useState('')
  const sensors = useSensors(
    useSensor(PointerSensor, { activationConstraint: { distance: 6 } }),
  )
  const columns = table
    .getAllColumns()
    .filter((column) => typeof column.accessorFn !== 'undefined')
    .sort((a, b) => columnOrder.indexOf(a.id) - columnOrder.indexOf(b.id))
    .filter((column) =>
      (column.columnDef.meta?.label ?? column.id)
        .toLowerCase()
        .includes(query.toLowerCase()),
    )

  const onDragEnd = (event: DragEndEvent) => {
    const { active, over } = event
    if (!over || active.id === over.id) return

    onColumnOrderChange((current) => {
      const oldIndex = current.indexOf(String(active.id))
      const newIndex = current.indexOf(String(over.id))
      return oldIndex >= 0 && newIndex >= 0
        ? arrayMove(current, oldIndex, newIndex)
        : current
    })
  }

  return (
    <>
      <Button
        variant="outlined"
        size="small"
        startIcon={<SettingsIcon />}
        onClick={(event) => setAnchorEl(event.currentTarget)}
      >
        View
      </Button>
      <Popover
        open={Boolean(anchorEl)}
        anchorEl={anchorEl}
        onClose={() => setAnchorEl(null)}
        anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
        transformOrigin={{ vertical: 'top', horizontal: 'right' }}
      >
        <Stack spacing={1.5} sx={{ width: 300, p: 2 }}>
          <TextField
            size="small"
            label="Search columns"
            value={query}
            onChange={(event) => setQuery(event.target.value)}
          />
          <DndContext
            sensors={sensors}
            collisionDetection={closestCenter}
            onDragEnd={onDragEnd}
          >
            <SortableContext
              items={columns.map((column) => column.id)}
              strategy={verticalListSortingStrategy}
            >
              <List dense disablePadding>
                {columns.map((column) => (
                  <SortableFrame key={column.id} id={column.id}>
                    <ListItem
                      disablePadding
                      secondaryAction={
                        <DragIndicatorIcon color="disabled" fontSize="small" />
                      }
                    >
                      <ListItemButton
                        dense
                        onClick={() =>
                          column.toggleVisibility(!column.getIsVisible())
                        }
                      >
                        <ListItemIcon>
                          <Checkbox
                            edge="start"
                            size="small"
                            checked={column.getIsVisible()}
                            tabIndex={-1}
                          />
                        </ListItemIcon>
                        <ListItemText
                          primary={column.columnDef.meta?.label ?? column.id}
                        />
                      </ListItemButton>
                    </ListItem>
                  </SortableFrame>
                ))}
              </List>
            </SortableContext>
          </DndContext>
        </Stack>
      </Popover>
    </>
  )
}

function SortListPopover({
  table,
  sorting,
  onSortingChange,
}: {
  table: AppTable
  sorting: SortingState
  onSortingChange: React.Dispatch<React.SetStateAction<SortingState>>
}) {
  const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null)
  const sensors = useSensors(
    useSensor(PointerSensor, { activationConstraint: { distance: 6 } }),
  )
  const sortableColumns = table
    .getAllColumns()
    .filter((column) => column.getCanSort())

  const updateSort = (index: number, patch: Partial<SortingState[number]>) => {
    onSortingChange((current) =>
      current.map((sort, sortIndex) =>
        sortIndex === index ? { ...sort, ...patch } : sort,
      ),
    )
  }

  const addSort = () => {
    const nextColumn = sortableColumns.find(
      (column) => !sorting.some((sort) => sort.id === column.id),
    )
    if (nextColumn)
      onSortingChange((current) => [
        ...current,
        { id: nextColumn.id, desc: false },
      ])
  }

  const onDragEnd = (event: DragEndEvent) => {
    const { active, over } = event
    if (!over || active.id === over.id) return

    onSortingChange((current) => {
      const oldIndex = current.findIndex((sort) => sort.id === active.id)
      const newIndex = current.findIndex((sort) => sort.id === over.id)
      return oldIndex >= 0 && newIndex >= 0
        ? arrayMove(current, oldIndex, newIndex)
        : current
    })
  }

  return (
    <>
      <Button
        variant="outlined"
        size="small"
        startIcon={<SortIcon />}
        endIcon={
          sorting.length ? <Chip size="small" label={sorting.length} /> : null
        }
        onClick={(event) => setAnchorEl(event.currentTarget)}
      >
        Sort
      </Button>
      <Popover
        open={Boolean(anchorEl)}
        anchorEl={anchorEl}
        onClose={() => setAnchorEl(null)}
        anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
      >
        <Stack spacing={2} sx={{ width: 480, p: 2 }}>
          <Typography variant="subtitle1">
            {sorting.length ? 'Sort by' : 'No sorting applied'}
          </Typography>
          <DndContext
            sensors={sensors}
            collisionDetection={closestCenter}
            onDragEnd={onDragEnd}
          >
            <SortableContext
              items={sorting.map((sort) => sort.id)}
              strategy={verticalListSortingStrategy}
            >
              <Stack spacing={1}>
                {sorting.map((sort, index) => (
                  <SortableFrame key={sort.id} id={sort.id}>
                    <Stack
                      direction="row"
                      spacing={1}
                      sx={{ alignItems: 'center' }}
                    >
                      <DragIndicatorIcon color="disabled" />
                      <Autocomplete
                        size="small"
                        fullWidth
                        options={sortableColumns}
                        value={
                          sortableColumns.find(
                            (column) => column.id === sort.id,
                          ) ?? null
                        }
                        getOptionLabel={(column) =>
                          column.columnDef.meta?.label ?? column.id
                        }
                        onChange={(_, column) => {
                          if (column) updateSort(index, { id: column.id })
                        }}
                        renderInput={(params) => (
                          <TextField {...params} label="Column" />
                        )}
                      />
                      <FormControl size="small" sx={{ minWidth: 110 }}>
                        <InputLabel>Direction</InputLabel>
                        <Select
                          label="Direction"
                          value={sort.desc ? 'desc' : 'asc'}
                          onChange={(event) =>
                            updateSort(index, {
                              desc: event.target.value === 'desc',
                            })
                          }
                        >
                          <MenuItem value="asc">Asc</MenuItem>
                          <MenuItem value="desc">Desc</MenuItem>
                        </Select>
                      </FormControl>
                      <IconButton
                        size="small"
                        aria-label="Remove sort"
                        onClick={() =>
                          onSortingChange((current) =>
                            current.filter(
                              (_, sortIndex) => sortIndex !== index,
                            ),
                          )
                        }
                      >
                        <DeleteIcon fontSize="small" />
                      </IconButton>
                    </Stack>
                  </SortableFrame>
                ))}
              </Stack>
            </SortableContext>
          </DndContext>
          <Stack direction="row" spacing={1}>
            <Button
              variant="contained"
              size="small"
              onClick={addSort}
              disabled={sorting.length >= sortableColumns.length}
            >
              Add sort
            </Button>
            <Button size="small" onClick={() => table.resetSorting()}>
              Reset
            </Button>
          </Stack>
        </Stack>
      </Popover>
    </>
  )
}

function FilterValueInput({
  column,
  filter,
  onFilterUpdate,
}: {
  column: AppColumn
  filter: ExtendedColumnFilter
  onFilterUpdate: (
    filterId: string,
    patch: Partial<ExtendedColumnFilter>,
  ) => void
}) {
  if (!filter.filterId) return null
  const variant = column.columnDef.meta?.variant ?? 'text'
  const operator = filter.operator ?? 'includesString'
  const disabled = operator === 'isEmpty' || operator === 'isNotEmpty'

  if (disabled) {
    return <Typography color="text.secondary">No value required</Typography>
  }

  if (variant === 'select' || variant === 'multi-select') {
    const options = column.columnDef.meta?.options ?? []
    const multiple = variant === 'multi-select'
    const value = multiple
      ? options.filter(
          (option) =>
            Array.isArray(filter.value) && filter.value.includes(option.value),
        )
      : (options.find((option) => option.value === filter.value) ?? null)

    return (
      <Autocomplete
        size="small"
        multiple={multiple}
        options={options}
        value={value}
        getOptionLabel={(option) => option.label}
        onChange={(_, nextValue) => {
          onFilterUpdate(filter.filterId!, {
            value: Array.isArray(nextValue)
              ? nextValue.map((option) => option.value)
              : nextValue?.value,
          })
        }}
        renderInput={(params) => <TextField {...params} label="Value" />}
      />
    )
  }

  if (variant === 'date') {
    if (operator === 'inRange') {
      const value = Array.isArray(filter.value) ? filter.value : []
      return (
        <Stack direction="row" spacing={1}>
          <TextField
            size="small"
            label="From"
            type="date"
            value={toDateInputValue(value[0])}
            onChange={(event) =>
              onFilterUpdate(filter.filterId!, {
                value: [
                  event.target.value
                    ? new Date(event.target.value).toISOString()
                    : undefined,
                  value[1],
                ],
              })
            }
            slotProps={{ inputLabel: { shrink: true } }}
          />
          <TextField
            size="small"
            label="To"
            type="date"
            value={toDateInputValue(value[1])}
            onChange={(event) =>
              onFilterUpdate(filter.filterId!, {
                value: [
                  value[0],
                  event.target.value
                    ? new Date(event.target.value).toISOString()
                    : undefined,
                ],
              })
            }
            slotProps={{ inputLabel: { shrink: true } }}
          />
        </Stack>
      )
    }

    return (
      <TextField
        size="small"
        label="Value"
        type="date"
        value={toDateInputValue(filter.value)}
        onChange={(event) =>
          onFilterUpdate(filter.filterId!, {
            value: event.target.value
              ? new Date(event.target.value).toISOString()
              : undefined,
          })
        }
        slotProps={{ inputLabel: { shrink: true } }}
      />
    )
  }

  if (variant === 'number') {
    return (
      <TextField
        size="small"
        label="Value"
        type="number"
        value={filter.value ?? ''}
        onChange={(event) =>
          onFilterUpdate(filter.filterId!, {
            value: event.target.value === '' ? '' : Number(event.target.value),
          })
        }
      />
    )
  }

  return (
    <TextField
      size="small"
      label="Value"
      value={filter.value ?? ''}
      onChange={(event) =>
        onFilterUpdate(filter.filterId!, { value: event.target.value })
      }
    />
  )
}

function FilterListPopover({
  table,
  columnFilters,
  onColumnFiltersChange,
}: {
  table: AppTable
  columnFilters: Array<ExtendedColumnFilter>
  onColumnFiltersChange: React.Dispatch<
    React.SetStateAction<Array<ExtendedColumnFilter>>
  >
}) {
  const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null)
  const filterableColumns = table
    .getAllColumns()
    .filter((column) => column.getCanFilter())

  const updateFilter = (
    filterId: string,
    patch: Partial<ExtendedColumnFilter>,
  ) => {
    onColumnFiltersChange((current) =>
      current.map((filter) =>
        filter.filterId === filterId ? { ...filter, ...patch } : filter,
      ),
    )
  }

  const addFilter = () => {
    if (filterableColumns.length === 0) return
    const [column] = filterableColumns
    onColumnFiltersChange((current) => [
      ...current,
      {
        id: column.id,
        filterId: crypto.randomUUID(),
        value: '',
        operator: 'includesString',
        joinOperator: current[0]?.joinOperator ?? 'and',
      },
    ])
  }

  return (
    <>
      <Button
        variant="outlined"
        size="small"
        startIcon={<FilterListIcon />}
        endIcon={
          columnFilters.length ? (
            <Chip size="small" label={columnFilters.length} />
          ) : null
        }
        onClick={(event) => setAnchorEl(event.currentTarget)}
      >
        Filter
      </Button>
      <Popover
        open={Boolean(anchorEl)}
        anchorEl={anchorEl}
        onClose={() => setAnchorEl(null)}
        anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
      >
        <Stack spacing={2} sx={{ width: 720, p: 2 }}>
          <Typography variant="subtitle1">Filters</Typography>
          {columnFilters.map((filter, index) => {
            const column = table.getColumn(filter.id)
            if (!column || !filter.filterId) return null
            const variant = column.columnDef.meta?.variant ?? 'text'
            const operators = getFilterOperators(variant)
            return (
              <Stack
                key={filter.filterId}
                direction="row"
                spacing={1}
                sx={{ alignItems: 'center' }}
              >
                {index === 0 ? (
                  <Typography sx={{ width: 70 }}>Where</Typography>
                ) : index === 1 ? (
                  <FormControl size="small" sx={{ width: 90 }}>
                    <Select
                      value={filter.joinOperator ?? 'and'}
                      onChange={(event) => {
                        const joinOperator = event.target.value
                        onColumnFiltersChange((current) =>
                          current.map((item) => ({ ...item, joinOperator })),
                        )
                      }}
                    >
                      <MenuItem value="and">and</MenuItem>
                      <MenuItem value="or">or</MenuItem>
                    </Select>
                  </FormControl>
                ) : (
                  <Typography sx={{ width: 70 }}>
                    {filter.joinOperator ?? 'and'}
                  </Typography>
                )}
                <Autocomplete
                  size="small"
                  sx={{ width: 190 }}
                  options={filterableColumns}
                  value={column}
                  getOptionLabel={(option) =>
                    option.columnDef.meta?.label ?? option.id
                  }
                  onChange={(_, nextColumn) => {
                    if (nextColumn) {
                      updateFilter(filter.filterId!, {
                        id: nextColumn.id,
                        operator: getFilterOperators(
                          nextColumn.columnDef.meta?.variant ?? 'text',
                        )[0].value,
                        value: '',
                      })
                    }
                  }}
                  renderInput={(params) => (
                    <TextField {...params} label="Field" />
                  )}
                />
                <FormControl size="small" sx={{ width: 180 }}>
                  <InputLabel>Operator</InputLabel>
                  <Select
                    label="Operator"
                    value={filter.operator ?? operators[0].value}
                    onChange={(event) =>
                      updateFilter(filter.filterId!, {
                        operator: event.target.value,
                        value: '',
                      })
                    }
                  >
                    {operators.map((operator) => (
                      <MenuItem key={operator.value} value={operator.value}>
                        {operator.label}
                      </MenuItem>
                    ))}
                  </Select>
                </FormControl>
                <Box sx={{ flex: 1 }}>
                  <FilterValueInput
                    column={column}
                    filter={filter}
                    onFilterUpdate={updateFilter}
                  />
                </Box>
                <IconButton
                  aria-label="Remove filter"
                  onClick={() =>
                    onColumnFiltersChange((current) =>
                      current.filter(
                        (item) => item.filterId !== filter.filterId,
                      ),
                    )
                  }
                >
                  <DeleteIcon fontSize="small" />
                </IconButton>
              </Stack>
            )
          })}
          <Stack direction="row" spacing={1}>
            <Button variant="contained" size="small" onClick={addFilter}>
              Add filter
            </Button>
            <Button size="small" onClick={() => onColumnFiltersChange([])}>
              Reset
            </Button>
          </Stack>
        </Stack>
      </Popover>
    </>
  )
}

function Pagination({ table }: { table: AppTable }) {
  return (
    <Stack
      direction={{ xs: 'column', sm: 'row' }}
      spacing={2}
      sx={{ p: 1, alignItems: 'center', justifyContent: 'space-between' }}
    >
      <Typography variant="body2" color="text.secondary">
        {table.getFilteredSelectedRowModel().rows.length.toLocaleString()} of{' '}
        {table.getFilteredRowModel().rows.length.toLocaleString()} row(s)
        selected.
      </Typography>
      <Stack direction="row" spacing={0.5} sx={{ alignItems: 'center' }}>
        <TablePagination
          component="div"
          count={table.getFilteredRowModel().rows.length}
          page={table.state.pagination.pageIndex}
          rowsPerPage={table.state.pagination.pageSize}
          rowsPerPageOptions={[10, 20, 30, 40, 50]}
          showFirstButton
          showLastButton
          onPageChange={(_, page) => table.setPageIndex(page)}
          onRowsPerPageChange={(event) => {
            table.setPageSize(Number(event.target.value))
            table.setPageIndex(0)
          }}
        />
      </Stack>
    </Stack>
  )
}

function ModeMenu({
  mode,
  setMode,
}: {
  mode: 'light' | 'dark' | 'system'
  setMode: React.Dispatch<React.SetStateAction<'light' | 'dark' | 'system'>>
}) {
  const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null)

  return (
    <>
      <Tooltip title="Theme">
        <IconButton onClick={(event) => setAnchorEl(event.currentTarget)}>
          {mode === 'dark' ? <DarkModeIcon /> : <LightModeIcon />}
        </IconButton>
      </Tooltip>
      <Menu
        anchorEl={anchorEl}
        open={Boolean(anchorEl)}
        onClose={() => setAnchorEl(null)}
      >
        {(['light', 'dark', 'system'] as const).map((themeMode) => (
          <MenuItem
            key={themeMode}
            selected={themeMode === mode}
            onClick={() => {
              setMode(themeMode)
              setAnchorEl(null)
            }}
          >
            {toSentenceCase(themeMode)}
          </MenuItem>
        ))}
      </Menu>
    </>
  )
}

function DebouncedTextField({
  value: initialValue,
  onChange,
  debounce = 300,
  ...props
}: {
  value: string | number
  onChange: (value: string | number) => void
  debounce?: number
} & Omit<React.ComponentProps<typeof TextField>, 'onChange'>) {
  const [value, setValue] = React.useState(initialValue)

  React.useEffect(() => {
    setValue(initialValue)
  }, [initialValue])

  const debouncedOnChange = useDebouncedCallback(onChange, { wait: debounce })

  return (
    <TextField
      {...props}
      value={value}
      onChange={(event) => {
        setValue(event.target.value)
        debouncedOnChange(event.target.value)
      }}
    />
  )
}

function App({
  mode,
  setMode,
}: {
  mode: 'light' | 'dark' | 'system'
  setMode: React.Dispatch<React.SetStateAction<'light' | 'dark' | 'system'>>
}) {
  const rerender = React.useReducer(() => ({}), {})[1]
  const [rowSelection, setRowSelection] = React.useState({})
  const [sorting, setSorting] = React.useState<SortingState>([])
  const [columnFilters, setColumnFilters] = React.useState<
    Array<ExtendedColumnFilter>
  >([])
  const [columnVisibility, setColumnVisibility] = React.useState({})
  const [columnSizing, setColumnSizing] = React.useState<ColumnSizingState>({})
  const [globalFilter, setGlobalFilter] = React.useState('')
  const [columnPinning, setColumnPinning] = React.useState<ColumnPinningState>({
    left: ['select'],
    right: ['actions'],
  })
  const [grouping, setGrouping] = React.useState<GroupingState>([])
  const [expanded, setExpanded] = React.useState<ExpandedState>({})
  const [data, setData] = React.useState(() => makeData(1_000))

  const columns = React.useMemo(
    () =>
      columnHelper.columns([
        columnHelper.display({
          id: 'select',
          header: ({ table }) => (
            <Checkbox
              checked={table.getIsAllPageRowsSelected()}
              indeterminate={
                !table.getIsAllPageRowsSelected() &&
                table.getIsSomePageRowsSelected()
              }
              onChange={(_, checked) =>
                table.toggleAllPageRowsSelected(checked)
              }
              slotProps={{ input: { 'aria-label': 'Select all' } }}
              size="small"
            />
          ),
          cell: ({ row }) => (
            <Checkbox
              checked={row.getIsSelected()}
              onChange={(_, checked) => row.toggleSelected(checked)}
              slotProps={{ input: { 'aria-label': 'Select row' } }}
              size="small"
            />
          ),
          maxSize: 48,
          enableSorting: false,
          enableHiding: false,
          enableResizing: false,
        }),
        columnHelper.accessor('firstName', {
          id: 'firstName',
          header: ({ column }) => (
            <ColumnHeaderMenu column={column} title="First Name" />
          ),
          cell: (info) => (
            <EllipsisText>{String(info.getValue())}</EllipsisText>
          ),
          meta: { label: 'First Name', variant: 'text' },
        }),
        columnHelper.accessor((row) => row.lastName, {
          id: 'lastName',
          header: ({ column }) => (
            <ColumnHeaderMenu column={column} title="Last Name" />
          ),
          cell: (info) => (
            <EllipsisText>{String(info.getValue())}</EllipsisText>
          ),
          meta: { label: 'Last Name', variant: 'text' },
        }),
        columnHelper.accessor('age', {
          id: 'age',
          header: ({ column }) => (
            <ColumnHeaderMenu column={column} title="Age" />
          ),
          cell: (info) => (
            <Typography variant="body2">{String(info.getValue())}</Typography>
          ),
          aggregationFn: 'mean',
          aggregatedCell: ({ getValue }) => (
            <Typography variant="body2" color="text.secondary">
              Avg: {Math.round(Number(getValue()) * 10) / 10}
            </Typography>
          ),
          meta: { label: 'Age', variant: 'number' },
        }),
        columnHelper.accessor('email', {
          id: 'email',
          header: ({ column }) => (
            <ColumnHeaderMenu column={column} title="Email" />
          ),
          cell: (info) => (
            <EllipsisText>{info.cell.getValue<string>()}</EllipsisText>
          ),
          meta: { label: 'Email', variant: 'text' },
        }),
        columnHelper.accessor('status', {
          id: 'status',
          header: ({ column }) => (
            <ColumnHeaderMenu column={column} title="Status" />
          ),
          cell: (info) => {
            const status = info.getValue<Person['status'] | undefined>()
            return status ? <StatusChip status={status} /> : null
          },
          aggregatedCell: () => null,
          meta: {
            label: 'Status',
            variant: 'select',
            options: statuses.map((status) => ({
              label: toSentenceCase(status),
              value: status,
            })),
          },
        }),
        columnHelper.accessor('department', {
          id: 'department',
          header: ({ column }) => (
            <ColumnHeaderMenu column={column} title="Department" />
          ),
          cell: (info) => {
            const department = info.getValue<Person['department'] | undefined>()
            return department ? (
              <DepartmentChip department={department} />
            ) : null
          },
          aggregatedCell: () => null,
          meta: {
            label: 'Department',
            variant: 'multi-select',
            options: departments.map((department) => ({
              label: toSentenceCase(department),
              value: department,
            })),
          },
        }),
        columnHelper.accessor('joinDate', {
          id: 'joinDate',
          header: ({ column }) => (
            <ColumnHeaderMenu column={column} title="Join Date" />
          ),
          cell: (info) => formatDate(info.getValue<string>()),
          aggregationFn: 'min',
          aggregatedCell: ({ getValue }) => {
            const earliest = getValue<string>()
            return (
              <Typography variant="body2" color="text.secondary">
                Earliest: {earliest ? formatDate(earliest) : '—'}
              </Typography>
            )
          },
          meta: { label: 'Join Date', variant: 'date' },
        }),
        columnHelper.display({
          id: 'actions',
          enableHiding: false,
          cell: ({ row }) => <RowActions person={row.original} />,
          maxSize: 44,
          enableResizing: false,
        }),
      ]),
    [],
  )

  const [columnOrder, setColumnOrder] = React.useState<Array<string>>(() =>
    columns.map((column) => column.id ?? ''),
  )

  const table = useTable(
    {
      key: 'kitchen-sink-material-ui', // needed for devtools
      features,
      columns,
      data,
      defaultColumn: {
        minSize: 60,
        maxSize: 800,
        filterFn: dynamicFilterFn,
      },
      globalFilterFn: 'fuzzy',
      state: {
        rowSelection,
        sorting,
        columnVisibility,
        columnOrder,
        columnSizing,
        columnFilters,
        globalFilter,
        columnPinning,
        grouping,
        expanded,
      },
      onSortingChange: setSorting,
      onColumnVisibilityChange: setColumnVisibility,
      onColumnOrderChange: setColumnOrder,
      onColumnSizingChange: setColumnSizing,
      onColumnFiltersChange: setColumnFilters,
      onGlobalFilterChange: setGlobalFilter,
      onColumnPinningChange: setColumnPinning,
      onGroupingChange: setGrouping,
      onExpandedChange: setExpanded,
      getRowId: (row) => row.id,
      enableRowSelection: true,
      onRowSelectionChange: setRowSelection,
      columnResizeMode: 'onChange',
      debugTable: true,
    },
    (state) => state, // default selector
  )

  useTanStackTableDevtools(table)

  const columnSizeVars = React.useMemo(() => {
    const headers = table.getFlatHeaders()
    const colSizes: Record<string, number> = {}
    for (const header of headers) {
      colSizes[`--header-${header.id}-size`] = header.getSize()
      colSizes[`--col-${header.column.id}-size`] = header.column.getSize()
    }
    return colSizes
  }, [table.state.columnSizing])

  const refreshData = () => setData(makeData(1_000))
  const stressTest = () => setData(makeData(200_000))

  return (
    <SortingContext.Provider value={sorting}>
      <Container maxWidth={false} sx={{ py: 3 }}>
        <Stack spacing={2}>
          <Paper variant="outlined">
            <Toolbar
              sx={{ gap: 1, justifyContent: 'flex-end', flexWrap: 'wrap' }}
            >
              <ModeMenu mode={mode} setMode={setMode} />
              <Button variant="outlined" size="small" onClick={refreshData}>
                Regenerate Data
              </Button>
              <Button variant="outlined" size="small" onClick={stressTest}>
                Stress Test (200k rows)
              </Button>
              <Button
                variant="outlined"
                size="small"
                onClick={() => rerender()}
              >
                Force Rerender
              </Button>
              <Button
                variant="outlined"
                size="small"
                onClick={() =>
                  console.info(
                    'table.getSelectedRowModel().flatRows',
                    table.getSelectedRowModel().flatRows,
                  )
                }
              >
                Log Selected Rows
              </Button>
            </Toolbar>
          </Paper>

          <Stack
            direction={{ xs: 'column', md: 'row' }}
            spacing={1}
            sx={{ alignItems: { md: 'center' } }}
          >
            <DebouncedTextField
              value={globalFilter}
              onChange={(value) => setGlobalFilter(String(value))}
              placeholder="Search all columns..."
              size="small"
              sx={{ width: { xs: '100%', md: 360 } }}
              slotProps={{
                input: {
                  startAdornment: (
                    <InputAdornment position="start">
                      <SearchIcon fontSize="small" />
                    </InputAdornment>
                  ),
                },
              }}
            />
            <FilterListPopover
              table={table}
              columnFilters={columnFilters}
              onColumnFiltersChange={setColumnFilters}
            />
            <SortListPopover
              table={table}
              sorting={sorting}
              onSortingChange={setSorting}
            />
            <ViewOptionsPopover
              table={table}
              columnOrder={columnOrder}
              onColumnOrderChange={setColumnOrder}
            />
          </Stack>

          <Paper variant="outlined">
            <TableContainer sx={{ maxHeight: 680 }}>
              <MuiTable
                stickyHeader
                size="small"
                sx={{
                  width: '100%',
                  tableLayout: 'fixed',
                  ...columnSizeVars,
                }}
              >
                <TableHead>
                  {table.getHeaderGroups().map((headerGroup) => (
                    <TableRow key={headerGroup.id}>
                      {headerGroup.headers
                        .filter((header) => header.column.getIsVisible())
                        .map((header) => (
                          <ResizableHeaderCell
                            key={header.id}
                            header={header}
                            table={table}
                          />
                        ))}
                    </TableRow>
                  ))}
                </TableHead>
                <TableBody>
                  {table.getRowModel().rows.map((row) => (
                    <TableRow
                      key={row.id}
                      hover
                      selected={row.getIsSelected()}
                      aria-selected={row.getIsSelected()}
                    >
                      {row.getVisibleCells().map((cell) => (
                        <TableCell
                          key={cell.id}
                          align={
                            cell.column.id === 'select' ? 'center' : 'left'
                          }
                          sx={{
                            width: `calc(var(--col-${cell.column.id}-size) * 1px)`,
                            overflow: 'hidden',
                            borderRight:
                              cell.column.id === 'actions' ? undefined : 1,
                            borderColor: 'divider',
                            ...getCommonPinningStyles(
                              cell.column,
                              row.getIsSelected(),
                            ),
                          }}
                        >
                          {cell.getIsGrouped() ? (
                            <Button
                              size="small"
                              variant="text"
                              startIcon={
                                row.getIsExpanded() ? (
                                  <ExpandLessIcon />
                                ) : (
                                  <ExpandMoreIcon />
                                )
                              }
                              onClick={row.getToggleExpandedHandler()}
                              disabled={!row.getCanExpand()}
                              sx={{ pl: row.depth * 2 + 1 }}
                            >
                              <table.FlexRender cell={cell} />
                              <Typography
                                component="span"
                                color="text.secondary"
                                sx={{ ml: 1 }}
                              >
                                ({row.subRows.length})
                              </Typography>
                            </Button>
                          ) : cell.column.id === 'progress' ? (
                            <Stack spacing={0.5}>
                              <Typography variant="body2">
                                {String(cell.getValue())}%
                              </Typography>
                              <LinearProgress
                                variant="determinate"
                                value={Number(cell.getValue())}
                              />
                            </Stack>
                          ) : (
                            <table.FlexRender cell={cell} />
                          )}
                        </TableCell>
                      ))}
                    </TableRow>
                  ))}
                </TableBody>
              </MuiTable>
            </TableContainer>
            <Pagination table={table} />
          </Paper>
        </Stack>
      </Container>
    </SortingContext.Provider>
  )
}

function ResizableHeaderCell({
  header,
  table,
}: {
  header: Header<typeof features, Person>
  table: {
    FlexRender: React.ComponentType<{
      header: Header<typeof features, Person>
    }>
  }
}) {
  const sorting = React.useContext(SortingContext)
  const sortDirection = getSortDirection(sorting, header.column.id)

  return (
    <TableCell
      colSpan={header.colSpan}
      align={header.column.id === 'select' ? 'center' : 'left'}
      sortDirection={sortDirection || false}
      aria-sort={getAriaSort(sortDirection || false)}
      data-sort={sortDirection}
      sx={{
        width: `calc(var(--header-${header.id}-size) * 1px)`,
        borderRight: header.id === 'actions' ? undefined : 1,
        borderColor: 'divider',
        p: 1,
        ...getCommonPinningStyles(header.column),
      }}
    >
      <Box
        sx={{ position: 'relative', pr: header.column.getCanResize() ? 1 : 0 }}
      >
        {header.isPlaceholder ? null : <table.FlexRender header={header} />}
        {header.column.getCanResize() ? (
          <Box
            onDoubleClick={() => header.column.resetSize()}
            onMouseDown={header.getResizeHandler()}
            onTouchStart={header.getResizeHandler()}
            sx={{
              position: 'absolute',
              top: 0,
              right: -6,
              width: 6,
              height: '100%',
              cursor: 'col-resize',
              touchAction: 'none',
              bgcolor: header.column.getIsResizing()
                ? 'primary.main'
                : 'transparent',
              '&:hover': { bgcolor: 'primary.main' },
            }}
          />
        ) : null}
      </Box>
    </TableCell>
  )
}

function Root() {
  const prefersDark = useMediaQuery('(prefers-color-scheme: dark)')
  const [mode, setMode] = React.useState<'light' | 'dark' | 'system'>('system')
  const resolvedMode =
    mode === 'system' ? (prefersDark ? 'dark' : 'light') : mode
  const theme = React.useMemo(
    () =>
      createTheme({
        palette: {
          mode: resolvedMode,
        },
      }),
    [resolvedMode],
  )

  return (
    <ThemeProvider theme={theme}>
      <CssBaseline />
      <App mode={mode} setMode={setMode} />
    </ThemeProvider>
  )
}

const rootElement = document.getElementById('root')
if (!rootElement) throw new Error('Failed to find the root element')

ReactDOM.createRoot(rootElement).render(
  <React.StrictMode>
    <Root />
  </React.StrictMode>,
)