UNPKG

@humanspeak/svelte-headless-table

Version:

A powerful, headless table library for Svelte that provides complete control over table UI while handling complex data operations like sorting, filtering, pagination, grouping, and row expansion. Build custom, accessible data tables with zero styling opin

191 lines (190 loc) 7.18 kB
import { derived, get } from 'svelte/store'; import { nonNull } from '../utils/filter.js'; import { recordSetStore } from '../utils/store.js'; const isAllSubRowsSelectedForRow = (row, $selectedDataIds, linkDataSubRows) => { if (row.isData()) { if (!linkDataSubRows || row.subRows === undefined) { return $selectedDataIds[row.dataId] === true; } } if (row.subRows === undefined) { return false; } return row.subRows.every((subRow) => isAllSubRowsSelectedForRow(subRow, $selectedDataIds, linkDataSubRows)); }; const isSomeSubRowsSelectedForRow = (row, $selectedDataIds, linkDataSubRows) => { if (row.isData()) { if (!linkDataSubRows || row.subRows === undefined) { return $selectedDataIds[row.dataId] === true; } } if (row.subRows === undefined) { return false; } return row.subRows.some((subRow) => isSomeSubRowsSelectedForRow(subRow, $selectedDataIds, linkDataSubRows)); }; const writeSelectedDataIds = (row, value, $selectedDataIds, linkDataSubRows) => { if (row.isData()) { $selectedDataIds[row.dataId] = value; if (!linkDataSubRows) { return; } } if (row.subRows === undefined) { return; } row.subRows.forEach((subRow) => { writeSelectedDataIds(subRow, value, $selectedDataIds, linkDataSubRows); }); }; const getRowIsSelectedStore = (row, selectedDataIds, linkDataSubRows) => { const { subscribe } = derived(selectedDataIds, ($selectedDataIds) => { if (row.isData()) { if (!linkDataSubRows) { return $selectedDataIds[row.dataId] === true; } if ($selectedDataIds[row.dataId] === true) { return true; } } return isAllSubRowsSelectedForRow(row, $selectedDataIds, linkDataSubRows); }); const update = (fn) => { selectedDataIds.update(($selectedDataIds) => { const oldValue = isAllSubRowsSelectedForRow(row, $selectedDataIds, linkDataSubRows); const $updatedSelectedDataIds = { ...$selectedDataIds }; writeSelectedDataIds(row, fn(oldValue), $updatedSelectedDataIds, linkDataSubRows); if (row.parentRow !== undefined && row.parentRow.isData()) { $updatedSelectedDataIds[row.parentRow.dataId] = isAllSubRowsSelectedForRow(row.parentRow, $updatedSelectedDataIds, linkDataSubRows); } return $updatedSelectedDataIds; }); }; const set = (value) => update(() => value); return { subscribe, update, set }; }; export const addSelectedRows = ({ initialSelectedDataIds = {}, linkDataSubRows = true } = {}) => ({ tableState }) => { const selectedDataIds = recordSetStore(initialSelectedDataIds); const getRowState = (row) => { const isSelected = getRowIsSelectedStore(row, selectedDataIds, linkDataSubRows); const isSomeSubRowsSelected = derived([isSelected, selectedDataIds], ([$isSelected, $selectedDataIds]) => { if ($isSelected) return false; return isSomeSubRowsSelectedForRow(row, $selectedDataIds, linkDataSubRows); }); const isAllSubRowsSelected = derived(selectedDataIds, ($selectedDataIds) => { return isAllSubRowsSelectedForRow(row, $selectedDataIds, linkDataSubRows); }); return { isSelected, isSomeSubRowsSelected, isAllSubRowsSelected }; }; // all rows const _allRowsSelected = derived([tableState.rows, selectedDataIds], ([$rows, $selectedDataIds]) => { return $rows.every((row) => { if (!row.isData()) { return true; } return $selectedDataIds[row.dataId] === true; }); }); const setAllRowsSelected = ($allRowsSelected) => { if ($allRowsSelected) { const $rows = get(tableState.rows); const allDataIds = $rows .map((row) => (row.isData() ? row.dataId : null)) .filter(nonNull); selectedDataIds.addAll(allDataIds); } else { selectedDataIds.clear(); } }; const allRowsSelected = { subscribe: _allRowsSelected.subscribe, update(fn) { const $allRowsSelected = get(_allRowsSelected); setAllRowsSelected(fn($allRowsSelected)); }, set: setAllRowsSelected }; const someRowsSelected = derived([tableState.rows, selectedDataIds], ([$rows, $selectedDataIds]) => { return $rows.some((row) => { if (!row.isData()) { return false; } return $selectedDataIds[row.dataId] === true; }); }); // page rows const _allPageRowsSelected = derived([tableState.pageRows, selectedDataIds], ([$pageRows, $selectedDataIds]) => { return $pageRows.every((row) => { if (!row.isData()) { return true; } return $selectedDataIds[row.dataId] === true; }); }); const setAllPageRowsSelected = ($allPageRowsSelected) => { const $pageRows = get(tableState.pageRows); const pageDataIds = $pageRows .map((row) => (row.isData() ? row.dataId : null)) .filter(nonNull); if ($allPageRowsSelected) { selectedDataIds.addAll(pageDataIds); } else { selectedDataIds.removeAll(pageDataIds); } }; const allPageRowsSelected = { subscribe: _allPageRowsSelected.subscribe, update(fn) { const $allPageRowsSelected = get(_allPageRowsSelected); setAllPageRowsSelected(fn($allPageRowsSelected)); }, set: setAllPageRowsSelected }; const somePageRowsSelected = derived([tableState.pageRows, selectedDataIds], ([$pageRows, $selectedDataIds]) => { return $pageRows.some((row) => { if (!row.isData()) { return false; } return $selectedDataIds[row.dataId] === true; }); }); const pluginState = { selectedDataIds, getRowState, allRowsSelected, someRowsSelected, allPageRowsSelected, somePageRowsSelected }; return { pluginState, hooks: { 'tbody.tr': (row) => { const props = derived(selectedDataIds, ($selectedDataIds) => { const someSubRowsSelected = isSomeSubRowsSelectedForRow(row, $selectedDataIds, linkDataSubRows); const allSubRowsSelected = isAllSubRowsSelectedForRow(row, $selectedDataIds, linkDataSubRows); const selected = row.isData() ? $selectedDataIds[row.dataId] === true : allSubRowsSelected; return { selected, someSubRowsSelected, allSubRowsSelected }; }); return { props }; } } }; };