@humanspeak/svelte-headless-table 5.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.
- package/LICENSE +22 -0
- package/README.md +122 -0
- package/dist/bodyCells.d.ts +53 -0
- package/dist/bodyCells.js +90 -0
- package/dist/bodyRows.d.ts +82 -0
- package/dist/bodyRows.js +256 -0
- package/dist/columns.d.ts +76 -0
- package/dist/columns.js +96 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/createTable.d.ts +17 -0
- package/dist/createTable.js +36 -0
- package/dist/createViewModel.d.ts +38 -0
- package/dist/createViewModel.js +230 -0
- package/dist/headerCells.d.ts +80 -0
- package/dist/headerCells.js +147 -0
- package/dist/headerRows.d.ts +35 -0
- package/dist/headerRows.js +185 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +12 -0
- package/dist/plugins/addColumnFilters.d.ts +39 -0
- package/dist/plugins/addColumnFilters.js +117 -0
- package/dist/plugins/addColumnOrder.d.ts +10 -0
- package/dist/plugins/addColumnOrder.js +24 -0
- package/dist/plugins/addDataExport.d.ts +21 -0
- package/dist/plugins/addDataExport.js +68 -0
- package/dist/plugins/addExpandedRows.d.ts +17 -0
- package/dist/plugins/addExpandedRows.js +51 -0
- package/dist/plugins/addFlatten.d.ts +19 -0
- package/dist/plugins/addFlatten.js +38 -0
- package/dist/plugins/addGridLayout.d.ts +2 -0
- package/dist/plugins/addGridLayout.js +73 -0
- package/dist/plugins/addGroupBy.d.ts +40 -0
- package/dist/plugins/addGroupBy.js +192 -0
- package/dist/plugins/addHiddenColumns.d.ts +9 -0
- package/dist/plugins/addHiddenColumns.js +17 -0
- package/dist/plugins/addPagination.d.ts +39 -0
- package/dist/plugins/addPagination.js +84 -0
- package/dist/plugins/addResizedColumns.d.ts +41 -0
- package/dist/plugins/addResizedColumns.js +252 -0
- package/dist/plugins/addSelectedRows.d.ts +29 -0
- package/dist/plugins/addSelectedRows.js +190 -0
- package/dist/plugins/addSortBy.d.ts +46 -0
- package/dist/plugins/addSortBy.js +176 -0
- package/dist/plugins/addSubRows.d.ts +9 -0
- package/dist/plugins/addSubRows.js +28 -0
- package/dist/plugins/addTableFilter.d.ts +28 -0
- package/dist/plugins/addTableFilter.js +95 -0
- package/dist/plugins/index.d.ts +15 -0
- package/dist/plugins/index.js +16 -0
- package/dist/plugins/package.json +5 -0
- package/dist/tableComponent.d.ts +19 -0
- package/dist/tableComponent.js +35 -0
- package/dist/types/Action.d.ts +5 -0
- package/dist/types/Action.js +1 -0
- package/dist/types/Entries.d.ts +3 -0
- package/dist/types/Entries.js +1 -0
- package/dist/types/KeyPath.d.ts +6 -0
- package/dist/types/KeyPath.js +1 -0
- package/dist/types/Label.d.ts +8 -0
- package/dist/types/Label.js +1 -0
- package/dist/types/Matrix.d.ts +1 -0
- package/dist/types/Matrix.js +1 -0
- package/dist/types/TablePlugin.d.ts +88 -0
- package/dist/types/TablePlugin.js +1 -0
- package/dist/utils/array.d.ts +2 -0
- package/dist/utils/array.js +9 -0
- package/dist/utils/attributes.d.ts +2 -0
- package/dist/utils/attributes.js +23 -0
- package/dist/utils/clone.d.ts +13 -0
- package/dist/utils/clone.js +18 -0
- package/dist/utils/compare.d.ts +2 -0
- package/dist/utils/compare.js +17 -0
- package/dist/utils/counter.d.ts +1 -0
- package/dist/utils/counter.js +7 -0
- package/dist/utils/css.d.ts +1 -0
- package/dist/utils/css.js +5 -0
- package/dist/utils/event.d.ts +1 -0
- package/dist/utils/event.js +5 -0
- package/dist/utils/filter.d.ts +4 -0
- package/dist/utils/filter.js +4 -0
- package/dist/utils/math.d.ts +2 -0
- package/dist/utils/math.js +2 -0
- package/dist/utils/matrix.d.ts +3 -0
- package/dist/utils/matrix.js +23 -0
- package/dist/utils/store.d.ts +37 -0
- package/dist/utils/store.js +123 -0
- package/package.json +98 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { BodyRow } from '../bodyRows.js';
|
|
2
|
+
import type { NewTablePropSet, TablePlugin } from '../types/TablePlugin.js';
|
|
3
|
+
import { type RecordSetStore } from '../utils/store.js';
|
|
4
|
+
import { type Readable, type Writable } from 'svelte/store';
|
|
5
|
+
export interface ExpandedRowsConfig<Item> {
|
|
6
|
+
initialExpandedIds?: Record<string, boolean>;
|
|
7
|
+
}
|
|
8
|
+
export interface ExpandedRowsState<Item> {
|
|
9
|
+
expandedIds: RecordSetStore<string>;
|
|
10
|
+
getRowState: (row: BodyRow<Item>) => ExpandedRowsRowState;
|
|
11
|
+
}
|
|
12
|
+
export interface ExpandedRowsRowState {
|
|
13
|
+
isExpanded: Writable<boolean>;
|
|
14
|
+
canExpand: Readable<boolean>;
|
|
15
|
+
isAllSubRowsExpanded: Readable<boolean>;
|
|
16
|
+
}
|
|
17
|
+
export declare const addExpandedRows: <Item>({ initialExpandedIds }?: ExpandedRowsConfig<Item>) => TablePlugin<Item, ExpandedRowsState<Item>, Record<string, never>, NewTablePropSet<never>>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { recordSetStore } from '../utils/store.js';
|
|
2
|
+
import { keyed } from '@humanspeak/svelte-keyed';
|
|
3
|
+
import { derived, readable } from 'svelte/store';
|
|
4
|
+
const withExpandedRows = (row, expandedIds) => {
|
|
5
|
+
if (row.subRows === undefined) {
|
|
6
|
+
return [row];
|
|
7
|
+
}
|
|
8
|
+
if (expandedIds[row.id] !== true) {
|
|
9
|
+
return [row];
|
|
10
|
+
}
|
|
11
|
+
const expandedSubRows = row.subRows.flatMap((subRow) => withExpandedRows(subRow, expandedIds));
|
|
12
|
+
return [row, ...expandedSubRows];
|
|
13
|
+
};
|
|
14
|
+
export const addExpandedRows = ({ initialExpandedIds = {} } = {}) => () => {
|
|
15
|
+
const expandedIds = recordSetStore(initialExpandedIds);
|
|
16
|
+
const getRowState = (row) => {
|
|
17
|
+
const isExpanded = keyed(expandedIds, row.id);
|
|
18
|
+
const canExpand = readable((row.subRows?.length ?? 0) > 0);
|
|
19
|
+
const subRowExpandedIds = derived(expandedIds, ($expandedIds) => {
|
|
20
|
+
// Check prefix with '>' to match child ids while ignoring this row's id.
|
|
21
|
+
return Object.entries($expandedIds).filter(([id, expanded]) => id.startsWith(`${row.id}>`) && expanded);
|
|
22
|
+
});
|
|
23
|
+
// If the number of expanded subRows is equal to the number of subRows
|
|
24
|
+
// that can expand, then all subRows are expanded.
|
|
25
|
+
const isAllSubRowsExpanded = derived(subRowExpandedIds, ($subRowExpandedIds) => {
|
|
26
|
+
if (row.subRows === undefined) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
// canExpand is derived from the presence of the `subRows` property.
|
|
30
|
+
const expandableSubRows = row.subRows.filter((subRow) => subRow.subRows !== undefined);
|
|
31
|
+
return $subRowExpandedIds.length === expandableSubRows.length;
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
isExpanded,
|
|
35
|
+
canExpand,
|
|
36
|
+
isAllSubRowsExpanded
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
const pluginState = { expandedIds, getRowState };
|
|
40
|
+
const deriveRows = (rows) => {
|
|
41
|
+
return derived([rows, expandedIds], ([$rows, $expandedIds]) => {
|
|
42
|
+
return $rows.flatMap((row) => {
|
|
43
|
+
return withExpandedRows(row, $expandedIds);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
pluginState,
|
|
49
|
+
deriveRows
|
|
50
|
+
};
|
|
51
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BodyRow } from '../bodyRows.js';
|
|
2
|
+
import type { NewTablePropSet, TablePlugin } from '../types/TablePlugin.js';
|
|
3
|
+
import { type Writable } from 'svelte/store';
|
|
4
|
+
export interface FlattenConfig {
|
|
5
|
+
initialDepth?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface FlattenState {
|
|
8
|
+
depth: Writable<number>;
|
|
9
|
+
}
|
|
10
|
+
export interface FlattenColumnOptions<Item> {
|
|
11
|
+
}
|
|
12
|
+
export type FlattenPropSet = NewTablePropSet<{
|
|
13
|
+
'tbody.tr.td': {
|
|
14
|
+
flatten: (depth: number) => void;
|
|
15
|
+
unflatten: () => void;
|
|
16
|
+
};
|
|
17
|
+
}>;
|
|
18
|
+
export declare const getFlattenedRows: <Item, Row extends BodyRow<Item>>(rows: Row[], depth: number) => Row[];
|
|
19
|
+
export declare const addFlatten: <Item>({ initialDepth }?: FlattenConfig) => TablePlugin<Item, FlattenState, FlattenColumnOptions<Item>, FlattenPropSet>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { derived, writable } from 'svelte/store';
|
|
2
|
+
export const getFlattenedRows = (rows, depth) => {
|
|
3
|
+
if (depth === 0)
|
|
4
|
+
return rows;
|
|
5
|
+
const flattenedRows = [];
|
|
6
|
+
for (const row of rows) {
|
|
7
|
+
if (row.subRows === undefined)
|
|
8
|
+
continue;
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
+
flattenedRows.push(...getFlattenedRows(row.subRows, depth - 1));
|
|
11
|
+
}
|
|
12
|
+
return flattenedRows;
|
|
13
|
+
};
|
|
14
|
+
export const addFlatten = ({ initialDepth = 0 } = {}) => () => {
|
|
15
|
+
const depth = writable(initialDepth);
|
|
16
|
+
const pluginState = { depth };
|
|
17
|
+
const deriveRows = (rows) => {
|
|
18
|
+
return derived([rows, depth], ([$rows, $depth]) => {
|
|
19
|
+
return getFlattenedRows($rows, $depth);
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
pluginState,
|
|
24
|
+
deriveRows,
|
|
25
|
+
hooks: {
|
|
26
|
+
'tbody.tr.td': () => {
|
|
27
|
+
const props = derived([], () => {
|
|
28
|
+
const flatten = ($depth) => {
|
|
29
|
+
depth.set($depth);
|
|
30
|
+
};
|
|
31
|
+
const unflatten = () => flatten(0);
|
|
32
|
+
return { flatten, unflatten };
|
|
33
|
+
});
|
|
34
|
+
return { props };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { derived } from 'svelte/store';
|
|
2
|
+
export const addGridLayout = () => ({ tableState }) => {
|
|
3
|
+
const pluginState = {};
|
|
4
|
+
const deriveTableAttrs = (attrs) => {
|
|
5
|
+
return derived([attrs, tableState.visibleColumns], ([$attrs, $visibleColumns]) => {
|
|
6
|
+
return {
|
|
7
|
+
...$attrs,
|
|
8
|
+
style: {
|
|
9
|
+
display: 'grid',
|
|
10
|
+
'grid-template-columns': `repeat(${$visibleColumns.length}, auto)`
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
const deriveTableHeadAttrs = (attrs) => {
|
|
16
|
+
return derived(attrs, ($attrs) => {
|
|
17
|
+
return {
|
|
18
|
+
...$attrs,
|
|
19
|
+
style: {
|
|
20
|
+
display: 'contents'
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
const deriveTableBodyAttrs = (attrs) => {
|
|
26
|
+
return derived(attrs, ($attrs) => {
|
|
27
|
+
return {
|
|
28
|
+
...$attrs,
|
|
29
|
+
style: {
|
|
30
|
+
display: 'contents'
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
pluginState,
|
|
37
|
+
deriveTableAttrs,
|
|
38
|
+
deriveTableHeadAttrs,
|
|
39
|
+
deriveTableBodyAttrs,
|
|
40
|
+
hooks: {
|
|
41
|
+
'thead.tr': () => {
|
|
42
|
+
const attrs = derived([], () => {
|
|
43
|
+
return {
|
|
44
|
+
style: {
|
|
45
|
+
display: 'contents'
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
return { attrs };
|
|
50
|
+
},
|
|
51
|
+
'thead.tr.th': (cell) => {
|
|
52
|
+
const attrs = derived([], () => {
|
|
53
|
+
return {
|
|
54
|
+
style: {
|
|
55
|
+
'grid-column': `${cell.colstart + 1} / span ${cell.colspan}`
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
return { attrs };
|
|
60
|
+
},
|
|
61
|
+
'tbody.tr': () => {
|
|
62
|
+
const attrs = derived([], () => {
|
|
63
|
+
return {
|
|
64
|
+
style: {
|
|
65
|
+
display: 'contents'
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
return { attrs };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { BodyRow } from '../bodyRows.js';
|
|
2
|
+
import type { DataLabel } from '../types/Label.js';
|
|
3
|
+
import type { NewTablePropSet, TablePlugin } from '../types/TablePlugin.js';
|
|
4
|
+
import { type ArraySetStore } from '../utils/store.js';
|
|
5
|
+
export interface GroupByConfig {
|
|
6
|
+
initialGroupByIds?: string[];
|
|
7
|
+
disableMultiGroup?: boolean;
|
|
8
|
+
isMultiGroupEvent?: (event: Event) => boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface GroupByState {
|
|
11
|
+
groupByIds: ArraySetStore<string>;
|
|
12
|
+
}
|
|
13
|
+
export interface GroupByColumnOptions<Item, Value = any, GroupOn extends string | number = any, Aggregate = any> {
|
|
14
|
+
disable?: boolean;
|
|
15
|
+
getAggregateValue?: (values: GroupOn[]) => Aggregate;
|
|
16
|
+
getGroupOn?: (value: Value) => GroupOn;
|
|
17
|
+
cell?: DataLabel<Item>;
|
|
18
|
+
}
|
|
19
|
+
export type GroupByPropSet = NewTablePropSet<{
|
|
20
|
+
'thead.tr.th': {
|
|
21
|
+
grouped: boolean;
|
|
22
|
+
toggle: (event: Event) => void;
|
|
23
|
+
clear: () => void;
|
|
24
|
+
disabled: boolean;
|
|
25
|
+
};
|
|
26
|
+
'tbody.tr.td': {
|
|
27
|
+
repeated: boolean;
|
|
28
|
+
aggregated: boolean;
|
|
29
|
+
grouped: boolean;
|
|
30
|
+
};
|
|
31
|
+
}>;
|
|
32
|
+
interface GetGroupedRowsProps {
|
|
33
|
+
repeatCellIds: Record<string, boolean>;
|
|
34
|
+
aggregateCellIds: Record<string, boolean>;
|
|
35
|
+
groupCellIds: Record<string, boolean>;
|
|
36
|
+
allGroupByIds: string[];
|
|
37
|
+
}
|
|
38
|
+
export declare const getGroupedRows: <Item, Row extends BodyRow<Item>, GroupOn extends string | number = any>(rows: Row[], groupByIds: string[], columnOptions: Record<string, GroupByColumnOptions<Item>>, { repeatCellIds, aggregateCellIds, groupCellIds, allGroupByIds }: GetGroupedRowsProps) => Row[];
|
|
39
|
+
export declare const addGroupBy: <Item>({ initialGroupByIds, disableMultiGroup, isMultiGroupEvent }?: GroupByConfig) => TablePlugin<Item, GroupByState, GroupByColumnOptions<Item>, GroupByPropSet>;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { DataBodyCell } from '../bodyCells.js';
|
|
2
|
+
import { BodyRow, DisplayBodyRow } from '../bodyRows.js';
|
|
3
|
+
import { isShiftClick } from '../utils/event.js';
|
|
4
|
+
import { nonUndefined } from '../utils/filter.js';
|
|
5
|
+
import { arraySetStore } from '../utils/store.js';
|
|
6
|
+
import { derived, writable } from 'svelte/store';
|
|
7
|
+
const getIdPrefix = (id) => {
|
|
8
|
+
const prefixTokens = id.split('>').slice(0, -1);
|
|
9
|
+
if (prefixTokens.length === 0) {
|
|
10
|
+
return '';
|
|
11
|
+
}
|
|
12
|
+
return `${prefixTokens.join('>')}>`;
|
|
13
|
+
};
|
|
14
|
+
const getIdLeaf = (id) => {
|
|
15
|
+
const tokens = id.split('>');
|
|
16
|
+
return tokens[tokens.length - 1] ?? '';
|
|
17
|
+
};
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
const deepenIdAndDepth = (row, parentId) => {
|
|
20
|
+
row.id = `${parentId}>${row.id}`;
|
|
21
|
+
row.depth = row.depth + 1;
|
|
22
|
+
row.subRows?.forEach((subRow) => deepenIdAndDepth(subRow, parentId));
|
|
23
|
+
};
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
25
|
+
export const getGroupedRows = (rows, groupByIds, columnOptions, { repeatCellIds, aggregateCellIds, groupCellIds, allGroupByIds }) => {
|
|
26
|
+
if (groupByIds.length === 0) {
|
|
27
|
+
return rows;
|
|
28
|
+
}
|
|
29
|
+
if (rows.length === 0) {
|
|
30
|
+
return rows;
|
|
31
|
+
}
|
|
32
|
+
const idPrefix = getIdPrefix(rows[0].id);
|
|
33
|
+
const [groupById, ...restIds] = groupByIds;
|
|
34
|
+
const subRowsForGroupOnValue = new Map();
|
|
35
|
+
for (const row of rows) {
|
|
36
|
+
const cell = row.cellForId[groupById];
|
|
37
|
+
if (!cell.isData()) {
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
const columnOption = columnOptions[groupById] ?? {};
|
|
41
|
+
const { getGroupOn } = columnOption;
|
|
42
|
+
const groupOnValue = getGroupOn?.(cell.value) ?? cell.value;
|
|
43
|
+
if (typeof groupOnValue === 'function' || typeof groupOnValue === 'object') {
|
|
44
|
+
console.warn(`Missing \`getGroupOn\` column option to aggregate column "${groupById}" with object values`);
|
|
45
|
+
}
|
|
46
|
+
const subRows = subRowsForGroupOnValue.get(groupOnValue) ?? [];
|
|
47
|
+
subRowsForGroupOnValue.set(groupOnValue, [...subRows, row]);
|
|
48
|
+
}
|
|
49
|
+
const groupedRows = [];
|
|
50
|
+
let groupRowIdx = 0;
|
|
51
|
+
for (const [groupOnValue, subRows] of subRowsForGroupOnValue.entries()) {
|
|
52
|
+
// Guaranteed to have at least one subRow.
|
|
53
|
+
const firstRow = subRows[0];
|
|
54
|
+
const groupRow = new DisplayBodyRow({
|
|
55
|
+
id: `${idPrefix}${groupRowIdx++}`,
|
|
56
|
+
// TODO Differentiate data rows and grouped rows.
|
|
57
|
+
depth: firstRow.depth,
|
|
58
|
+
cells: [],
|
|
59
|
+
cellForId: {}
|
|
60
|
+
});
|
|
61
|
+
const groupRowCellForId = Object.fromEntries(Object.entries(firstRow.cellForId).map(([id, cell]) => {
|
|
62
|
+
if (id === groupById) {
|
|
63
|
+
const newCell = new DataBodyCell({
|
|
64
|
+
column: cell.column,
|
|
65
|
+
row: groupRow,
|
|
66
|
+
value: groupOnValue
|
|
67
|
+
});
|
|
68
|
+
return [id, newCell];
|
|
69
|
+
}
|
|
70
|
+
const columnCells = subRows.map((row) => row.cellForId[id]).filter(nonUndefined);
|
|
71
|
+
if (!columnCells[0].isData()) {
|
|
72
|
+
const clonedCell = columnCells[0].clone();
|
|
73
|
+
clonedCell.row = groupRow;
|
|
74
|
+
return [id, clonedCell];
|
|
75
|
+
}
|
|
76
|
+
const { cell: label, getAggregateValue } = columnOptions[id] ?? {};
|
|
77
|
+
const columnValues = columnCells.map((cell) => cell.value);
|
|
78
|
+
const value = getAggregateValue === undefined ? '' : getAggregateValue(columnValues);
|
|
79
|
+
const newCell = new DataBodyCell({
|
|
80
|
+
column: cell.column,
|
|
81
|
+
row: groupRow,
|
|
82
|
+
value,
|
|
83
|
+
label
|
|
84
|
+
});
|
|
85
|
+
return [id, newCell];
|
|
86
|
+
}));
|
|
87
|
+
const groupRowCells = firstRow.cells.map((cell) => {
|
|
88
|
+
return groupRowCellForId[cell.id];
|
|
89
|
+
});
|
|
90
|
+
groupRow.cellForId = groupRowCellForId;
|
|
91
|
+
groupRow.cells = groupRowCells;
|
|
92
|
+
const groupRowSubRows = subRows.map((subRow) => {
|
|
93
|
+
const clonedSubRow = subRow.clone({ includeCells: true, includeSubRows: true });
|
|
94
|
+
deepenIdAndDepth(clonedSubRow, groupRow.id);
|
|
95
|
+
return clonedSubRow;
|
|
96
|
+
});
|
|
97
|
+
groupRow.subRows = getGroupedRows(groupRowSubRows, restIds, columnOptions, {
|
|
98
|
+
repeatCellIds,
|
|
99
|
+
aggregateCellIds,
|
|
100
|
+
groupCellIds,
|
|
101
|
+
allGroupByIds
|
|
102
|
+
});
|
|
103
|
+
groupedRows.push(groupRow);
|
|
104
|
+
groupRow.cells.forEach((cell) => {
|
|
105
|
+
if (cell.id === groupById) {
|
|
106
|
+
groupCellIds[cell.rowColId()] = true;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
aggregateCellIds[cell.rowColId()] = true;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
groupRow.subRows.forEach((subRow) => {
|
|
113
|
+
subRow.parentRow = groupRow;
|
|
114
|
+
subRow.cells.forEach((cell) => {
|
|
115
|
+
if (allGroupByIds.includes(cell.id) && groupCellIds[cell.rowColId()] !== true) {
|
|
116
|
+
repeatCellIds[cell.rowColId()] = true;
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return groupedRows;
|
|
122
|
+
};
|
|
123
|
+
export const addGroupBy = ({ initialGroupByIds = [], disableMultiGroup = false, isMultiGroupEvent = isShiftClick } = {}) => ({ columnOptions }) => {
|
|
124
|
+
const disabledGroupIds = Object.entries(columnOptions)
|
|
125
|
+
.filter(([, option]) => option.disable === true)
|
|
126
|
+
.map(([columnId]) => columnId);
|
|
127
|
+
const groupByIds = arraySetStore(initialGroupByIds);
|
|
128
|
+
const repeatCellIds = writable({});
|
|
129
|
+
const aggregateCellIds = writable({});
|
|
130
|
+
const groupCellIds = writable({});
|
|
131
|
+
const pluginState = {
|
|
132
|
+
groupByIds
|
|
133
|
+
};
|
|
134
|
+
const deriveRows = (rows) => {
|
|
135
|
+
return derived([rows, groupByIds], ([$rows, $groupByIds]) => {
|
|
136
|
+
const $repeatCellIds = {};
|
|
137
|
+
const $aggregateCellIds = {};
|
|
138
|
+
const $groupCellIds = {};
|
|
139
|
+
const $groupedRows = getGroupedRows($rows, $groupByIds, columnOptions, {
|
|
140
|
+
repeatCellIds: $repeatCellIds,
|
|
141
|
+
aggregateCellIds: $aggregateCellIds,
|
|
142
|
+
groupCellIds: $groupCellIds,
|
|
143
|
+
allGroupByIds: $groupByIds
|
|
144
|
+
});
|
|
145
|
+
repeatCellIds.set($repeatCellIds);
|
|
146
|
+
aggregateCellIds.set($aggregateCellIds);
|
|
147
|
+
groupCellIds.set($groupCellIds);
|
|
148
|
+
return $groupedRows;
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
return {
|
|
152
|
+
pluginState,
|
|
153
|
+
deriveRows,
|
|
154
|
+
hooks: {
|
|
155
|
+
'thead.tr.th': (cell) => {
|
|
156
|
+
const disabled = disabledGroupIds.includes(cell.id) || !cell.isData();
|
|
157
|
+
const props = derived(groupByIds, ($groupByIds) => {
|
|
158
|
+
const grouped = $groupByIds.includes(cell.id);
|
|
159
|
+
const toggle = (event) => {
|
|
160
|
+
if (!cell.isData())
|
|
161
|
+
return;
|
|
162
|
+
if (disabled)
|
|
163
|
+
return;
|
|
164
|
+
groupByIds.toggle(cell.id, {
|
|
165
|
+
clearOthers: disableMultiGroup || !isMultiGroupEvent(event)
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
const clear = () => {
|
|
169
|
+
groupByIds.remove(cell.id);
|
|
170
|
+
};
|
|
171
|
+
return {
|
|
172
|
+
grouped,
|
|
173
|
+
toggle,
|
|
174
|
+
clear,
|
|
175
|
+
disabled
|
|
176
|
+
};
|
|
177
|
+
});
|
|
178
|
+
return { props };
|
|
179
|
+
},
|
|
180
|
+
'tbody.tr.td': (cell) => {
|
|
181
|
+
const props = derived([repeatCellIds, aggregateCellIds, groupCellIds], ([$repeatCellIds, $aggregateCellIds, $groupCellIds]) => {
|
|
182
|
+
return {
|
|
183
|
+
repeated: $repeatCellIds[cell.rowColId()] === true,
|
|
184
|
+
aggregated: $aggregateCellIds[cell.rowColId()] === true,
|
|
185
|
+
grouped: $groupCellIds[cell.rowColId()] === true
|
|
186
|
+
};
|
|
187
|
+
});
|
|
188
|
+
return { props };
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NewTablePropSet, TablePlugin } from '../types/TablePlugin.js';
|
|
2
|
+
import { type Writable } from 'svelte/store';
|
|
3
|
+
export interface HiddenColumnsConfig {
|
|
4
|
+
initialHiddenColumnIds?: string[];
|
|
5
|
+
}
|
|
6
|
+
export interface HiddenColumnsState {
|
|
7
|
+
hiddenColumnIds: Writable<string[]>;
|
|
8
|
+
}
|
|
9
|
+
export declare const addHiddenColumns: <Item>({ initialHiddenColumnIds }?: HiddenColumnsConfig) => TablePlugin<Item, HiddenColumnsState, Record<string, never>, NewTablePropSet<never>>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { derived, writable } from 'svelte/store';
|
|
2
|
+
export const addHiddenColumns = ({ initialHiddenColumnIds = [] } = {}) => () => {
|
|
3
|
+
const hiddenColumnIds = writable(initialHiddenColumnIds);
|
|
4
|
+
const pluginState = { hiddenColumnIds };
|
|
5
|
+
const deriveFlatColumns = (flatColumns) => {
|
|
6
|
+
return derived([flatColumns, hiddenColumnIds], ([$flatColumns, $hiddenColumnIds]) => {
|
|
7
|
+
if ($hiddenColumnIds.length === 0) {
|
|
8
|
+
return $flatColumns;
|
|
9
|
+
}
|
|
10
|
+
return $flatColumns.filter((c) => !$hiddenColumnIds.includes(c.id));
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
return {
|
|
14
|
+
pluginState,
|
|
15
|
+
deriveFlatColumns
|
|
16
|
+
};
|
|
17
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { NewTablePropSet, TablePlugin } from '../types/TablePlugin.js';
|
|
2
|
+
import { type Readable, type Updater, type Writable } from 'svelte/store';
|
|
3
|
+
export type PaginationConfig = {
|
|
4
|
+
initialPageIndex?: number;
|
|
5
|
+
initialPageSize?: number;
|
|
6
|
+
} & ({
|
|
7
|
+
serverSide?: false | undefined;
|
|
8
|
+
serverItemCount?: undefined;
|
|
9
|
+
} | {
|
|
10
|
+
serverSide: true;
|
|
11
|
+
serverItemCount: Readable<number>;
|
|
12
|
+
});
|
|
13
|
+
export interface PaginationState {
|
|
14
|
+
pageSize: Writable<number>;
|
|
15
|
+
pageIndex: Writable<number>;
|
|
16
|
+
pageCount: Readable<number>;
|
|
17
|
+
hasPreviousPage: Readable<boolean>;
|
|
18
|
+
hasNextPage: Readable<boolean>;
|
|
19
|
+
}
|
|
20
|
+
export declare const createPageStore: ({ items, initialPageSize, initialPageIndex, serverSide, serverItemCount }: PageStoreConfig) => {
|
|
21
|
+
pageSize: {
|
|
22
|
+
subscribe: (this: void, run: import("svelte/store").Subscriber<number>, invalidate?: import("svelte/store").Invalidator<number> | undefined) => import("svelte/store").Unsubscriber;
|
|
23
|
+
update: (fn: Updater<number>) => void;
|
|
24
|
+
set: (newPageSize: number) => void;
|
|
25
|
+
};
|
|
26
|
+
pageIndex: Writable<number>;
|
|
27
|
+
pageCount: Readable<number>;
|
|
28
|
+
serverItemCount: Readable<number> | undefined;
|
|
29
|
+
hasPreviousPage: Readable<boolean>;
|
|
30
|
+
hasNextPage: Readable<boolean>;
|
|
31
|
+
};
|
|
32
|
+
export interface PageStoreConfig {
|
|
33
|
+
items: Readable<unknown[]>;
|
|
34
|
+
initialPageSize?: number;
|
|
35
|
+
initialPageIndex?: number;
|
|
36
|
+
serverSide?: boolean;
|
|
37
|
+
serverItemCount?: Readable<number>;
|
|
38
|
+
}
|
|
39
|
+
export declare const addPagination: <Item>({ initialPageIndex, initialPageSize, serverSide, serverItemCount }?: PaginationConfig) => TablePlugin<Item, PaginationState, Record<string, never>, NewTablePropSet<never>>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { derived, writable } from 'svelte/store';
|
|
2
|
+
const MIN_PAGE_SIZE = 1;
|
|
3
|
+
export const createPageStore = ({ items, initialPageSize, initialPageIndex, serverSide, serverItemCount }) => {
|
|
4
|
+
const pageSize = writable(initialPageSize);
|
|
5
|
+
const updatePageSize = (fn) => {
|
|
6
|
+
pageSize.update(($pageSize) => {
|
|
7
|
+
const newPageSize = fn($pageSize);
|
|
8
|
+
return Math.max(newPageSize, MIN_PAGE_SIZE);
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
const setPageSize = (newPageSize) => updatePageSize(() => newPageSize);
|
|
12
|
+
const pageIndex = writable(initialPageIndex);
|
|
13
|
+
function calcPageCountAndLimitIndex([$pageSize, $itemCount]) {
|
|
14
|
+
const $pageCount = Math.ceil($itemCount / $pageSize);
|
|
15
|
+
pageIndex.update(($pageIndex) => {
|
|
16
|
+
if ($pageCount > 0 && $pageIndex >= $pageCount) {
|
|
17
|
+
return $pageCount - 1;
|
|
18
|
+
}
|
|
19
|
+
return $pageIndex;
|
|
20
|
+
});
|
|
21
|
+
return $pageCount;
|
|
22
|
+
}
|
|
23
|
+
let pageCount;
|
|
24
|
+
if (serverSide && serverItemCount != null) {
|
|
25
|
+
pageCount = derived([pageSize, serverItemCount], calcPageCountAndLimitIndex);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
const itemCount = derived(items, ($items) => $items.length);
|
|
29
|
+
pageCount = derived([pageSize, itemCount], calcPageCountAndLimitIndex);
|
|
30
|
+
}
|
|
31
|
+
const hasPreviousPage = derived(pageIndex, ($pageIndex) => {
|
|
32
|
+
return $pageIndex > 0;
|
|
33
|
+
});
|
|
34
|
+
const hasNextPage = derived([pageIndex, pageCount], ([$pageIndex, $pageCount]) => {
|
|
35
|
+
return $pageIndex < $pageCount - 1;
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
pageSize: {
|
|
39
|
+
subscribe: pageSize.subscribe,
|
|
40
|
+
update: updatePageSize,
|
|
41
|
+
set: setPageSize
|
|
42
|
+
},
|
|
43
|
+
pageIndex,
|
|
44
|
+
pageCount,
|
|
45
|
+
serverItemCount,
|
|
46
|
+
hasPreviousPage,
|
|
47
|
+
hasNextPage
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
export const addPagination = ({ initialPageIndex = 0, initialPageSize = 10, serverSide = false, serverItemCount } = {}) => () => {
|
|
51
|
+
const prePaginatedRows = writable([]);
|
|
52
|
+
const paginatedRows = writable([]);
|
|
53
|
+
const { pageSize, pageIndex, pageCount, hasPreviousPage, hasNextPage } = createPageStore({
|
|
54
|
+
items: prePaginatedRows,
|
|
55
|
+
initialPageIndex,
|
|
56
|
+
initialPageSize,
|
|
57
|
+
serverSide,
|
|
58
|
+
serverItemCount
|
|
59
|
+
});
|
|
60
|
+
const pluginState = {
|
|
61
|
+
pageSize,
|
|
62
|
+
pageIndex,
|
|
63
|
+
pageCount,
|
|
64
|
+
hasPreviousPage,
|
|
65
|
+
hasNextPage
|
|
66
|
+
};
|
|
67
|
+
const derivePageRows = (rows) => {
|
|
68
|
+
return derived([rows, pageSize, pageIndex], ([$rows, $pageSize, $pageIndex]) => {
|
|
69
|
+
prePaginatedRows.set($rows);
|
|
70
|
+
if (serverSide) {
|
|
71
|
+
paginatedRows.set($rows);
|
|
72
|
+
return $rows;
|
|
73
|
+
}
|
|
74
|
+
const startIdx = $pageIndex * $pageSize;
|
|
75
|
+
const _paginatedRows = $rows.slice(startIdx, startIdx + $pageSize);
|
|
76
|
+
paginatedRows.set(_paginatedRows);
|
|
77
|
+
return _paginatedRows;
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
return {
|
|
81
|
+
pluginState,
|
|
82
|
+
derivePageRows
|
|
83
|
+
};
|
|
84
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { NewTableAttributeSet, NewTablePropSet, TablePlugin } from '../types/TablePlugin.js';
|
|
2
|
+
import { type Writable } from 'svelte/store';
|
|
3
|
+
export interface AddResizedColumnsConfig {
|
|
4
|
+
onResizeEnd?: (ev: Event) => void;
|
|
5
|
+
}
|
|
6
|
+
export type ResizedColumnsState = {
|
|
7
|
+
columnWidths: Writable<Record<string, number>>;
|
|
8
|
+
};
|
|
9
|
+
export type ResizedColumnsColumnOptions = {
|
|
10
|
+
initialWidth?: number;
|
|
11
|
+
minWidth?: number;
|
|
12
|
+
maxWidth?: number;
|
|
13
|
+
disable?: boolean;
|
|
14
|
+
};
|
|
15
|
+
export type ResizedColumnsPropSet = NewTablePropSet<{
|
|
16
|
+
'thead.tr.th': {
|
|
17
|
+
(node: Element): void;
|
|
18
|
+
drag: (node: Element) => void;
|
|
19
|
+
reset: (node: Element) => void;
|
|
20
|
+
disabled: boolean;
|
|
21
|
+
};
|
|
22
|
+
}>;
|
|
23
|
+
export type ResizedColumnsAttributeSet = NewTableAttributeSet<{
|
|
24
|
+
'thead.tr.th': {
|
|
25
|
+
style?: {
|
|
26
|
+
width: string;
|
|
27
|
+
'min-width': string;
|
|
28
|
+
'max-width': string;
|
|
29
|
+
'box-sizing': 'border-box';
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
'tbody.tr.td': {
|
|
33
|
+
style?: {
|
|
34
|
+
width: string;
|
|
35
|
+
'min-width': string;
|
|
36
|
+
'max-width': string;
|
|
37
|
+
'box-sizing': 'border-box';
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
}>;
|
|
41
|
+
export declare const addResizedColumns: <Item>({ onResizeEnd }?: AddResizedColumnsConfig) => TablePlugin<Item, ResizedColumnsState, ResizedColumnsColumnOptions, ResizedColumnsPropSet, ResizedColumnsAttributeSet>;
|