@revolist/revogrid 4.22.1 → 4.23.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.
Files changed (120) hide show
  1. package/dist/cjs/{cell-renderer-BQdEGQXP.js → cell-renderer-DWJ9Px9f.js} +9 -3
  2. package/dist/cjs/{column.drag.plugin-RDjQhKCH.js → column.drag.plugin-CaEBDG-Q.js} +391 -256
  3. package/dist/cjs/{column.service-DXYMehqK.js → column.service-f612L4ql.js} +1 -1
  4. package/dist/cjs/{dimension.helpers-CiiNnlLa.js → dimension.helpers-B9HgANnM.js} +14 -145
  5. package/dist/cjs/{edit.utils-CecCfA4E.js → edit.utils-pKeiYFLJ.js} +1 -1
  6. package/dist/cjs/{header-cell-renderer-DGyBrK8I.js → header-cell-renderer-4yq9_WbM.js} +1 -1
  7. package/dist/cjs/index-DxaSE5uZ.js +136 -0
  8. package/dist/cjs/index.cjs.js +37 -32
  9. package/dist/cjs/revo-grid.cjs.entry.js +35 -15
  10. package/dist/cjs/revogr-attribution_7.cjs.entry.js +43 -25
  11. package/dist/cjs/revogr-clipboard_3.cjs.entry.js +10 -8
  12. package/dist/cjs/revogr-data_4.cjs.entry.js +26 -17
  13. package/dist/cjs/revogr-filter-panel.cjs.entry.js +2 -1
  14. package/dist/cjs/{text-editor-DnLZW1a-.js → text-editor-B4W-m-r-.js} +3 -3
  15. package/dist/cjs/{throttle-CfgQFkfR.js → throttle-BCwEuJJq.js} +59 -24
  16. package/dist/cjs/viewport.helpers-BND76K2j.js +140 -0
  17. package/dist/cjs/{viewport.store-q6YdR9mg.js → viewport.store-BlKQ4x9H.js} +16 -16
  18. package/dist/collection/components/data/revogr-data.js +5 -3
  19. package/dist/collection/components/header/header-group-renderer.js +1 -1
  20. package/dist/collection/components/header/header-renderer.js +1 -1
  21. package/dist/collection/components/header/revogr-header-style.css +13 -3
  22. package/dist/collection/components/header/revogr-header.js +5 -2
  23. package/dist/collection/components/order/order-row.service.js +6 -5
  24. package/dist/collection/components/overlay/keyboard.service.js +23 -1
  25. package/dist/collection/components/overlay/selection.utils.js +8 -6
  26. package/dist/collection/components/revoGrid/revo-grid.js +6 -5
  27. package/dist/collection/components/revoGrid/viewport.service.js +2 -1
  28. package/dist/collection/components/scroll/revogr-viewport-scroll.js +10 -6
  29. package/dist/collection/components/scrollable/revogr-scroll-virtual.js +4 -10
  30. package/dist/collection/plugins/filter/filter.panel.js +2 -1
  31. package/dist/collection/plugins/filter/filter.plugin.js +11 -4
  32. package/dist/collection/plugins/groupingRow/grouping.row.plugin.js +25 -1
  33. package/dist/collection/plugins/moveColumn/column.drag.plugin.js +4 -4
  34. package/dist/collection/plugins/sorting/sorting.func.js +173 -15
  35. package/dist/collection/plugins/sorting/sorting.plugin.js +167 -84
  36. package/dist/collection/plugins/sorting/sorting.sign.js +7 -1
  37. package/dist/collection/serve/controller.js +98 -37
  38. package/dist/collection/serve/data.js +273 -144
  39. package/dist/collection/services/dimension.provider.js +16 -1
  40. package/dist/collection/services/local.scroll.service.js +59 -24
  41. package/dist/collection/services/scroll.dimension.helpers.js +83 -0
  42. package/dist/collection/services/selection.store.connector.js +4 -1
  43. package/dist/collection/store/dimension/dimension.recalculate.plugin.js +22 -9
  44. package/dist/collection/store/dimension/dimension.store.js +4 -2
  45. package/dist/collection/store/vp/viewport.helpers.js +9 -0
  46. package/dist/collection/store/vp/viewport.store.js +5 -16
  47. package/dist/collection/utils/store.utils.js +3 -3
  48. package/dist/{revo-grid/cell-renderer-CALsEsnh.js → esm/cell-renderer-8UiGd-s7.js} +9 -3
  49. package/dist/esm/{column.drag.plugin-Dy5ztusn.js → column.drag.plugin-BsfhsfmB.js} +388 -255
  50. package/dist/esm/{column.service-CCvAi5l4.js → column.service-DbpulTog.js} +1 -1
  51. package/dist/{revo-grid/debounce-BfO9dz9v.js → esm/debounce-PCRWZliA.js} +1 -1
  52. package/dist/{revo-grid/dimension.helpers-DmIvjIa7.js → esm/dimension.helpers-CGKwSvw6.js} +7 -128
  53. package/dist/esm/{edit.utils-DYN6XZh8.js → edit.utils-Dnnbd0xG.js} +1 -1
  54. package/dist/{revo-grid/header-cell-renderer-DU8wKAbg.js → esm/header-cell-renderer-DGI2FAD8.js} +1 -1
  55. package/dist/esm/index-Db3qZoW5.js +127 -0
  56. package/dist/esm/index.js +11 -10
  57. package/dist/esm/revo-grid.entry.js +34 -14
  58. package/dist/esm/revogr-attribution_7.entry.js +42 -24
  59. package/dist/esm/revogr-clipboard_3.entry.js +11 -9
  60. package/dist/esm/revogr-data_4.entry.js +27 -18
  61. package/dist/esm/revogr-filter-panel.entry.js +3 -2
  62. package/dist/esm/{text-editor-DpCnd6Fq.js → text-editor-C3RUSwH5.js} +2 -2
  63. package/dist/esm/{throttle-ERvyruXb.js → throttle-CaUDyxyU.js} +60 -25
  64. package/dist/esm/viewport.helpers-CoCAvmZs.js +133 -0
  65. package/dist/{revo-grid/viewport.store-CFjDW-3l.js → esm/viewport.store-COAfzAyu.js} +15 -17
  66. package/dist/{esm/cell-renderer-CALsEsnh.js → revo-grid/cell-renderer-8UiGd-s7.js} +9 -3
  67. package/dist/revo-grid/{column.drag.plugin-Dy5ztusn.js → column.drag.plugin-BsfhsfmB.js} +388 -255
  68. package/dist/revo-grid/{column.service-CCvAi5l4.js → column.service-DbpulTog.js} +1 -1
  69. package/dist/{esm/debounce-BfO9dz9v.js → revo-grid/debounce-PCRWZliA.js} +1 -1
  70. package/dist/{esm/dimension.helpers-DmIvjIa7.js → revo-grid/dimension.helpers-CGKwSvw6.js} +7 -128
  71. package/dist/revo-grid/{edit.utils-DYN6XZh8.js → edit.utils-Dnnbd0xG.js} +1 -1
  72. package/dist/{esm/header-cell-renderer-DU8wKAbg.js → revo-grid/header-cell-renderer-DGI2FAD8.js} +1 -1
  73. package/dist/revo-grid/index-Db3qZoW5.js +127 -0
  74. package/dist/revo-grid/index.esm.js +11 -10
  75. package/dist/revo-grid/revo-grid.entry.js +34 -14
  76. package/dist/revo-grid/revogr-attribution_7.entry.js +42 -24
  77. package/dist/revo-grid/revogr-clipboard_3.entry.js +11 -9
  78. package/dist/revo-grid/revogr-data_4.entry.js +27 -18
  79. package/dist/revo-grid/revogr-filter-panel.entry.js +3 -2
  80. package/dist/revo-grid/{text-editor-DpCnd6Fq.js → text-editor-C3RUSwH5.js} +2 -2
  81. package/dist/revo-grid/{throttle-ERvyruXb.js → throttle-CaUDyxyU.js} +60 -25
  82. package/dist/revo-grid/viewport.helpers-CoCAvmZs.js +133 -0
  83. package/dist/{esm/viewport.store-CFjDW-3l.js → revo-grid/viewport.store-COAfzAyu.js} +15 -17
  84. package/dist/types/components/header/header-group-renderer.d.ts +1 -0
  85. package/dist/types/components/header/header-renderer.d.ts +1 -0
  86. package/dist/types/components/overlay/keyboard.service.d.ts +5 -0
  87. package/dist/types/plugins/groupingRow/grouping.row.plugin.d.ts +8 -0
  88. package/dist/types/plugins/sorting/sorting.func.d.ts +25 -2
  89. package/dist/types/plugins/sorting/sorting.plugin.d.ts +84 -9
  90. package/dist/types/plugins/sorting/sorting.sign.d.ts +5 -1
  91. package/dist/types/plugins/sorting/sorting.types.d.ts +46 -1
  92. package/dist/types/services/local.scroll.service.d.ts +10 -2
  93. package/dist/types/services/scroll.dimension.helpers.d.ts +20 -0
  94. package/dist/types/store/vp/viewport.helpers.d.ts +2 -0
  95. package/dist/types/types/interfaces.d.ts +11 -0
  96. package/hydrate/index.js +649 -365
  97. package/hydrate/index.mjs +649 -365
  98. package/package.json +1 -1
  99. package/standalone/column.service.js +1 -1
  100. package/standalone/data.store.js +1 -1
  101. package/standalone/debounce.js +1 -1
  102. package/standalone/dimension.helpers.js +1 -1
  103. package/standalone/index.js +1 -1
  104. package/standalone/local.scroll.timer.js +1 -1
  105. package/standalone/revo-grid.js +1 -1
  106. package/standalone/revogr-data2.js +1 -1
  107. package/standalone/revogr-filter-panel.js +1 -1
  108. package/standalone/revogr-header2.js +1 -1
  109. package/standalone/revogr-order-editor2.js +1 -1
  110. package/standalone/revogr-overlay-selection2.js +1 -1
  111. package/standalone/revogr-row-headers.js +1 -1
  112. package/standalone/revogr-row-headers2.js +1 -1
  113. package/standalone/revogr-scroll-virtual2.js +1 -1
  114. package/standalone/revogr-viewport-scroll2.js +1 -1
  115. package/standalone/selection.utils.js +1 -1
  116. package/standalone/throttle.js +1 -1
  117. package/standalone/toNumber.js +1 -1
  118. package/dist/cjs/viewport.helpers-BAovztDd.js +0 -58
  119. package/dist/esm/viewport.helpers-VXhsJZtn.js +0 -52
  120. package/dist/revo-grid/viewport.helpers-VXhsJZtn.js +0 -52
