js-spread-grid 0.0.1

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.
Files changed (67) hide show
  1. package/package.json +26 -0
  2. package/src/core/render.js +400 -0
  3. package/src/core/state.js +183 -0
  4. package/src/core-utils/defaults.js +4 -0
  5. package/src/core-utils/rect.js +49 -0
  6. package/src/core-utils/rect.test.js +111 -0
  7. package/src/core-utils/roundToPixels.js +3 -0
  8. package/src/core-utils/stringifyId.js +27 -0
  9. package/src/core-utils/stringifyId.test.js +27 -0
  10. package/src/index.js +595 -0
  11. package/src/state-utils/getActive.js +17 -0
  12. package/src/state-utils/getCellSection.js +22 -0
  13. package/src/state-utils/getCellType.js +7 -0
  14. package/src/state-utils/getClipboardData.js +53 -0
  15. package/src/state-utils/getColumnIndex.js +25 -0
  16. package/src/state-utils/getCombinedCells.js +6 -0
  17. package/src/state-utils/getDataFormatting.js +46 -0
  18. package/src/state-utils/getEditableCells.js +23 -0
  19. package/src/state-utils/getEditedCellsAndFilters.js +4 -0
  20. package/src/state-utils/getEdition.js +6 -0
  21. package/src/state-utils/getFilterFormatting.js +3 -0
  22. package/src/state-utils/getFiltered.js +61 -0
  23. package/src/state-utils/getFilteringRules.js +5 -0
  24. package/src/state-utils/getFixedSize.js +8 -0
  25. package/src/state-utils/getFormatResolver.js +5 -0
  26. package/src/state-utils/getFormattingRules.js +5 -0
  27. package/src/state-utils/getHighlightedCells.js +40 -0
  28. package/src/state-utils/getHoveredCell.js +43 -0
  29. package/src/state-utils/getInputFormatting.js +3 -0
  30. package/src/state-utils/getInputPlacement.js +51 -0
  31. package/src/state-utils/getInvoked.js +5 -0
  32. package/src/state-utils/getIsTextValid.js +3 -0
  33. package/src/state-utils/getKeys.js +3 -0
  34. package/src/state-utils/getLookup.js +3 -0
  35. package/src/state-utils/getMeasureFormatting.js +3 -0
  36. package/src/state-utils/getMeasured.js +113 -0
  37. package/src/state-utils/getMousePosition.js +8 -0
  38. package/src/state-utils/getNewSortBy.js +20 -0
  39. package/src/state-utils/getPinned.js +8 -0
  40. package/src/state-utils/getPlaced.js +45 -0
  41. package/src/state-utils/getReducedCells.js +6 -0
  42. package/src/state-utils/getRenderFormatting.js +122 -0
  43. package/src/state-utils/getResizable.js +49 -0
  44. package/src/state-utils/getResolved.js +42 -0
  45. package/src/state-utils/getResolvedFilters.js +7 -0
  46. package/src/state-utils/getResolvedSortBy.js +7 -0
  47. package/src/state-utils/getRowIndex.js +25 -0
  48. package/src/state-utils/getScrollRect.js +41 -0
  49. package/src/state-utils/getSections.js +101 -0
  50. package/src/state-utils/getSelection.js +5 -0
  51. package/src/state-utils/getSorted.js +130 -0
  52. package/src/state-utils/getSortingFormatting.js +3 -0
  53. package/src/state-utils/getSortingRules.js +5 -0
  54. package/src/state-utils/getTextResolver.js +5 -0
  55. package/src/state-utils/getToggledValue.js +11 -0
  56. package/src/state-utils/getTotalSize.js +6 -0
  57. package/src/state-utils/getUnfolded.js +86 -0
  58. package/src/types/Edition.js +37 -0
  59. package/src/types/FilteringRules.js +48 -0
  60. package/src/types/FormatResolver.js +19 -0
  61. package/src/types/FormattingRules.js +120 -0
  62. package/src/types/FormattingRules.test.js +90 -0
  63. package/src/types/RulesLookup.js +118 -0
  64. package/src/types/Selection.js +25 -0
  65. package/src/types/SortingRules.js +62 -0
  66. package/src/types/TextResolver.js +60 -0
  67. package/src/types/VisibilityResolver.js +61 -0
