handsontable 0.0.0-next-a4bd0c0-20250307 → 0.0.0-next-89e4f85-20250310

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.

Potentially problematic release.


This version of handsontable might be problematic. Click here for more details.

Files changed (36) hide show
  1. package/base.js +2 -2
  2. package/base.mjs +2 -2
  3. package/dist/handsontable.css +2 -2
  4. package/dist/handsontable.full.css +2 -2
  5. package/dist/handsontable.full.js +260 -227
  6. package/dist/handsontable.full.min.css +2 -2
  7. package/dist/handsontable.full.min.js +13 -13
  8. package/dist/handsontable.js +260 -227
  9. package/dist/handsontable.min.css +2 -2
  10. package/dist/handsontable.min.js +11 -11
  11. package/helpers/mixed.js +1 -1
  12. package/helpers/mixed.mjs +1 -1
  13. package/package.json +1 -1
  14. package/plugins/columnSorting/columnSorting.js +17 -1
  15. package/plugins/columnSorting/columnSorting.mjs +18 -2
  16. package/plugins/columnSorting/utils.js +14 -0
  17. package/plugins/columnSorting/utils.mjs +13 -0
  18. package/plugins/filters/conditionCollection.js +0 -26
  19. package/plugins/filters/conditionCollection.mjs +0 -26
  20. package/plugins/filters/filters.d.ts +2 -0
  21. package/plugins/filters/filters.js +77 -8
  22. package/plugins/filters/filters.mjs +77 -8
  23. package/plugins/mergeCells/mergeCells.js +2 -3
  24. package/plugins/mergeCells/mergeCells.mjs +2 -3
  25. package/plugins/multiColumnSorting/multiColumnSorting.js +1 -21
  26. package/plugins/multiColumnSorting/multiColumnSorting.mjs +1 -21
  27. package/plugins/undoRedo/actions/filters.js +2 -2
  28. package/plugins/undoRedo/actions/filters.mjs +2 -2
  29. package/styles/handsontable.css +2 -2
  30. package/styles/handsontable.min.css +2 -2
  31. package/styles/ht-theme-horizon.css +2 -2
  32. package/styles/ht-theme-horizon.min.css +2 -2
  33. package/styles/ht-theme-main.css +2 -2
  34. package/styles/ht-theme-main.min.css +2 -2
  35. package/plugins/multiColumnSorting/utils.js +0 -13
  36. package/plugins/multiColumnSorting/utils.mjs +0 -9