@@ -1,13 +1,177 @@
1
1
  /*!
2
2
  * Built by Revolist OU ❤️
3
3
  */
4
+ import { GROUP_COLUMN_PROP } from "../groupingRow/grouping.const";
4
5
  import { isGrouping } from "../groupingRow/grouping.service";
5
6
  import { getCellRaw } from "../../utils/column.utils";
6
- export function sortIndexByItems(indexes, source, sortingFunc = {}) {
7
+ /**
8
+ * Checks whether a sorting map contains at least one active order.
9
+ *
10
+ * Empty maps and properties with `undefined` order are treated as inactive.
11
+ */
12
+ export function hasActiveSorting(sorting) {
13
+ for (const prop of Object.keys(sorting || {})) {
14
+ if (sorting === null || sorting === void 0 ? void 0 : sorting[prop]) {
15
+ return true;
16
+ }
17
+ }
18
+ return false;
19
+ }
20
+ /**
21
+ * Compares column properties after object-key coercion.
22
+ */
23
+ function isSameColumnProp(a, b) {
24
+ return String(a) === String(b);
25
+ }
26
+ /**
27
+ * Returns active sorting properties in explicit priority order.
28
+ */
29
+ function getActiveSortingProps(sorting, sortingOrder) {
30
+ const activeProps = [];
31
+ const add = (prop) => {
32
+ if ((sorting === null || sorting === void 0 ? void 0 : sorting[prop]) && !activeProps.some(active => isSameColumnProp(active, prop))) {
33
+ activeProps.push(prop);
34
+ }
35
+ };
36
+ sortingOrder === null || sortingOrder === void 0 ? void 0 : sortingOrder.forEach(add);
37
+ Object.keys(sorting || {}).forEach(add);
38
+ return activeProps;
39
+ }
40
+ /**
41
+ * Returns one-based additive sorting rank for a column.
42
+ *
43
+ * A single active sort does not need a visible rank, so it returns undefined.
44
+ */
45
+ export function getSortingIndex(sorting, prop, sortingOrder) {
46
+ const activeProps = getActiveSortingProps(sorting, sortingOrder);
47
+ if (activeProps.length <= 1) {
48
+ return undefined;
49
+ }
50
+ const index = activeProps.findIndex(active => isSameColumnProp(active, prop));
51
+ return index >= 0 ? index + 1 : undefined;
52
+ }
53
+ /**
54
+ * Collects only active comparator functions from a sorting function map.
55
+ *
56
+ * This keeps undefined comparator entries from triggering sorting work.
57
+ */
58
+ function activeSortingEntries(sortingFunc = {}, sortingOrder) {
59
+ const entries = [];
60
+ const add = (prop) => {
61
+ const cmp = sortingFunc[prop];
62
+ if (typeof cmp === 'function' && !entries.some(([active]) => isSameColumnProp(active, prop))) {
63
+ entries.push([prop, cmp]);
64
+ }
65
+ };
66
+ sortingOrder === null || sortingOrder === void 0 ? void 0 : sortingOrder.forEach(add);
67
+ Object.keys(sortingFunc).forEach(add);
68
+ return entries;
69
+ }
70
+ /**
71
+ * Reads and normalizes a value for the built-in default comparer.
72
+ */
73
+ function getDefaultCompareValue(item, prop, column) {
74
+ const aRaw = column ? getCellRaw(item, column) : item === null || item === void 0 ? void 0 : item[prop];
75
+ return typeof aRaw === 'number' ? aRaw : aRaw === null || aRaw === void 0 ? void 0 : aRaw.toString().toLowerCase();
76
+ }
77
+ function isEmptyCompareValue(value) {
78
+ return value === '' || value === null || value === undefined;
79
+ }
80
+ /**
81
+ * Compares two already-normalized default comparer values.
82
+ */
83
+ function compareValues(av, bv) {
84
+ if (av === bv) {
85
+ return 0;
86
+ }
87
+ const aEmpty = isEmptyCompareValue(av);
88
+ const bEmpty = isEmptyCompareValue(bv);
89
+ if (aEmpty || bEmpty) {
90
+ if (aEmpty && bEmpty) {
91
+ return 0;
92
+ }
93
+ return aEmpty ? -1 : 1;
94
+ }
95
+ if (av > bv) {
96
+ return 1;
97
+ }
98
+ return -1;
99
+ }
100
+ /**
101
+ * Sorts indexes by precomputed values for default column comparers.
102
+ *
103
+ * This avoids repeatedly parsing the same cell value during large default
104
+ * sorts while preserving normal multi-column ordering.
105
+ */
106
+ function sortIndexByDefaultComparers(indexes, source, entries, sorting, sortingColumns) {
107
+ const prepared = entries.map(([prop]) => {
108
+ const values = [];
109
+ const column = sortingColumns[prop];
110
+ for (const index of indexes) {
111
+ values[index] = getDefaultCompareValue(source[index], prop, column);
112
+ }
113
+ return {
114
+ order: sorting[prop],
115
+ values,
116
+ };
117
+ });
118
+ return indexes.sort((a, b) => {
119
+ for (const { order, values } of prepared) {
120
+ const sorted = compareValues(values[a], values[b]);
121
+ if (sorted) {
122
+ return order === 'desc' ? -sorted : sorted;
123
+ }
124
+ }
125
+ return 0;
126
+ });
127
+ }
128
+ /**
129
+ * Detects whether the optimized default-comparer path can be used.
130
+ *
131
+ * Grouped rows and custom `cellCompare` functions stay on the legacy
132
+ * comparator path to preserve their exact behavior.
133
+ */
134
+ function canUseDefaultCompareFastPath(entries, indexes, source, sorting, sortingColumns) {
135
+ return !indexes.some(index => isGrouping(source[index])) && !!sorting && !!sortingColumns && entries.every(([prop]) => {
136
+ const order = sorting[prop];
137
+ const column = sortingColumns[prop];
138
+ return !!order && !(column === null || column === void 0 ? void 0 : column.cellCompare);
139
+ });
140
+ }
141
+ /**
142
+ * Group placeholder rows are generated for their grouping column. If sorting is
143
+ * requested for another column, the grouped source must be unwrapped first.
144
+ */
145
+ function hasGroupingRowsForOtherSorting(entries, indexes, source) {
146
+ return indexes.some(index => {
147
+ const item = source[index];
148
+ return isGrouping(item) && !entries.some(([prop]) => isSameColumnProp(item[GROUP_COLUMN_PROP], prop));
149
+ });
150
+ }
151
+ /**
152
+ * Sorts row indexes against a source collection.
153
+ *
154
+ * @param indexes - Current proxy row indexes to sort.
155
+ * @param source - Full source collection addressed by the indexes.
156
+ * @param sortingFunc - Comparator functions by column property.
157
+ * @param sorting - Active sorting order by column property.
158
+ * @param sortingColumns - Column metadata by property for default-comparer optimization.
159
+ * @param sortingOrder - Active sorting priority in click/config insertion order.
160
+ * @returns Sorted proxy indexes. With no sorting function keys, returns source-order indexes.
161
+ */
162
+ export function sortIndexByItems(indexes, source, sortingFunc = {}, sorting, sortingColumns, sortingOrder) {
163
+ const hasSortingKeys = Object.keys(sortingFunc).length > 0;
164
+ const sortingEntries = activeSortingEntries(sortingFunc, sortingOrder);
7
165
  // if no sorting - return unsorted indexes
8
- if (Object.entries(sortingFunc).length === 0) {
166
+ if (sortingEntries.length === 0) {
9
167
  // Unsorted indexes
10
- return [...Array(indexes.length).keys()];
168
+ return hasSortingKeys ? indexes : [...new Array(indexes.length).keys()];
169
+ }
170
+ if (hasGroupingRowsForOtherSorting(sortingEntries, indexes, source)) {
171
+ return indexes;
172
+ }
173
+ if (canUseDefaultCompareFastPath(sortingEntries, indexes, source, sorting, sortingColumns)) {
174
+ return sortIndexByDefaultComparers(indexes, source, sortingEntries, sorting, sortingColumns);
11
175
  }
12
176
  //
13
177
  /**
@@ -17,15 +181,15 @@ export function sortIndexByItems(indexes, source, sortingFunc = {}) {
17
181
  return indexes.sort((a, b) => {
18
182
  const itemA = source[a];
19
183
  const itemB = source[b];
20
- for (const [prop, cmp] of Object.entries(sortingFunc)) {
184
+ for (const [prop, cmp] of sortingEntries) {
21
185
  if (isGrouping(itemA)) {
22
- if (itemA['__rvgr-prop'] !== prop) {
23
- return 0;
186
+ if (!isSameColumnProp(itemA[GROUP_COLUMN_PROP], prop)) {
187
+ return a - b;
24
188
  }
25
189
  }
26
190
  if (isGrouping(itemB)) {
27
- if (itemB['__rvgr-prop'] !== prop) {
28
- return 0;
191
+ if (!isSameColumnProp(itemB[GROUP_COLUMN_PROP], prop)) {
192
+ return a - b;
29
193
  }
30
194
  }
31
195
  /**
@@ -45,13 +209,7 @@ export function defaultCellCompare(prop, a, b) {
45
209
  const bRaw = this.column ? getCellRaw(b, this.column) : b === null || b === void 0 ? void 0 : b[prop];
46
210
  const av = typeof aRaw === 'number' ? aRaw : aRaw === null || aRaw === void 0 ? void 0 : aRaw.toString().toLowerCase();
47
211
  const bv = typeof bRaw === 'number' ? bRaw : bRaw === null || bRaw === void 0 ? void 0 : bRaw.toString().toLowerCase();
48
- if (av === bv) {
49
- return 0;
50
- }
51
- if (av > bv) {
52
- return 1;
53
- }
54
- return -1;
212
+ return compareValues(av, bv);
55
213
  }
56
214
  export function descCellCompare(cmp) {
57
215
  return (prop, a, b) => {
@@ -1,15 +1,30 @@
1
1
  /*!
2
2
  * Built by Revolist OU ❤️
3
3
  */
4
- import size from "lodash/size";
5
4
  import debounce from "lodash/debounce";
6
5
  import { BasePlugin } from "../base.plugin";
7
6
  import { getColumnByProp } from "../../utils/column.utils";
8
7
  import { columnTypes, rowTypes } from "../../store/index";
9
- import { getComparer, getNextOrder, sortIndexByItems } from "./sorting.func";
8
+ import { isGrouping } from "../groupingRow/grouping.service";
9
+ import { getComparer, getNextOrder, getSortingIndex, hasActiveSorting, sortIndexByItems, } from "./sorting.func";
10
10
  export * from './sorting.types';
11
11
  export * from './sorting.func';
12
12
  export * from './sorting.sign';
13
+ function getSortableRowIndexes(indexes, source) {
14
+ return indexes.filter(index => !isGrouping(source[index]));
15
+ }
16
+ function mergeSortedRowsWithGroups(indexes, source, sortedRows) {
17
+ if (sortedRows.length === indexes.length) {
18
+ return sortedRows;
19
+ }
20
+ let rowIndex = 0;
21
+ return indexes.map(index => {
22
+ if (isGrouping(source[index])) {
23
+ return index;
24
+ }
25
+ return sortedRows[rowIndex++];
26
+ });
27
+ }
13
28
  /**
14
29
  * Lifecycle
15
30
  * 1. @event `beforesorting` - Triggered when sorting just starts. Nothing has happened yet. This can be triggered from a column or from the source. If the type is from rows, the column will be undefined.
@@ -24,54 +39,37 @@ export class SortingPlugin extends BasePlugin {
24
39
  super(revogrid, providers);
25
40
  this.revogrid = revogrid;
26
41
  /**
27
- * Delayed sorting promise
42
+ * Delayed sorting promise registered in the grid render job queue.
28
43
  */
29
44
  this.sortingPromise = null;
30
45
  /**
31
- * We need to sort only so often
46
+ * Debounced sorting entry point.
47
+ *
48
+ * Sorting can be requested by column changes, source changes, and header
49
+ * clicks in quick succession, so the actual sort is delayed and coalesced.
32
50
  */
33
- this.postponeSort = debounce((order, comparison, ignoreViewportUpdate) => this.runSorting(order, comparison, ignoreViewportUpdate), 50);
34
- const setConfig = (cfg) => {
35
- var _a;
36
- if (cfg) {
37
- const sortingFunc = {};
38
- const order = {};
39
- (_a = cfg.columns) === null || _a === void 0 ? void 0 : _a.forEach(col => {
40
- sortingFunc[col.prop] = getComparer(col, col.order);
41
- order[col.prop] = col.order;
42
- });
43
- if (cfg.additive) {
44
- this.sorting = Object.assign(Object.assign({}, this.sorting), order);
45
- this.sortingFunc = Object.assign(Object.assign({}, this.sortingFunc), sortingFunc);
46
- }
47
- else {
48
- // // set sorting
49
- this.sorting = order;
50
- this.sortingFunc = sortingFunc;
51
- }
52
- }
53
- };
54
- setConfig(config);
51
+ this.postponeSort = debounce((order, comparison, sortingColumns, sortingOrder, ignoreViewportUpdate) => this.runSorting(order, comparison, sortingColumns, sortingOrder, ignoreViewportUpdate), 50);
52
+ this.applySortingConfig(config);
55
53
  this.addEventListener('sortingconfigchanged', ({ detail }) => {
56
54
  config = detail;
57
- setConfig(detail);
58
- this.startSorting(this.sorting, this.sortingFunc);
55
+ this.applySortingConfig(detail);
56
+ this.startSorting(this.sorting, this.sortingFunc, this.sortingColumns, this.sortingOrder);
59
57
  });
60
58
  this.addEventListener('beforeheaderrender', ({ detail, }) => {
61
59
  var _a;
62
60
  const { data: column } = detail;
63
61
  if (column.sortable) {
64
- detail.data = Object.assign(Object.assign({}, column), { order: (_a = this.sorting) === null || _a === void 0 ? void 0 : _a[column.prop] });
62
+ detail.data = Object.assign(Object.assign({}, column), { order: (_a = this.sorting) === null || _a === void 0 ? void 0 : _a[column.prop], sortIndex: getSortingIndex(this.sorting, column.prop, this.sortingOrder) });
65
63
  }
66
64
  });
67
65
  this.addEventListener('beforeanysource', ({ detail: { type }, }) => {
68
66
  // if sorting was provided - sort data
69
- if (!!this.sorting && this.sortingFunc) {
67
+ if (hasActiveSorting(this.sorting) && this.sortingFunc) {
70
68
  const event = this.emit('beforesourcesortingapply', { type, sorting: this.sorting });
71
69
  if (event.defaultPrevented) {
72
70
  return;
73
71
  }
74
- this.startSorting(this.sorting, this.sortingFunc);
72
+ this.startSorting(this.sorting, this.sortingFunc, this.sortingColumns, this.sortingOrder);
75
73
  }
76
74
  });
77
75
  this.addEventListener('aftercolumnsset', ({ detail: { order }, }) => {
@@ -81,13 +79,24 @@ export class SortingPlugin extends BasePlugin {
81
79
  }
82
80
  const columns = this.providers.column.getColumns();
83
81
  const sortingFunc = {};
82
+ const sortingColumns = {};
83
+ const sortingOrder = [];
84
+ const sorting = {};
84
85
  for (let prop in order) {
85
- const cmp = getComparer(getColumnByProp(columns, prop), order[prop]);
86
- sortingFunc[prop] = cmp;
86
+ if (order[prop]) {
87
+ const column = getColumnByProp(columns, prop);
88
+ const cmp = getComparer(column, order[prop]);
89
+ sorting[prop] = order[prop];
90
+ sortingFunc[prop] = cmp;
91
+ sortingColumns[prop] = column;
92
+ sortingOrder.push(prop);
93
+ }
87
94
  }
88
95
  // set sorting
89
- this.sorting = order;
90
- this.sortingFunc = order && sortingFunc;
96
+ this.sorting = hasActiveSorting(sorting) ? sorting : undefined;
97
+ this.sortingFunc = this.sorting ? sortingFunc : undefined;
98
+ this.sortingColumns = this.sorting ? sortingColumns : undefined;
99
+ this.sortingOrder = this.sorting ? sortingOrder : undefined;
91
100
  });
92
101
  this.addEventListener('beforeheaderclick', (e) => {
93
102
  var _a, _b, _c, _d;
@@ -101,23 +110,89 @@ export class SortingPlugin extends BasePlugin {
101
110
  });
102
111
  }
103
112
  /**
104
- * Entry point for sorting, waits for all delays, registers jobs
113
+ * Creates mutable sorting maps from current state when additive sorting is requested.
114
+ */
115
+ createSortingState(additive) {
116
+ var _a;
117
+ return {
118
+ sorting: additive ? Object.assign({}, this.sorting) : {},
119
+ sortingFunc: additive ? Object.assign({}, this.sortingFunc) : {},
120
+ sortingColumns: additive ? Object.assign({}, this.sortingColumns) : {},
121
+ sortingOrder: additive ? [...((_a = this.sortingOrder) !== null && _a !== void 0 ? _a : [])] : [],
122
+ };
123
+ }
124
+ /**
125
+ * Stores normalized sorting state, clearing inactive empty maps.
126
+ */
127
+ setSortingState({ sorting, sortingFunc, sortingColumns, sortingOrder, }) {
128
+ this.sorting = hasActiveSorting(sorting) ? sorting : undefined;
129
+ this.sortingFunc = this.sorting ? sortingFunc : undefined;
130
+ this.sortingColumns = this.sorting ? sortingColumns : undefined;
131
+ this.sortingOrder = this.sorting ? sortingOrder : undefined;
132
+ }
133
+ /**
134
+ * Adds or replaces a column in a sorting state.
105
135
  */
106
- startSorting(order, sortingFunc, ignoreViewportUpdate) {
136
+ setColumnSorting(state, prop, order, cmp, column) {
137
+ state.sorting[prop] = order;
138
+ state.sortingFunc[prop] = cmp;
139
+ state.sortingColumns[prop] = column;
140
+ if (!state.sortingOrder.some(sortingProp => String(sortingProp) === String(prop))) {
141
+ state.sortingOrder.push(prop);
142
+ }
143
+ }
144
+ /**
145
+ * Removes a column from a sorting state.
146
+ */
147
+ clearColumnSorting(state, prop) {
148
+ delete state.sorting[prop];
149
+ delete state.sortingFunc[prop];
150
+ delete state.sortingColumns[prop];
151
+ const index = state.sortingOrder.findIndex(sortingProp => String(sortingProp) === String(prop));
152
+ if (index >= 0) {
153
+ state.sortingOrder.splice(index, 1);
154
+ }
155
+ }
156
+ /**
157
+ * Normalizes external sorting configuration into internal order,
158
+ * comparator, and column metadata maps.
159
+ */
160
+ applySortingConfig(cfg) {
161
+ var _a;
162
+ if (!cfg) {
163
+ return;
164
+ }
165
+ const state = this.createSortingState(cfg.additive);
166
+ (_a = cfg.columns) === null || _a === void 0 ? void 0 : _a.forEach(col => {
167
+ if (col.order) {
168
+ this.setColumnSorting(state, col.prop, col.order, getComparer(col, col.order), col);
169
+ return;
170
+ }
171
+ this.clearColumnSorting(state, col.prop);
172
+ });
173
+ this.setSortingState(state);
174
+ }
175
+ startSorting(order, sortingFunc, sortingColumns, sortingOrder, ignoreViewportUpdate) {
107
176
  if (!this.sortingPromise) {
108
177
  // add job before render
109
178
  this.revogrid.jobsBeforeRender.push(new Promise(resolve => {
110
179
  this.sortingPromise = resolve;
111
180
  }));
112
181
  }
113
- this.postponeSort(order, sortingFunc, ignoreViewportUpdate);
182
+ if (typeof sortingColumns === 'boolean') {
183
+ this.postponeSort(order, sortingFunc, undefined, undefined, sortingColumns);
184
+ return;
185
+ }
186
+ this.postponeSort(order, sortingFunc, sortingColumns, sortingOrder, ignoreViewportUpdate);
114
187
  }
115
188
  /**
116
- * Apply sorting to data on header click
117
- * If additive - add to existing sorting, multiple columns can be sorted
189
+ * Applies sorting requested by a sortable header click.
190
+ *
191
+ * @param column - Column that initiated sorting.
192
+ * @param additive - If true, add/remove this column from the current multi-sort state.
118
193
  */
119
194
  headerclick(column, additive) {
120
- var _a, _b, _c;
195
+ var _a;
121
196
  const columnProp = column.prop;
122
197
  let order = getNextOrder((_a = this.sorting) === null || _a === void 0 ? void 0 : _a[columnProp]);
123
198
  const beforeEvent = this.emit('beforesorting', { column, order, additive });
@@ -135,52 +210,59 @@ export class SortingPlugin extends BasePlugin {
135
210
  return;
136
211
  }
137
212
  const cmp = getComparer(beforeApplyEvent.detail.column, beforeApplyEvent.detail.order);
138
- if (beforeApplyEvent.detail.additive && this.sorting) {
139
- const sorting = {};
140
- const sortingFunc = {};
141
- if (columnProp in sorting && size(sorting) > 1 && order === undefined) {
142
- delete sorting[columnProp];
143
- delete sortingFunc[columnProp];
144
- }
145
- else {
146
- sorting[columnProp] = order;
147
- sortingFunc[columnProp] = cmp;
148
- }
149
- this.sorting = Object.assign(Object.assign({}, this.sorting), sorting);
150
- // extend sorting function with new sorting for multiple columns sorting
151
- this.sortingFunc = Object.assign(Object.assign({}, this.sortingFunc), sortingFunc);
213
+ this.applyHeaderSorting(beforeApplyEvent.detail.column, beforeApplyEvent.detail.additive, order, cmp);
214
+ this.startSorting(this.sorting, this.sortingFunc, this.sortingColumns, this.sortingOrder);
215
+ }
216
+ /**
217
+ * Applies sorting state produced by a header click.
218
+ */
219
+ applyHeaderSorting(column, additive, order, cmp) {
220
+ if (!additive) {
221
+ this.setSortingState(order ? {
222
+ sorting: { [column.prop]: order },
223
+ sortingFunc: { [column.prop]: cmp },
224
+ sortingColumns: { [column.prop]: column },
225
+ sortingOrder: [column.prop],
226
+ } : this.createSortingState());
227
+ return;
228
+ }
229
+ const state = this.createSortingState(true);
230
+ if (order) {
231
+ this.setColumnSorting(state, column.prop, order, cmp, column);
152
232
  }
153
233
  else {
154
- if (order) {
155
- // reset sorting
156
- this.sorting = { [columnProp]: order };
157
- this.sortingFunc = { [columnProp]: cmp };
158
- }
159
- else {
160
- (_b = this.sorting) === null || _b === void 0 ? true : delete _b[columnProp];
161
- (_c = this.sortingFunc) === null || _c === void 0 ? true : delete _c[columnProp];
162
- }
234
+ this.clearColumnSorting(state, column.prop);
163
235
  }
164
- this.startSorting(this.sorting, this.sortingFunc);
236
+ this.setSortingState(state);
165
237
  }
166
- runSorting(order, comparison, ignoreViewportUpdate) {
167
- var _a;
168
- this.sort(order, comparison, undefined, ignoreViewportUpdate);
169
- (_a = this.sortingPromise) === null || _a === void 0 ? void 0 : _a.call(this);
238
+ runSorting(order, comparison, sortingColumns, sortingOrder, ignoreViewportUpdate) {
239
+ var _a, _b;
240
+ if (typeof sortingColumns === 'boolean') {
241
+ this.sort(order, comparison, undefined, undefined, undefined, sortingColumns);
242
+ (_a = this.sortingPromise) === null || _a === void 0 ? void 0 : _a.call(this);
243
+ this.sortingPromise = null;
244
+ return;
245
+ }
246
+ this.sort(order, comparison, sortingColumns, sortingOrder, undefined, ignoreViewportUpdate);
247
+ (_b = this.sortingPromise) === null || _b === void 0 ? void 0 : _b.call(this);
170
248
  this.sortingPromise = null;
171
249
  }
172
- /**
173
- * Sort items by sorting function
174
- * @requires proxyItems applied to row store
175
- * @requires source applied to row store
176
- *
177
- * @param sorting - per column sorting
178
- * @param data - this.stores['rgRow'].store.get('source')
179
- */
180
- sort(sorting, sortingFunc, types = rowTypes, ignoreViewportUpdate = false) {
250
+ sort(sorting, sortingFunc, sortingColumns, sortingOrder, types = rowTypes, ignoreViewportUpdate = false) {
251
+ let activeSortingColumns;
252
+ let activeSortingOrder;
253
+ let activeTypes = types;
254
+ let activeIgnoreViewportUpdate = ignoreViewportUpdate;
255
+ if (Array.isArray(sortingColumns)) {
256
+ activeTypes = sortingColumns;
257
+ activeIgnoreViewportUpdate = typeof sortingOrder === 'boolean' ? sortingOrder : false;
258
+ }
259
+ else {
260
+ activeSortingColumns = sortingColumns;
261
+ activeSortingOrder = Array.isArray(sortingOrder) ? sortingOrder : undefined;
262
+ }
181
263
  // if no sorting - reset
182
264
  if (!Object.keys(sorting || {}).length) {
183
- for (let type of types) {
265
+ for (let type of activeTypes) {
184
266
  const storeService = this.providers.data.stores[type];
185
267
  // row data
186
268
  const source = storeService.store.get('source');
@@ -189,26 +271,27 @@ export class SortingPlugin extends BasePlugin {
189
271
  // row indexes
190
272
  const newItemsOrder = Array.from({ length: source.length }, (_, i) => i); // recover indexes range(0, source.length)
191
273
  this.providers.dimension.updateSizesPositionByNewDataIndexes(type, newItemsOrder, proxyItems);
192
- storeService.setData({ proxyItems: newItemsOrder, source: [...source], });
274
+ storeService.setData({ proxyItems: newItemsOrder });
193
275
  }
194
276
  }
195
277
  else {
196
- for (let type of types) {
278
+ for (let type of activeTypes) {
197
279
  const storeService = this.providers.data.stores[type];
198
280
  // row data
199
281
  const source = storeService.store.get('source');
200
282
  // row indexes
201
283
  const proxyItems = storeService.store.get('proxyItems');
202
- const newItemsOrder = sortIndexByItems([...proxyItems], source, sortingFunc);
284
+ const sortItems = getSortableRowIndexes(proxyItems, source);
285
+ const sortedItems = sortIndexByItems([...sortItems], source, sortingFunc, sorting, activeSortingColumns, activeSortingOrder);
286
+ const newItemsOrder = mergeSortedRowsWithGroups(proxyItems, source, sortedItems);
203
287
  // take row indexes before trim applied and proxy items
204
288
  const prevItems = storeService.store.get('items');
205
289
  storeService.setData({
206
290
  proxyItems: newItemsOrder,
207
- source: [...source],
208
291
  });
209
292
  // take currently visible row indexes
210
293
  const newItems = storeService.store.get('items');
211
- if (!ignoreViewportUpdate) {
294
+ if (!activeIgnoreViewportUpdate) {
212
295
  this.providers.dimension
213
296
  .updateSizesPositionByNewDataIndexes(type, newItems, prevItems);
214
297
  }
@@ -2,7 +2,13 @@
2
2
  * Built by Revolist OU ❤️
3
3
  */
4
4
  import { h } from "@stencil/core";
5
+ /**
6
+ * Renders sorting direction and optional additive sorting rank.
7
+ */
5
8
  export const SortingSign = ({ column }) => {
6
9
  var _a;
7
- return h("i", { class: (_a = column === null || column === void 0 ? void 0 : column.order) !== null && _a !== void 0 ? _a : 'sort-off' });
10
+ const indicatorAttrs = { class: 'sort-indicator' };
11
+ const iconAttrs = { class: (_a = column === null || column === void 0 ? void 0 : column.order) !== null && _a !== void 0 ? _a : 'sort-off' };
12
+ const orderIndexAttrs = { class: 'sort-order-index' };
13
+ return (h("span", Object.assign({}, indicatorAttrs), h("i", Object.assign({}, iconAttrs)), (column === null || column === void 0 ? void 0 : column.sortIndex) ? (h("sup", Object.assign({}, orderIndexAttrs), column.sortIndex)) : null));
8
14
  };