@@ -0,0 +1,53 @@
1
+ import stringifyId from "../core-utils/stringifyId.js";
2
+ import Selection from "../types/Selection.js";
3
+
4
+ export default function getClipboardData(selectedCells, columns, rows, formatResolver) {
5
+ const columnLookup = new Map(columns.map(column => [column.key, column]));
6
+ const rowLookup = new Map(rows.map(row => [row.key, row]));
7
+ const selectedCellKeys = selectedCells.map(cell => ({
8
+ columnKey: stringifyId(cell.columnId),
9
+ rowKey: stringifyId(cell.rowId)
10
+ })).filter(cell => columnLookup.has(cell.columnKey) && rowLookup.has(cell.rowKey));
11
+ const selectedColumns = new Set(selectedCellKeys.map(cell => cell.columnKey));
12
+ const selectedRows = new Set(selectedCellKeys.map(cell => cell.rowKey));
13
+
14
+ if (selectedCellKeys.length === 0)
15
+ return '';
16
+
17
+ const selection = new Selection(selectedCells);
18
+ const minColumnIndex = Math.min(...selectedCellKeys.map(cell => columnLookup.get(cell.columnKey).index));
19
+ const maxColumnIndex = Math.max(...selectedCellKeys.map(cell => columnLookup.get(cell.columnKey).index));
20
+ const minRowIndex = Math.min(...selectedCellKeys.map(cell => rowLookup.get(cell.rowKey).index));
21
+ const maxRowIndex = Math.max(...selectedCellKeys.map(cell => rowLookup.get(cell.rowKey).index));
22
+
23
+ const stringBuilder = [];
24
+
25
+ for (let rowIndex = minRowIndex; rowIndex <= maxRowIndex; rowIndex++) {
26
+ const row = rows[rowIndex];
27
+ const rowKey = row.key;
28
+
29
+ if (!selectedRows.has(rowKey))
30
+ continue;
31
+
32
+ for (let columnIndex = minColumnIndex; columnIndex <= maxColumnIndex; columnIndex++) {
33
+ const column = columns[columnIndex];
34
+ const columnKey = column.key;
35
+
36
+ if (!selectedColumns.has(columnKey))
37
+ continue;
38
+
39
+ if (selection.isKeySelected(rowKey, columnKey)) {
40
+ const cellData = formatResolver.resolve(row, column).text;
41
+ stringBuilder.push(cellData);
42
+ }
43
+
44
+ if (columnIndex < maxColumnIndex)
45
+ stringBuilder.push('\t');
46
+ }
47
+
48
+ if (rowIndex < maxRowIndex)
49
+ stringBuilder.push('\n');
50
+ }
51
+
52
+ return stringBuilder.join('');
53
+ }
@@ -0,0 +1,25 @@
1
+ export default function getColumnIndex(columns, x) {
2
+ if (columns.length === 0)
3
+ return -1;
4
+ if (x < columns[0].leftWithBorder)
5
+ return -1;
6
+ if (x > columns[columns.length - 1].rightWithBorder)
7
+ return -1;
8
+
9
+ let iterA = 0;
10
+ let iterC = columns.length - 1;
11
+
12
+ while (iterA <= iterC) {
13
+ const iterB = Math.floor((iterA + iterC) / 2);
14
+
15
+ if (x < columns[iterB].leftWithBorder)
16
+ iterC = iterB - 1;
17
+ else if (x > columns[iterB].rightWithBorder)
18
+ iterA = iterB + 1;
19
+
20
+ else
21
+ return iterB;
22
+ }
23
+
24
+ return -1;
25
+ }
@@ -0,0 +1,6 @@
1
+ import Selection from "../types/Selection.js";
2
+
3
+ export default function getCombinedCells(previousCells, newCells) {
4
+ const selection = new Selection(newCells);
5
+ return [...newCells, ...previousCells.filter(cell => !selection.isIdSelected(cell.rowId, cell.columnId))];
6
+ }
@@ -0,0 +1,46 @@
1
+ export default function getDataFormatting(formatting, dataSelector, sortBy) {
2
+ return [
3
+ {
4
+ column: { match: 'DATA' },
5
+ row: { match: 'HEADER' },
6
+ value: ({ column }) => column.header === undefined ? column.id : column.header
7
+ },
8
+ {
9
+ column: { match: 'HEADER' },
10
+ row: { match: 'DATA' },
11
+ value: ({ row }) => row.header === undefined ? row.id : row.header
12
+ },
13
+ {
14
+ column: { match: 'HEADER' },
15
+ row: { match: 'SPECIAL' },
16
+ value: ''
17
+ },
18
+ {
19
+ column: { match: 'SPECIAL' },
20
+ row: { match: 'HEADER' },
21
+ value: ''
22
+ },
23
+ ...sortBy.map(({ columnId, rowId, direction }, index) => ({
24
+ column: { id: columnId },
25
+ row: { id: rowId },
26
+ text: ({ column }) => `${sortBy.length > 1 ? index + 1 : ''}${direction === 'ASC' ? '⇣' : '⇡'} ${column.header}`
27
+ })),
28
+ {
29
+ column: { match: 'ANY' },
30
+ row: { match: 'FILTER' },
31
+ value: ({ newValue }) => newValue || '',
32
+ text: ({ newValue }) => newValue || 'Search...', // TODO: Move to render formatting?
33
+ edit: {
34
+ validate: () => true,
35
+ parse: ({ string }) => string,
36
+ autoCommit: true
37
+ },
38
+ },
39
+ {
40
+ column: { match: 'DATA' },
41
+ row: { match: 'DATA' },
42
+ value: dataSelector
43
+ },
44
+ ...formatting
45
+ ];
46
+ }
@@ -0,0 +1,23 @@
1
+ import stringifyId from "../core-utils/stringifyId.js";
2
+ import getCellType from "./getCellType.js";
3
+
4
+ export default function getEditableCells(selectedCells, formatResolver, columnLookup, rowLookup) {
5
+ return selectedCells.map(cell => {
6
+ const columnKey = stringifyId(cell.columnId);
7
+ const rowKey = stringifyId(cell.rowId);
8
+
9
+ if (!columnLookup.has(columnKey))
10
+ return null;
11
+ if (!rowLookup.has(rowKey))
12
+ return null;
13
+
14
+ const column = columnLookup.get(columnKey);
15
+ const row = rowLookup.get(rowKey);
16
+
17
+ return {
18
+ edit: formatResolver.resolve(row, column).edit,
19
+ cell: cell,
20
+ type: getCellType(column, row)
21
+ }
22
+ }).filter(cell => cell?.edit);
23
+ }
@@ -0,0 +1,4 @@
1
+ // TODO: Move
2
+ export default function getEditedCellsAndFilters(editedCells, filters) {
3
+ return [...editedCells, ...filters.map(filter => ({ columnId: filter.columnId, rowId: filter.rowId, value: filter.expression }))];
4
+ }
@@ -0,0 +1,6 @@
1
+ import Edition from "../types/Edition.js";
2
+
3
+ // TODO: Move
4
+ export default function getEdition(editedCellsAndFilters) {
5
+ return new Edition(editedCellsAndFilters);
6
+ }
@@ -0,0 +1,3 @@
1
+ export default function getFilterFormatting(formatting) {
2
+ return formatting;
3
+ }
@@ -0,0 +1,61 @@
1
+ import stringifyId from "../core-utils/stringifyId.js";
2
+
3
+ function getFilterLookup(filters, primaryKey, secondaryKey) {
4
+ const filterLookup = new Map();
5
+ for (const cell of filters) {
6
+ const primary = stringifyId(cell[primaryKey]);
7
+ const secondary = stringifyId(cell[secondaryKey]);
8
+
9
+ if (!filterLookup.has(primary))
10
+ filterLookup.set(primary, new Map());
11
+
12
+ filterLookup.get(primary).set(secondary, cell.expression);
13
+ }
14
+ return filterLookup;
15
+ }
16
+
17
+ export function getFilteredRows(filters, filteringRules, formattingRules, data, rows, columns, edition) {
18
+ if (filters.length === 0)
19
+ return rows;
20
+
21
+ const filterLookup = getFilterLookup(filters, 'columnId', 'rowId');
22
+ const filteredColumns = columns.filter(column => column.type !== 'FILTER' && filterLookup.has(column.key));
23
+
24
+ if (filteredColumns.length === 0)
25
+ return rows;
26
+
27
+ return rows.filter(row => {
28
+ for (const column of filteredColumns) {
29
+ const cell = formattingRules.resolve(data, rows, columns, row, column, edition);
30
+ const columnFilters = filterLookup.get(column.key);
31
+ const visible = filteringRules.resolve(data, rows, columns, row, column, cell.value, cell.text, columnFilters);
32
+
33
+ if (!visible)
34
+ return false;
35
+ }
36
+ return true;
37
+ });
38
+ }
39
+
40
+ export function getFilteredColumns(filters, filteringRules, formattingRules, data, rows, columns, edition) {
41
+ if (filters.length === 0)
42
+ return columns;
43
+
44
+ const filterLookup = getFilterLookup(filters, 'rowId', 'columnId');
45
+ const filteredRows = rows.filter(row => row.type !== 'FILTER' && filterLookup.has(row.key));
46
+
47
+ if (filteredRows.length === 0)
48
+ return columns;
49
+
50
+ return columns.filter(column => {
51
+ for (const row of filteredRows) {
52
+ const cell = formattingRules.resolve(data, rows, columns, row, column, edition);
53
+ const rowFilters = filterLookup.get(row.key);
54
+ const visible = filteringRules.resolve(data, rows, columns, row, column, cell.value, cell.text, rowFilters);
55
+
56
+ if (!visible)
57
+ return false;
58
+ }
59
+ return true;
60
+ });
61
+ }
@@ -0,0 +1,5 @@
1
+ import FilteringRules from "../types/FilteringRules.js";
2
+
3
+ export default function getFilteringRules(filtering) {
4
+ return new FilteringRules(filtering);
5
+ }
@@ -0,0 +1,8 @@
1
+ export default function getFixedSize(top, bottom, left, right) {
2
+ return {
3
+ top: top,
4
+ bottom: bottom,
5
+ left: left,
6
+ right: right
7
+ };
8
+ }
@@ -0,0 +1,5 @@
1
+ import FormatResolver from "../types/FormatResolver.js";
2
+
3
+ export default function getFormatResolver(formattingRules, data, rows, columns, edition) {
4
+ return new FormatResolver(formattingRules, data, rows, columns, edition);
5
+ }
@@ -0,0 +1,5 @@
1
+ import FormattingRules from "../types/FormattingRules.js";
2
+
3
+ export default function getFormattingRules(dataFormatting) {
4
+ return new FormattingRules(dataFormatting);
5
+ }
@@ -0,0 +1,40 @@
1
+ import stringifyId from "../core-utils/stringifyId.js";
2
+
3
+ export default function getHighlightedCells(isMouseDown, isResizing, focusedCell, hoveredCell, columns, rows, columnLookup, rowLookup) {
4
+ if (!isMouseDown)
5
+ return [];
6
+ if (isResizing)
7
+ return [];
8
+ if (!hoveredCell)
9
+ return [];
10
+ if (!focusedCell)
11
+ return [];
12
+
13
+ const focusedColumnKey = stringifyId(focusedCell.columnId);
14
+ const focusedRowKey = stringifyId(focusedCell.rowId);
15
+ const hoveredColumnKey = stringifyId(hoveredCell.columnId);
16
+ const hoveredRowKey = stringifyId(hoveredCell.rowId);
17
+
18
+ if (!columnLookup.has(focusedColumnKey))
19
+ return [];
20
+ if (!rowLookup.has(focusedRowKey))
21
+ return [];
22
+ if (!columnLookup.has(hoveredColumnKey))
23
+ return [];
24
+ if (!rowLookup.has(hoveredRowKey))
25
+ return [];
26
+
27
+ const minColumnIndex = Math.min(columnLookup.get(focusedColumnKey).index, columnLookup.get(hoveredColumnKey).index);
28
+ const maxColumnIndex = Math.max(columnLookup.get(focusedColumnKey).index, columnLookup.get(hoveredColumnKey).index);
29
+ const minRowIndex = Math.min(rowLookup.get(focusedRowKey).index, rowLookup.get(hoveredRowKey).index);
30
+ const maxRowIndex = Math.max(rowLookup.get(focusedRowKey).index, rowLookup.get(hoveredRowKey).index);
31
+
32
+ return columns.slice(minColumnIndex, maxColumnIndex + 1).flatMap(column => {
33
+ return rows.slice(minRowIndex, maxRowIndex + 1).map(row => {
34
+ return {
35
+ rowId: row.id,
36
+ columnId: column.id
37
+ };
38
+ });
39
+ });
40
+ }
@@ -0,0 +1,43 @@
1
+ import getColumnIndex from "./getColumnIndex.js";
2
+ import getRowIndex from "./getRowIndex.js";
3
+
4
+ export default function getHoveredCell(element, mousePosition, rows, columns, fixedSize, totalSize) {
5
+ // TODO: sometimes mousePosition is outside of bounds and it crashes the click events
6
+
7
+ if (!mousePosition)
8
+ return null;
9
+ if (mousePosition.x < 0 || mousePosition.y < 0 || mousePosition.x > totalSize.width || mousePosition.y > totalSize.height)
10
+ return null;
11
+
12
+ const scrollOffset = {
13
+ left: element.scrollLeft,
14
+ top: element.scrollTop
15
+ };
16
+
17
+ const clientSize = {
18
+ width: element.clientWidth,
19
+ height: element.clientHeight
20
+ };
21
+
22
+ const x = mousePosition.x <= fixedSize.left
23
+ ? mousePosition.x
24
+ : mousePosition.x >= clientSize.width - fixedSize.right
25
+ ? totalSize.width - clientSize.width + mousePosition.x
26
+ : mousePosition.x + scrollOffset.left;
27
+ const y = mousePosition.y <= fixedSize.top
28
+ ? mousePosition.y
29
+ : mousePosition.y >= clientSize.height - fixedSize.bottom
30
+ ? totalSize.height - clientSize.height + mousePosition.y
31
+ : mousePosition.y + scrollOffset.top;
32
+
33
+ const hoverRowIndex = getRowIndex(rows, y);
34
+ const hoverColumnIndex = getColumnIndex(columns, x);
35
+
36
+ if (hoverRowIndex === -1 || hoverColumnIndex === -1)
37
+ return null;
38
+
39
+ return {
40
+ rowId: rows[hoverRowIndex].id,
41
+ columnId: columns[hoverColumnIndex].id
42
+ };
43
+ }
@@ -0,0 +1,3 @@
1
+ export default function getInputFormatting(formatting) {
2
+ return formatting;
3
+ }
@@ -0,0 +1,51 @@
1
+ import stringifyId from "../core-utils/stringifyId.js";
2
+ import getCellSection from "./getCellSection.js";
3
+
4
+ export default function getInputPlacement(editableCells, focusedCell, columnLookup, rowLookup, sections) {
5
+ if (!focusedCell)
6
+ return null;
7
+
8
+ const focusedColumnKey = stringifyId(focusedCell.columnId);
9
+ const focusedRowKey = stringifyId(focusedCell.rowId);
10
+
11
+ if (!columnLookup.has(focusedColumnKey))
12
+ return null;
13
+ if (!rowLookup.has(focusedRowKey))
14
+ return null;
15
+
16
+ const column = columnLookup.get(focusedColumnKey);
17
+ const row = rowLookup.get(focusedRowKey);
18
+
19
+ if (editableCells.length === 0)
20
+ return null;
21
+
22
+ const position = {
23
+ width: column.width,
24
+ height: row.height,
25
+ section: getCellSection(column, row)
26
+ };
27
+
28
+ switch (row.pinned) {
29
+ case "BEGIN":
30
+ position.top = row.top;
31
+ break;
32
+ case "END":
33
+ position.bottom = sections.top.height + sections.middle.height + sections.bottom.height - row.top - row.height;
34
+ break;
35
+ default:
36
+ position.marginTop = row.top - sections.top.height;
37
+ }
38
+
39
+ switch (column.pinned) {
40
+ case "BEGIN":
41
+ position.left = column.left;
42
+ break;
43
+ case "END":
44
+ position.right = sections.left.width + sections.center.width + sections.right.width - column.left - column.width;
45
+ break;
46
+ default:
47
+ position.marginLeft = column.left - sections.left.width;
48
+ }
49
+
50
+ return position;
51
+ }
@@ -0,0 +1,5 @@
1
+ export default function getInvoked(columnsOrRows, data) {
2
+ return typeof columnsOrRows === 'function'
3
+ ? columnsOrRows(data)
4
+ : columnsOrRows;
5
+ }
@@ -0,0 +1,3 @@
1
+ export default function getIsTextValid(text, editableCells) {
2
+ return editableCells.every(cell => cell.edit.validate({ string: text }));
3
+ }
@@ -0,0 +1,3 @@
1
+ export default function getKeys(entries) {
2
+ return new Set(entries.map(entry => entry.key));
3
+ }
@@ -0,0 +1,3 @@
1
+ export default function getLookup(elements) {
2
+ return elements.reduce((lookup, element) => lookup.set(element.key, element), new Map());
3
+ }
@@ -0,0 +1,3 @@
1
+ export default function getMeasureFormatting(formatting) {
2
+ return formatting;
3
+ }
@@ -0,0 +1,113 @@
1
+ import { defaultPadding } from "../core-utils/defaults.js";
2
+
3
+ // TODO: add expand and expand-data
4
+
5
+ export function getMeasuredColumns(columns, rows, textResolver, formatResolver, measuringCache, presentKeys) {
6
+ if (columns.every(column => typeof column.width === 'number'))
7
+ return columns;
8
+
9
+ const measureColumn = column => {
10
+ const requestedWidth = 'width' in column ? column.width : 'fit-once';
11
+
12
+ if (typeof requestedWidth === 'number')
13
+ return requestedWidth;
14
+
15
+ if (measuringCache.has(column.key)) {
16
+ const cached = measuringCache.get(column.key);
17
+
18
+ if (requestedWidth === 'fit-once' && !cached.dataOnly)
19
+ return cached.width;
20
+ if (requestedWidth === 'fit-data-once' && cached.dataOnly)
21
+ return cached.width;
22
+ }
23
+
24
+ let width = 0;
25
+ for (const row of rows) {
26
+ if (row.type !== 'DATA' && requestedWidth === 'fit-data-once')
27
+ continue;
28
+ if (row.type !== 'DATA' && requestedWidth === 'fit-data')
29
+ continue;
30
+
31
+ const cell = formatResolver.resolve(row, column);
32
+ const text = cell.text;
33
+ const font = cell.font;
34
+ const padding = cell.padding.left + cell.padding.right;
35
+
36
+ const cellWidth = textResolver.measureWidth(text, font) + padding;
37
+
38
+ width = Math.max(width, cellWidth);
39
+ }
40
+
41
+ measuringCache.set(column.key, {
42
+ width,
43
+ dataOnly: requestedWidth === 'fit-data-once'
44
+ });
45
+
46
+ return width;
47
+ }
48
+
49
+ for (const key of measuringCache.keys()) {
50
+ if (!presentKeys.has(key))
51
+ measuringCache.delete(key);
52
+ }
53
+
54
+ return columns.map(column => ({
55
+ ...column,
56
+ width: measureColumn(column)
57
+ }));
58
+ }
59
+
60
+ export function getMeasuredRows(columns, rows, textResolver, formatResolver, measuringCache, presentKeys) {
61
+ if (rows.every(row => typeof row.height === 'number'))
62
+ return rows;
63
+
64
+ const measureRow = row => {
65
+ const requestedHeight = 'height' in row ? row.height : 'fit-once';
66
+
67
+ if (typeof requestedHeight === 'number')
68
+ return requestedHeight;
69
+
70
+ if (measuringCache.has(row.key)) {
71
+ const cached = measuringCache.get(row.key);
72
+
73
+ if (requestedHeight === 'fit-once' && !cached.dataOnly)
74
+ return cached.height;
75
+ if (requestedHeight === 'fit-data-once' && cached.dataOnly)
76
+ return cached.height;
77
+ }
78
+
79
+ let height = 0;
80
+ for (const column of columns) {
81
+ if (column.type !== 'DATA' && requestedHeight === 'fit-data-once')
82
+ continue;
83
+ if (column.type !== 'DATA' && requestedHeight === 'fit-data')
84
+ continue;
85
+
86
+ const cell = formatResolver.resolve(row, column);
87
+ const text = cell.text;
88
+ const font = cell.font;
89
+ const padding = cell.padding.top + cell.padding.bottom;
90
+
91
+ const cellHeight = textResolver.measureHeight(text, font) + padding;
92
+
93
+ height = Math.max(height, cellHeight);
94
+ }
95
+
96
+ measuringCache.set(row.key, {
97
+ height,
98
+ dataOnly: requestedHeight === 'fit-data-once'
99
+ });
100
+
101
+ return height;
102
+ }
103
+
104
+ for (const key of measuringCache.keys()) {
105
+ if (!presentKeys.has(key))
106
+ measuringCache.delete(key);
107
+ }
108
+
109
+ return rows.map(row => ({
110
+ ...row,
111
+ height: measureRow(row)
112
+ }));
113
+ }
@@ -0,0 +1,8 @@
1
+ export default function getMousePosition(event) {
2
+ const element = event.currentTarget;
3
+ const rect = element.getBoundingClientRect();
4
+ return {
5
+ x: event.clientX - rect.left,
6
+ y: event.clientY - rect.top
7
+ };
8
+ }
@@ -0,0 +1,20 @@
1
+ import stringifyId from '../core-utils/stringifyId.js';
2
+
3
+ export default function getNewSortBy(sortBy, column, row, ctrlKey) {
4
+ // TODO: better support for multi-row sorting
5
+
6
+ function isCurrentRule(rule) {
7
+ return column.key === stringifyId(rule.columnId || 'HEADER') && row.key === stringifyId(rule.rowId || 'HEADER');
8
+ }
9
+
10
+ const directionLoop = ['ASC', 'DESC', undefined];
11
+ const currentRule = sortBy.find(isCurrentRule);
12
+ const directionIndex = directionLoop.indexOf(currentRule?.direction);
13
+ const newDirection = directionLoop[(directionIndex + 1) % directionLoop.length];
14
+ const isLastRule = sortBy.indexOf(currentRule) === sortBy.length - 1;
15
+ const shouldKeepOld = ctrlKey && (isLastRule || !currentRule);
16
+ const rulesToKeep = shouldKeepOld ? sortBy.filter(rule => !isCurrentRule(rule)) : [];
17
+ const newRules = newDirection ? [{ columnId: column.id, rowId: row.id, direction: newDirection }] : [];
18
+
19
+ return [...rulesToKeep, ...newRules];
20
+ }
@@ -0,0 +1,8 @@
1
+ // TODO: Move
2
+ export default function getPinned(index, length, pinnedBegin, pinnedEnd) {
3
+ if (index < pinnedBegin)
4
+ return "BEGIN";
5
+ if (index >= length - pinnedEnd)
6
+ return "END";
7
+ return undefined;
8
+ }
@@ -0,0 +1,45 @@
1
+ import roundToPixels from "../core-utils/roundToPixels.js";
2
+
3
+ export function getPlacedColumns(columns, devicePixelRatio, borderWidth) {
4
+ let left = borderWidth;
5
+
6
+ return columns.map((column, index) => {
7
+ const assignedWidth = 'width' in column ? column.width : 100;
8
+ const width = roundToPixels(assignedWidth, devicePixelRatio);
9
+ const newColumn = {
10
+ ...column,
11
+ index: index,
12
+ width: width,
13
+ leftWithBorder: left - borderWidth,
14
+ left: left,
15
+ right: left + width,
16
+ rightWithBorder: left + width + borderWidth
17
+ };
18
+
19
+ left += newColumn.width + borderWidth;
20
+
21
+ return newColumn;
22
+ });
23
+ }
24
+
25
+ export function getPlacedRows(rows, devicePixelRatio, borderWidth) {
26
+ let top = borderWidth;
27
+
28
+ return rows.map((row, index) => {
29
+ const assignedHeight = 'height' in row ? row.height : 20;
30
+ const height = roundToPixels(assignedHeight, devicePixelRatio);
31
+ const newRow = {
32
+ ...row,
33
+ index: index,
34
+ height: height,
35
+ topWithBorder: top - borderWidth,
36
+ top: top,
37
+ bottom: top + height,
38
+ bottomWithBorder: top + height + borderWidth
39
+ };
40
+
41
+ top += newRow.height + borderWidth;
42
+
43
+ return newRow;
44
+ });
45
+ }
@@ -0,0 +1,6 @@
1
+ import Selection from "../types/Selection.js";
2
+
3
+ export default function getReducedCells(previousCells, cellsToRemove) {
4
+ const selection = new Selection(cellsToRemove);
5
+ return previousCells.filter(cell => !selection.isIdSelected(cell.rowId, cell.columnId));
6
+ }