package/helpers/mixed.js CHANGED
@@ -134,7 +134,7 @@ const domMessages = {
134
134
  function _injectProductInfo(key, element) {
135
135
  const hasValidType = !isEmpty(key);
136
136
  const isNonCommercial = typeof key === 'string' && key.toLowerCase() === 'non-commercial-and-evaluation';
137
- const hotVersion = "0.0.0-next-a4bd0c0-20250307";
137
+ const hotVersion = "0.0.0-next-89e4f85-20250310";
138
138
  let keyValidityDate;
139
139
  let consoleMessageState = 'invalid';
140
140
  let domMessageState = 'invalid';
package/helpers/mixed.mjs CHANGED
@@ -124,7 +124,7 @@ const domMessages = {
124
124
  export function _injectProductInfo(key, element) {
125
125
  const hasValidType = !isEmpty(key);
126
126
  const isNonCommercial = typeof key === 'string' && key.toLowerCase() === 'non-commercial-and-evaluation';
127
- const hotVersion = "0.0.0-next-a4bd0c0-20250307";
127
+ const hotVersion = "0.0.0-next-89e4f85-20250310";
128
128
  let keyValidityDate;
129
129
  let consoleMessageState = 'invalid';
130
130
  let domMessageState = 'invalid';
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "url": "https://github.com/handsontable/handsontable/issues"
11
11
  },
12
12
  "author": "Handsoncode <hello@handsontable.com>",
13
- "version": "0.0.0-next-a4bd0c0-20250307",
13
+ "version": "0.0.0-next-89e4f85-20250310",
14
14
  "main": "index",
15
15
  "module": "index.mjs",
16
16
  "jsnext:main": "index.mjs",
@@ -35,6 +35,13 @@ const SHORTCUTS_GROUP = PLUGIN_KEY;
35
35
  _hooks.Hooks.getSingleton().register('beforeColumnSort');
36
36
  _hooks.Hooks.getSingleton().register('afterColumnSort');
37
37
 
38
+ /**
39
+ * Tracks the conflicts between `columnSorting` and `multiColumnSorting` options.
40
+ * Only one plugin can be enabled for Handsontable instance. Once one of them is enabled,
41
+ * the other should remain disabled even if it's set to `true`.
42
+ */
43
+ const pluginConflictsState = new WeakMap();
44
+
38
45
  // DIFF - MultiColumnSorting & ColumnSorting: changed configuration documentation.
39
46
  /**
40
47
  * @plugin ColumnSorting
@@ -143,9 +150,17 @@ class ColumnSorting extends _base.BasePlugin {
143
150
  */
144
151
  enablePlugin() {
145
152
  var _this = this;
153
+ if (pluginConflictsState.has(this.hot) && pluginConflictsState.get(this.hot) !== this.pluginKey) {
154
+ this.hot.updateSettings({
155
+ [this.pluginKey]: false
156
+ });
157
+ (0, _utils.warnAboutPluginsConflict)(pluginConflictsState.get(this.hot), this.pluginKey);
158
+ return;
159
+ }
146
160
  if (this.enabled) {
147
161
  return;
148
162
  }
163
+ pluginConflictsState.set(this.hot, this.pluginKey);
149
164
  this.columnStatesManager = new _columnStatesManager.ColumnStatesManager(this.hot, `${this.pluginKey}.sortingStates`);
150
165
  this.columnMetaCache = new _translations.PhysicalIndexToValueMap(physicalIndex => {
151
166
  let visualIndex = this.hot.toVisualColumn(physicalIndex);
@@ -190,6 +205,7 @@ class ColumnSorting extends _base.BasePlugin {
190
205
  }
191
206
  this.updateHeaderClasses(headerSpanElement);
192
207
  };
208
+ pluginConflictsState.delete(this.hot);
193
209
 
194
210
  // Changing header width and removing indicator.
195
211
  this.hot.addHook('afterGetColHeader', clearColHeader);
@@ -667,7 +683,7 @@ class ColumnSorting extends _base.BasePlugin {
667
683
  * @param {object} newSettings New settings object.
668
684
  */
669
685
  onUpdateSettings(newSettings) {
670
- super.onUpdateSettings();
686
+ super.onUpdateSettings(newSettings);
671
687
  if (this.columnMetaCache !== null) {
672
688
  // Column meta cache base on settings, thus we should re-init the map.
673
689
  this.columnMetaCache.init(this.hot.columnIndexMapper.getNumberOfIndexes());
@@ -18,7 +18,7 @@ import { IndexesSequence, PhysicalIndexToValueMap as IndexToValueMap } from "../
18
18
  import { Hooks } from "../../core/hooks/index.mjs";
19
19
  import { ColumnStatesManager } from "./columnStatesManager.mjs";
20
20
  import { EDITOR_EDIT_GROUP as SHORTCUTS_GROUP_EDITOR } from "../../shortcutContexts/index.mjs";
21
- import { HEADER_SPAN_CLASS, getNextSortOrder, areValidSortStates, getHeaderSpanElement, isFirstLevelColumnHeader, wasHeaderClickedProperly } from "./utils.mjs";
21
+ import { HEADER_SPAN_CLASS, getNextSortOrder, areValidSortStates, getHeaderSpanElement, isFirstLevelColumnHeader, wasHeaderClickedProperly, warnAboutPluginsConflict } from "./utils.mjs";
22
22
  import { getClassesToRemove, getClassesToAdd } from "./domHelpers.mjs";
23
23
  import { rootComparator } from "./rootComparator.mjs";
24
24
  import { registerRootComparator, sort } from "./sortService/index.mjs";
@@ -32,6 +32,13 @@ registerRootComparator(PLUGIN_KEY, rootComparator);
32
32
  Hooks.getSingleton().register('beforeColumnSort');
33
33
  Hooks.getSingleton().register('afterColumnSort');
34
34
 
35
+ /**
36
+ * Tracks the conflicts between `columnSorting` and `multiColumnSorting` options.
37
+ * Only one plugin can be enabled for Handsontable instance. Once one of them is enabled,
38
+ * the other should remain disabled even if it's set to `true`.
39
+ */
40
+ const pluginConflictsState = new WeakMap();
41
+
35
42
  // DIFF - MultiColumnSorting & ColumnSorting: changed configuration documentation.
36
43
  /**
37
44
  * @plugin ColumnSorting
@@ -140,9 +147,17 @@ export class ColumnSorting extends BasePlugin {
140
147
  */
141
148
  enablePlugin() {
142
149
  var _this = this;
150
+ if (pluginConflictsState.has(this.hot) && pluginConflictsState.get(this.hot) !== this.pluginKey) {
151
+ this.hot.updateSettings({
152
+ [this.pluginKey]: false
153
+ });
154
+ warnAboutPluginsConflict(pluginConflictsState.get(this.hot), this.pluginKey);
155
+ return;
156
+ }
143
157
  if (this.enabled) {
144
158
  return;
145
159
  }
160
+ pluginConflictsState.set(this.hot, this.pluginKey);
146
161
  this.columnStatesManager = new ColumnStatesManager(this.hot, `${this.pluginKey}.sortingStates`);
147
162
  this.columnMetaCache = new IndexToValueMap(physicalIndex => {
148
163
  let visualIndex = this.hot.toVisualColumn(physicalIndex);
@@ -187,6 +202,7 @@ export class ColumnSorting extends BasePlugin {
187
202
  }
188
203
  this.updateHeaderClasses(headerSpanElement);
189
204
  };
205
+ pluginConflictsState.delete(this.hot);
190
206
 
191
207
  // Changing header width and removing indicator.
192
208
  this.hot.addHook('afterGetColHeader', clearColHeader);
@@ -664,7 +680,7 @@ export class ColumnSorting extends BasePlugin {
664
680
  * @param {object} newSettings New settings object.
665
681
  */
666
682
  onUpdateSettings(newSettings) {
667
- super.onUpdateSettings();
683
+ super.onUpdateSettings(newSettings);
668
684
  if (this.columnMetaCache !== null) {
669
685
  // Column meta cache base on settings, thus we should re-init the map.
670
686
  this.columnMetaCache.init(this.hot.columnIndexMapper.getNumberOfIndexes());
@@ -6,6 +6,7 @@ exports.createDateTimeCompareFunction = createDateTimeCompareFunction;
6
6
  exports.getHeaderSpanElement = getHeaderSpanElement;
7
7
  exports.getNextSortOrder = getNextSortOrder;
8
8
  exports.isFirstLevelColumnHeader = isFirstLevelColumnHeader;
9
+ exports.warnAboutPluginsConflict = warnAboutPluginsConflict;
9
10
  exports.wasHeaderClickedProperly = wasHeaderClickedProperly;
10
11
  require("core-js/modules/es.set.difference.v2.js");
11
12
  require("core-js/modules/es.set.intersection.v2.js");
@@ -22,6 +23,8 @@ var _object = require("../../helpers/object");
22
23
  var _event = require("../../helpers/dom/event");
23
24
  var _mixed = require("../../helpers/mixed");
24
25
  var _sortService = require("./sortService");
26
+ var _console = require("../../helpers/console");
27
+ var _templateLiteralTag = require("../../helpers/templateLiteralTag");
25
28
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
26
29
  const ASC_SORT_STATE = exports.ASC_SORT_STATE = 'asc';
27
30
  const DESC_SORT_STATE = exports.DESC_SORT_STATE = 'desc';
@@ -173,4 +176,15 @@ function createDateTimeCompareFunction(sortOrder, format, columnPluginSettings)
173
176
  }
174
177
  return _sortService.DO_NOT_SWAP;
175
178
  };
179
+ }
180
+
181
+ /**
182
+ * Warn users about problems when using `columnSorting` and `multiColumnSorting` plugins simultaneously.
183
+ *
184
+ * @param {string} workingPlugin The plugin that will work.
185
+ * @param {string} disabledPlugin The plugin that will remain disabled.
186
+ */
187
+ function warnAboutPluginsConflict(workingPlugin, disabledPlugin) {
188
+ (0, _console.warn)((0, _templateLiteralTag.toSingleLine)`Plugins \`columnSorting\` and \`multiColumnSorting\` should not be enabled simultaneously.\x20
189
+ Only \`${workingPlugin}\` will work. The \`${disabledPlugin}\` plugin will remain disabled.`);
176
190
  }
@@ -13,6 +13,8 @@ import { isObject } from "../../helpers/object.mjs";
13
13
  import { isRightClick } from "../../helpers/dom/event.mjs";
14
14
  import { isEmpty } from "../../helpers/mixed.mjs";
15
15
  import { DO_NOT_SWAP, FIRST_BEFORE_SECOND, FIRST_AFTER_SECOND } from "./sortService/index.mjs";
16
+ import { warn } from "../../helpers/console.mjs";
17
+ import { toSingleLine } from "../../helpers/templateLiteralTag.mjs";
16
18
  export const ASC_SORT_STATE = 'asc';
17
19
  export const DESC_SORT_STATE = 'desc';
18
20
  export const HEADER_SPAN_CLASS = 'colHeader';
@@ -163,4 +165,15 @@ export function createDateTimeCompareFunction(sortOrder, format, columnPluginSet
163
165
  }
164
166
  return DO_NOT_SWAP;
165
167
  };
168
+ }
169
+
170
+ /**
171
+ * Warn users about problems when using `columnSorting` and `multiColumnSorting` plugins simultaneously.
172
+ *
173
+ * @param {string} workingPlugin The plugin that will work.
174
+ * @param {string} disabledPlugin The plugin that will remain disabled.
175
+ */
176
+ export function warnAboutPluginsConflict(workingPlugin, disabledPlugin) {
177
+ warn(toSingleLine`Plugins \`columnSorting\` and \`multiColumnSorting\` should not be enabled simultaneously.\x20
178
+ Only \`${workingPlugin}\` will work. The \`${disabledPlugin}\` plugin will remain disabled.`);
166
179
  }
@@ -49,13 +49,6 @@ class ConditionCollection {
49
49
  * @type {LinkedPhysicalIndexToValueMap}
50
50
  */
51
51
  _defineProperty(this, "filteringStates", new _translations.LinkedPhysicalIndexToValueMap());
52
- /**
53
- * Stores the previous state of the condition stack before the latest filter operation.
54
- * This is used in the `beforeFilter` plugin to allow performing the undo operation.
55
- *
56
- * @type {null|Array}
57
- */
58
- _defineProperty(this, "previousConditionStack", null);
59
52
  this.hot = hot;
60
53
  this.isMapRegistrable = isMapRegistrable;
61
54
  if (this.isMapRegistrable === true) {
@@ -124,13 +117,6 @@ class ConditionCollection {
124
117
  const localeForColumn = this.hot.getCellMeta(0, column).locale;
125
118
  const args = conditionDefinition.args.map(v => typeof v === 'string' ? v.toLocaleLowerCase(localeForColumn) : v);
126
119
  const name = conditionDefinition.name || conditionDefinition.command.key;
127
-
128
- // If there's no previous condition stack defined (which means the condition stack was not cleared after the
129
- // previous filter operation or that there was no filter operation performed yet), store the current conditions as
130
- // the previous condition stack.
131
- if (this.previousConditionStack === null) {
132
- this.setPreviousConditionStack(this.exportAllConditions());
133
- }
134
120
  this.runLocalHooks('beforeAdd', column);
135
121
  const columnType = this.getOperation(column);
136
122
  if (columnType) {
@@ -259,8 +245,6 @@ class ConditionCollection {
259
245
  * @fires ConditionCollection#afterRemove
260
246
  */
261
247
  removeConditions(column) {
262
- // Store the current conditions as the previous condition stack before it's cleared.
263
- this.setPreviousConditionStack(this.exportAllConditions());
264
248
  this.runLocalHooks('beforeRemove', column);
265
249
  this.filteringStates.clearValue(column);
266
250
  this.runLocalHooks('afterRemove', column);
@@ -294,16 +278,6 @@ class ConditionCollection {
294
278
  return conditions.length > 0;
295
279
  }
296
280
 
297
- /**
298
- * Updates the `previousConditionStack` property with the provided stack.
299
- * It is used to store the current conditions before they are modified, allowing for undo operations.
300
- *
301
- * @param {Array|null} previousConditionStack The stack of previous conditions.
302
- */
303
- setPreviousConditionStack(previousConditionStack) {
304
- this.previousConditionStack = previousConditionStack;
305
- }
306
-
307
281
  /**
308
282
  * Destroy object.
309
283
  */
@@ -45,13 +45,6 @@ class ConditionCollection {
45
45
  * @type {LinkedPhysicalIndexToValueMap}
46
46
  */
47
47
  _defineProperty(this, "filteringStates", new IndexToValueMap());
48
- /**
49
- * Stores the previous state of the condition stack before the latest filter operation.
50
- * This is used in the `beforeFilter` plugin to allow performing the undo operation.
51
- *
52
- * @type {null|Array}
53
- */
54
- _defineProperty(this, "previousConditionStack", null);
55
48
  this.hot = hot;
56
49
  this.isMapRegistrable = isMapRegistrable;
57
50
  if (this.isMapRegistrable === true) {
@@ -120,13 +113,6 @@ class ConditionCollection {
120
113
  const localeForColumn = this.hot.getCellMeta(0, column).locale;
121
114
  const args = conditionDefinition.args.map(v => typeof v === 'string' ? v.toLocaleLowerCase(localeForColumn) : v);
122
115
  const name = conditionDefinition.name || conditionDefinition.command.key;
123
-
124
- // If there's no previous condition stack defined (which means the condition stack was not cleared after the
125
- // previous filter operation or that there was no filter operation performed yet), store the current conditions as
126
- // the previous condition stack.
127
- if (this.previousConditionStack === null) {
128
- this.setPreviousConditionStack(this.exportAllConditions());
129
- }
130
116
  this.runLocalHooks('beforeAdd', column);
131
117
  const columnType = this.getOperation(column);
132
118
  if (columnType) {
@@ -255,8 +241,6 @@ class ConditionCollection {
255
241
  * @fires ConditionCollection#afterRemove
256
242
  */
257
243
  removeConditions(column) {
258
- // Store the current conditions as the previous condition stack before it's cleared.
259
- this.setPreviousConditionStack(this.exportAllConditions());
260
244
  this.runLocalHooks('beforeRemove', column);
261
245
  this.filteringStates.clearValue(column);
262
246
  this.runLocalHooks('afterRemove', column);
@@ -290,16 +274,6 @@ class ConditionCollection {
290
274
  return conditions.length > 0;
291
275
  }
292
276
 
293
- /**
294
- * Updates the `previousConditionStack` property with the provided stack.
295
- * It is used to store the current conditions before they are modified, allowing for undo operations.
296
- *
297
- * @param {Array|null} previousConditionStack The stack of previous conditions.
298
- */
299
- setPreviousConditionStack(previousConditionStack) {
300
- this.previousConditionStack = previousConditionStack;
301
- }
302
-
303
277
  /**
304
278
  * Destroy object.
305
279
  */
@@ -48,6 +48,8 @@ export declare class Filters extends BasePlugin {
48
48
  addCondition(column: number, name: string, args: any[], operationId?: OperationType): void;
49
49
  removeConditions(column: number): void;
50
50
  clearConditions(column?: number): void;
51
+ importConditions(conditions: ColumnConditions[]): void;
52
+ exportConditions(): ColumnConditions[];
51
53
  filter(): void;
52
54
  getSelectedColumn(): { physicalIndex: number, visualIndex: number } | null;
53
55
  getDataMapAtColumn(column?: number): CellLikeData[];
@@ -81,6 +81,7 @@ const SHORTCUTS_GROUP = PLUGIN_KEY;
81
81
  */
82
82
  var _menuFocusNavigator = /*#__PURE__*/new WeakMap();
83
83
  var _dropdownMenuTraces = /*#__PURE__*/new WeakMap();
84
+ var _previousConditionStack = /*#__PURE__*/new WeakMap();
84
85
  var _Filters_brand = /*#__PURE__*/new WeakSet();
85
86
  class Filters extends _base.BasePlugin {
86
87
  static get PLUGIN_KEY() {
@@ -152,6 +153,13 @@ class Filters extends _base.BasePlugin {
152
153
  * @type {WeakSet<Menu>}
153
154
  */
154
155
  _classPrivateFieldInitSpec(this, _dropdownMenuTraces, new WeakSet());
156
+ /**
157
+ * Stores the previous state of the condition stack before the latest filter operation.
158
+ * This is used in the `beforeFilter` plugin to allow performing the undo operation.
159
+ *
160
+ * @type {Array}
161
+ */
162
+ _classPrivateFieldInitSpec(this, _previousConditionStack, []);
155
163
  this.hot.addHook('afterGetColHeader', function () {
156
164
  for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
157
165
  args[_key] = arguments[_key];
@@ -494,6 +502,51 @@ class Filters extends _base.BasePlugin {
494
502
  }
495
503
  }
496
504
 
505
+ /**
506
+ * Imports filter conditions to all columns to the plugin. The method accepts
507
+ * the array of conditions with the same structure as the {@link Filters#exportConditions} method returns.
508
+ * Importing conditions will replace the current conditions. Once replaced, the state of the condition
509
+ * will be reflected in the UI. To apply the changes and filter the table, call
510
+ * the {@link Filters#filter} method eventually.
511
+ *
512
+ * @param {Array} conditions Array of conditions.
513
+ */
514
+ importConditions(conditions) {
515
+ this.conditionCollection.importAllConditions(conditions);
516
+ }
517
+
518
+ /* eslint-disable jsdoc/require-description-complete-sentence */
519
+ /**
520
+ * Exports filter conditions for all columns from the plugin.
521
+ * The array represents the filter state for each column. For example:
522
+ *
523
+ * ```js
524
+ * [
525
+ * {
526
+ * column: 1,
527
+ * operation: 'conjunction',
528
+ * conditions: [
529
+ * { name: 'gt', args: [95] },
530
+ * ]
531
+ * },
532
+ * {
533
+ * column: 7,
534
+ * operation: 'conjunction',
535
+ * conditions: [
536
+ * { name: 'contains', args: ['mike'] },
537
+ * { name: 'begins_with', args: ['m'] },
538
+ * ]
539
+ * },
540
+ * ]
541
+ * ```
542
+ *
543
+ * @returns {Array}
544
+ */
545
+ /* eslint-enable jsdoc/require-description-complete-sentence */
546
+ exportConditions() {
547
+ return this.conditionCollection.exportAllConditions();
548
+ }
549
+
497
550
  /**
498
551
  * Filters data based on added filter conditions.
499
552
  *
@@ -507,8 +560,8 @@ class Filters extends _base.BasePlugin {
507
560
  const dataFilter = this._createDataFilter();
508
561
  const needToFilter = !this.conditionCollection.isEmpty();
509
562
  let visibleVisualRows = [];
510
- const conditions = this.conditionCollection.exportAllConditions();
511
- const allowFiltering = this.hot.runHooks('beforeFilter', conditions, this.conditionCollection.previousConditionStack);
563
+ const conditions = this.exportConditions();
564
+ const allowFiltering = this.hot.runHooks('beforeFilter', conditions, _classPrivateFieldGet(_previousConditionStack, this));
512
565
  if (allowFiltering !== false) {
513
566
  if (needToFilter) {
514
567
  const trimmedRows = [];
@@ -531,11 +584,13 @@ class Filters extends _base.BasePlugin {
531
584
  } else {
532
585
  this.filtersRowsMap.clear();
533
586
  }
587
+ _classPrivateFieldSet(_previousConditionStack, this, this.exportConditions());
588
+ this.hot.runHooks('afterFilter', conditions);
589
+ this.hot.view.adjustElementsSize();
590
+ this.hot.render();
591
+ } else {
592
+ this.importConditions(_classPrivateFieldGet(_previousConditionStack, this));
534
593
  }
535
- this.hot.runHooks('afterFilter', conditions);
536
- this.conditionCollection.setPreviousConditionStack(null);
537
- this.hot.view.adjustElementsSize();
538
- this.hot.render();
539
594
  if (this.hot.selection.isSelected()) {
540
595
  this.hot.selectCell(navigableHeaders ? -1 : 0, this.hot.getSelectedRangeLast().highlight.col);
541
596
  }
@@ -898,7 +953,6 @@ function _onActionBarSubmit(submitType) {
898
953
  }
899
954
  this.conditionUpdateObserver.flush();
900
955
  this.components.forEach(component => component.saveState(physicalIndex));
901
- this.filtersRowsMap.clear();
902
956
  this.filter();
903
957
  }
904
958
  (_this$dropdownMenuPlu3 = this.dropdownMenuPlugin) === null || _this$dropdownMenuPlu3 === void 0 || _this$dropdownMenuPlu3.close();
@@ -940,8 +994,23 @@ function _updateComponents(conditionsState) {
940
994
  editedConditionStack: {
941
995
  conditions,
942
996
  column
943
- }
997
+ },
998
+ conditionArgsChange
944
999
  } = conditionsState;
1000
+ if (Array.isArray(conditionArgsChange)) {
1001
+ // update the previous condition stack (only for 'by_value' condition) on each dataset
1002
+ // change to make the undo/redo work properly
1003
+ _classPrivateFieldSet(_previousConditionStack, this, _classPrivateFieldGet(_previousConditionStack, this).map(stack => {
1004
+ if (stack.column === column && conditions.length > 0) {
1005
+ stack.conditions.forEach(condition => {
1006
+ if (condition.name === 'by_value') {
1007
+ condition.args = [[...conditionArgsChange]];
1008
+ }
1009
+ });
1010
+ }
1011
+ return stack;
1012
+ }));
1013
+ }
945
1014
  const conditionsByValue = conditions.filter(condition => condition.name === _constants2.CONDITION_BY_VALUE);
946
1015
  const conditionsWithoutByValue = conditions.filter(condition => condition.name !== _constants2.CONDITION_BY_VALUE);
947
1016
  if (conditionsByValue.length >= 2 || conditionsWithoutByValue.length >= 3) {
@@ -75,6 +75,7 @@ const SHORTCUTS_GROUP = PLUGIN_KEY;
75
75
  */
76
76
  var _menuFocusNavigator = /*#__PURE__*/new WeakMap();
77
77
  var _dropdownMenuTraces = /*#__PURE__*/new WeakMap();
78
+ var _previousConditionStack = /*#__PURE__*/new WeakMap();
78
79
  var _Filters_brand = /*#__PURE__*/new WeakSet();
79
80
  export class Filters extends BasePlugin {
80
81
  static get PLUGIN_KEY() {
@@ -146,6 +147,13 @@ export class Filters extends BasePlugin {
146
147
  * @type {WeakSet<Menu>}
147
148
  */
148
149
  _classPrivateFieldInitSpec(this, _dropdownMenuTraces, new WeakSet());
150
+ /**
151
+ * Stores the previous state of the condition stack before the latest filter operation.
152
+ * This is used in the `beforeFilter` plugin to allow performing the undo operation.
153
+ *
154
+ * @type {Array}
155
+ */
156
+ _classPrivateFieldInitSpec(this, _previousConditionStack, []);
149
157
  this.hot.addHook('afterGetColHeader', function () {
150
158
  for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
151
159
  args[_key] = arguments[_key];
@@ -488,6 +496,51 @@ export class Filters extends BasePlugin {
488
496
  }
489
497
  }
490
498
 
499
+ /**
500
+ * Imports filter conditions to all columns to the plugin. The method accepts
501
+ * the array of conditions with the same structure as the {@link Filters#exportConditions} method returns.
502
+ * Importing conditions will replace the current conditions. Once replaced, the state of the condition
503
+ * will be reflected in the UI. To apply the changes and filter the table, call
504
+ * the {@link Filters#filter} method eventually.
505
+ *
506
+ * @param {Array} conditions Array of conditions.
507
+ */
508
+ importConditions(conditions) {
509
+ this.conditionCollection.importAllConditions(conditions);
510
+ }
511
+
512
+ /* eslint-disable jsdoc/require-description-complete-sentence */
513
+ /**
514
+ * Exports filter conditions for all columns from the plugin.
515
+ * The array represents the filter state for each column. For example:
516
+ *
517
+ * ```js
518
+ * [
519
+ * {
520
+ * column: 1,
521
+ * operation: 'conjunction',
522
+ * conditions: [
523
+ * { name: 'gt', args: [95] },
524
+ * ]
525
+ * },
526
+ * {
527
+ * column: 7,
528
+ * operation: 'conjunction',
529
+ * conditions: [
530
+ * { name: 'contains', args: ['mike'] },
531
+ * { name: 'begins_with', args: ['m'] },
532
+ * ]
533
+ * },
534
+ * ]
535
+ * ```
536
+ *
537
+ * @returns {Array}
538
+ */
539
+ /* eslint-enable jsdoc/require-description-complete-sentence */
540
+ exportConditions() {
541
+ return this.conditionCollection.exportAllConditions();
542
+ }
543
+
491
544
  /**
492
545
  * Filters data based on added filter conditions.
493
546
  *
@@ -501,8 +554,8 @@ export class Filters extends BasePlugin {
501
554
  const dataFilter = this._createDataFilter();
502
555
  const needToFilter = !this.conditionCollection.isEmpty();
503
556
  let visibleVisualRows = [];
504
- const conditions = this.conditionCollection.exportAllConditions();
505
- const allowFiltering = this.hot.runHooks('beforeFilter', conditions, this.conditionCollection.previousConditionStack);
557
+ const conditions = this.exportConditions();
558
+ const allowFiltering = this.hot.runHooks('beforeFilter', conditions, _classPrivateFieldGet(_previousConditionStack, this));
506
559
  if (allowFiltering !== false) {
507
560
  if (needToFilter) {
508
561
  const trimmedRows = [];
@@ -525,11 +578,13 @@ export class Filters extends BasePlugin {
525
578
  } else {
526
579
  this.filtersRowsMap.clear();
527
580
  }
581
+ _classPrivateFieldSet(_previousConditionStack, this, this.exportConditions());
582
+ this.hot.runHooks('afterFilter', conditions);
583
+ this.hot.view.adjustElementsSize();
584
+ this.hot.render();
585
+ } else {
586
+ this.importConditions(_classPrivateFieldGet(_previousConditionStack, this));
528
587
  }
529
- this.hot.runHooks('afterFilter', conditions);
530
- this.conditionCollection.setPreviousConditionStack(null);
531
- this.hot.view.adjustElementsSize();
532
- this.hot.render();
533
588
  if (this.hot.selection.isSelected()) {
534
589
  this.hot.selectCell(navigableHeaders ? -1 : 0, this.hot.getSelectedRangeLast().highlight.col);
535
590
  }
@@ -891,7 +946,6 @@ function _onActionBarSubmit(submitType) {
891
946
  }
892
947
  this.conditionUpdateObserver.flush();
893
948
  this.components.forEach(component => component.saveState(physicalIndex));
894
- this.filtersRowsMap.clear();
895
949
  this.filter();
896
950
  }
897
951
  (_this$dropdownMenuPlu3 = this.dropdownMenuPlugin) === null || _this$dropdownMenuPlu3 === void 0 || _this$dropdownMenuPlu3.close();
@@ -933,8 +987,23 @@ function _updateComponents(conditionsState) {
933
987
  editedConditionStack: {
934
988
  conditions,
935
989
  column
936
- }
990
+ },
991
+ conditionArgsChange
937
992
  } = conditionsState;
993
+ if (Array.isArray(conditionArgsChange)) {
994
+ // update the previous condition stack (only for 'by_value' condition) on each dataset
995
+ // change to make the undo/redo work properly
996
+ _classPrivateFieldSet(_previousConditionStack, this, _classPrivateFieldGet(_previousConditionStack, this).map(stack => {
997
+ if (stack.column === column && conditions.length > 0) {
998
+ stack.conditions.forEach(condition => {
999
+ if (condition.name === 'by_value') {
1000
+ condition.args = [[...conditionArgsChange]];
1001
+ }
1002
+ });
1003
+ }
1004
+ return stack;
1005
+ }));
1006
+ }
938
1007
  const conditionsByValue = conditions.filter(condition => condition.name === CONDITION_BY_VALUE);
939
1008
  const conditionsWithoutByValue = conditions.filter(condition => condition.name !== CONDITION_BY_VALUE);
940
1009
  if (conditionsByValue.length >= 2 || conditionsWithoutByValue.length >= 3) {
@@ -1497,12 +1497,11 @@ function _sumCellsHeights(row, rowspan) {
1497
1497
  } = this.hot;
1498
1498
  const stylesHandler = view.getStylesHandler();
1499
1499
  const defaultHeight = view.getDefaultRowHeight();
1500
- const autoRowSizePlugin = this.hot.getPlugin('autoRowSize');
1501
1500
  let height = 0;
1502
1501
  for (let i = row; i < row + rowspan; i++) {
1503
1502
  if (!rowIndexMapper.isHidden(i)) {
1504
- var _autoRowSizePlugin$ge;
1505
- height += (_autoRowSizePlugin$ge = autoRowSizePlugin === null || autoRowSizePlugin === void 0 ? void 0 : autoRowSizePlugin.getRowHeight(i)) !== null && _autoRowSizePlugin$ge !== void 0 ? _autoRowSizePlugin$ge : defaultHeight;
1503
+ var _this$hot$getRowHeigh;
1504
+ height += (_this$hot$getRowHeigh = this.hot.getRowHeight(i)) !== null && _this$hot$getRowHeigh !== void 0 ? _this$hot$getRowHeigh : defaultHeight;
1506
1505
  if (i === 0 && !stylesHandler.isClassicTheme()) {
1507
1506
  height += 1; // border-top-width
1508
1507
  }
@@ -1492,12 +1492,11 @@ function _sumCellsHeights(row, rowspan) {
1492
1492
  } = this.hot;
1493
1493
  const stylesHandler = view.getStylesHandler();
1494
1494
  const defaultHeight = view.getDefaultRowHeight();
1495
- const autoRowSizePlugin = this.hot.getPlugin('autoRowSize');
1496
1495
  let height = 0;
1497
1496
  for (let i = row; i < row + rowspan; i++) {
1498
1497
  if (!rowIndexMapper.isHidden(i)) {
1499
- var _autoRowSizePlugin$ge;
1500
- height += (_autoRowSizePlugin$ge = autoRowSizePlugin === null || autoRowSizePlugin === void 0 ? void 0 : autoRowSizePlugin.getRowHeight(i)) !== null && _autoRowSizePlugin$ge !== void 0 ? _autoRowSizePlugin$ge : defaultHeight;
1498
+ var _this$hot$getRowHeigh;
1499
+ height += (_this$hot$getRowHeigh = this.hot.getRowHeight(i)) !== null && _this$hot$getRowHeigh !== void 0 ? _this$hot$getRowHeigh : defaultHeight;
1501
1500
  if (i === 0 && !stylesHandler.isClassicTheme()) {
1502
1501
  height += 1; // border-top-width
1503
1502
  }
@@ -7,7 +7,6 @@ var _sortService = require("../columnSorting/sortService");
7
7
  var _utils = require("../columnSorting/utils");
8
8
  var _element = require("../../helpers/dom/element");
9
9
  var _rootComparator = require("./rootComparator");
10
- var _utils2 = require("./utils");
11
10
  var _domHelpers = require("./domHelpers");
12
11
  var _shortcutContexts = require("../../shortcutContexts");
13
12
  function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
@@ -15,7 +14,6 @@ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol"
15
14
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
16
15
  const PLUGIN_KEY = exports.PLUGIN_KEY = 'multiColumnSorting';
17
16
  const PLUGIN_PRIORITY = exports.PLUGIN_PRIORITY = 170;
18
- const CONFLICTED_PLUGIN_KEY = 'columnSorting';
19
17
  const SHORTCUTS_GROUP = PLUGIN_KEY;
20
18
  (0, _sortService.registerRootComparator)(PLUGIN_KEY, _rootComparator.rootComparator);
21
19
 
@@ -95,17 +93,13 @@ class MultiColumnSorting extends _columnSorting.ColumnSorting {
95
93
  * @returns {boolean}
96
94
  */
97
95
  isEnabled() {
98
- return super.isEnabled();
96
+ return !!this.hot.getSettings()[this.pluginKey];
99
97
  }
100
98
 
101
99
  /**
102
100
  * Enables the plugin functionality for this Handsontable instance.
103
101
  */
104
102
  enablePlugin() {
105
- if (!this.enabled && this.hot.getSettings()[this.pluginKey] && this.hot.getSettings()[CONFLICTED_PLUGIN_KEY]) {
106
- (0, _utils2.warnAboutPluginsConflict)();
107
- this.hot.getPlugin(CONFLICTED_PLUGIN_KEY).disablePlugin();
108
- }
109
103
  super.enablePlugin();
110
104
  }
111
105
 
@@ -277,20 +271,6 @@ class MultiColumnSorting extends _columnSorting.ColumnSorting {
277
271
  }
278
272
  }
279
273
 
280
- /**
281
- * Overwriting base plugin's `onUpdateSettings` method. Please keep in mind that `onAfterUpdateSettings` isn't called
282
- * for `updateSettings` in specific situations.
283
- *
284
- * @private
285
- * @param {object} newSettings New settings object.
286
- */
287
- onUpdateSettings(newSettings) {
288
- if (this.hot.getSettings()[this.pluginKey] && this.hot.getSettings()[CONFLICTED_PLUGIN_KEY]) {
289
- (0, _utils2.warnAboutPluginsConflict)();
290
- }
291
- super.onUpdateSettings(newSettings);
292
- }
293
-
294
274
  /**
295
275
  * Callback for the `onAfterOnCellMouseDown` hook.
296
276
  *