handsontable 17.0.0 → 17.0.1-rc2

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/3rdparty/walkontable/src/overlays.js +3 -3
  2. package/3rdparty/walkontable/src/overlays.mjs +3 -3
  3. package/3rdparty/walkontable/src/selection/border/border.js +26 -15
  4. package/3rdparty/walkontable/src/selection/border/border.mjs +26 -15
  5. package/3rdparty/walkontable/src/selection/manager.js +12 -0
  6. package/3rdparty/walkontable/src/selection/manager.mjs +12 -0
  7. package/CHANGELOG.md +21 -2
  8. package/base.js +2 -2
  9. package/base.mjs +2 -2
  10. package/core.d.ts +5 -0
  11. package/core.js +10 -2
  12. package/core.mjs +10 -2
  13. package/dataMap/metaManager/metaSchema.js +2 -2
  14. package/dataMap/metaManager/metaSchema.mjs +2 -2
  15. package/dist/handsontable.full.js +594 -225
  16. package/dist/handsontable.full.min.js +75 -75
  17. package/dist/handsontable.js +579 -221
  18. package/dist/handsontable.min.js +29 -29
  19. package/dist/languages/all.min.js +1 -1
  20. package/dist/languages/ar-AR.min.js +1 -1
  21. package/dist/languages/cs-CZ.min.js +1 -1
  22. package/dist/languages/de-CH.min.js +1 -1
  23. package/dist/languages/de-DE.min.js +1 -1
  24. package/dist/languages/en-US.min.js +1 -1
  25. package/dist/languages/es-MX.min.js +1 -1
  26. package/dist/languages/fa-IR.min.js +1 -1
  27. package/dist/languages/fr-FR.min.js +1 -1
  28. package/dist/languages/hr-HR.min.js +1 -1
  29. package/dist/languages/it-IT.min.js +1 -1
  30. package/dist/languages/ja-JP.min.js +1 -1
  31. package/dist/languages/ko-KR.min.js +1 -1
  32. package/dist/languages/lv-LV.min.js +1 -1
  33. package/dist/languages/nb-NO.min.js +1 -1
  34. package/dist/languages/nl-NL.min.js +1 -1
  35. package/dist/languages/pl-PL.min.js +1 -1
  36. package/dist/languages/pt-BR.min.js +1 -1
  37. package/dist/languages/ru-RU.min.js +1 -1
  38. package/dist/languages/sr-SP.min.js +1 -1
  39. package/dist/languages/zh-CN.min.js +1 -1
  40. package/dist/languages/zh-TW.min.js +1 -1
  41. package/dist/themes/classic.js +2 -2
  42. package/dist/themes/classic.min.js +3 -3
  43. package/dist/themes/horizon.js +2 -2
  44. package/dist/themes/horizon.min.js +3 -3
  45. package/dist/themes/main.js +4 -4
  46. package/dist/themes/main.min.js +3 -3
  47. package/dist/themes/static/variables/colors/ant.js +2 -2
  48. package/dist/themes/static/variables/colors/ant.min.js +3 -3
  49. package/dist/themes/static/variables/colors/classic.js +2 -2
  50. package/dist/themes/static/variables/colors/classic.min.js +3 -3
  51. package/dist/themes/static/variables/colors/horizon.js +2 -2
  52. package/dist/themes/static/variables/colors/horizon.min.js +3 -3
  53. package/dist/themes/static/variables/colors/main.js +2 -2
  54. package/dist/themes/static/variables/colors/main.min.js +3 -3
  55. package/dist/themes/static/variables/colors/material.js +2 -2
  56. package/dist/themes/static/variables/colors/material.min.js +3 -3
  57. package/dist/themes/static/variables/colors/shadcn.js +2 -2
  58. package/dist/themes/static/variables/colors/shadcn.min.js +3 -3
  59. package/dist/themes/static/variables/density.js +2 -2
  60. package/dist/themes/static/variables/density.min.js +3 -3
  61. package/dist/themes/static/variables/helpers/iconsMap.js +2 -2
  62. package/dist/themes/static/variables/helpers/iconsMap.min.js +3 -3
  63. package/dist/themes/static/variables/icons/horizon.js +2 -2
  64. package/dist/themes/static/variables/icons/horizon.min.js +3 -3
  65. package/dist/themes/static/variables/icons/main.js +2 -2
  66. package/dist/themes/static/variables/icons/main.min.js +3 -3
  67. package/dist/themes/static/variables/sizing.js +2 -2
  68. package/dist/themes/static/variables/sizing.min.js +3 -3
  69. package/dist/themes/static/variables/tokens/classic.js +2 -2
  70. package/dist/themes/static/variables/tokens/classic.min.js +3 -3
  71. package/dist/themes/static/variables/tokens/horizon.js +2 -2
  72. package/dist/themes/static/variables/tokens/horizon.min.js +3 -3
  73. package/dist/themes/static/variables/tokens/main.js +4 -4
  74. package/dist/themes/static/variables/tokens/main.min.js +3 -3
  75. package/editors/baseEditor/baseEditor.js +17 -23
  76. package/editors/baseEditor/baseEditor.mjs +17 -23
  77. package/editors/dateEditor/dateEditor.js +28 -4
  78. package/editors/dateEditor/dateEditor.mjs +28 -4
  79. package/helpers/browser.d.ts +1 -0
  80. package/helpers/browser.js +19 -1
  81. package/helpers/browser.mjs +18 -1
  82. package/helpers/dom/element.js +42 -26
  83. package/helpers/dom/element.mjs +43 -27
  84. package/helpers/mixed.js +6 -6
  85. package/helpers/mixed.mjs +6 -6
  86. package/package.json +1 -1
  87. package/plugins/exportFile/exportFile.js +60 -21
  88. package/plugins/exportFile/exportFile.mjs +60 -21
  89. package/plugins/formulas/formulas.js +3 -5
  90. package/plugins/formulas/formulas.mjs +3 -5
  91. package/plugins/nestedHeaders/nestedHeaders.js +22 -2
  92. package/plugins/nestedHeaders/nestedHeaders.mjs +22 -2
  93. package/plugins/nestedHeaders/utils/ghostTable.js +162 -57
  94. package/plugins/nestedHeaders/utils/ghostTable.mjs +162 -57
  95. package/plugins/nestedRows/nestedRows.js +55 -27
  96. package/plugins/nestedRows/nestedRows.mjs +55 -27
  97. package/plugins/undoRedo/actions/dataChange.js +11 -6
  98. package/plugins/undoRedo/actions/dataChange.mjs +11 -6
  99. package/plugins/undoRedo/undoRedo.js +3 -1
  100. package/plugins/undoRedo/undoRedo.mjs +3 -1
  101. package/styles/handsontable.css +27 -10
  102. package/styles/handsontable.min.css +3 -3
  103. package/styles/handsontableStyles.js +1 -1
  104. package/styles/handsontableStyles.mjs +1 -1
  105. package/styles/ht-icons-horizon.min.css +2 -2
  106. package/styles/ht-icons-main.min.css +2 -2
  107. package/styles/ht-theme-classic-no-icons.min.css +2 -2
  108. package/styles/ht-theme-classic.min.css +2 -2
  109. package/styles/ht-theme-horizon-no-icons.min.css +2 -2
  110. package/styles/ht-theme-horizon.min.css +2 -2
  111. package/styles/ht-theme-main-no-icons.css +2 -2
  112. package/styles/ht-theme-main-no-icons.min.css +3 -3
  113. package/styles/ht-theme-main.css +2 -2
  114. package/styles/ht-theme-main.min.css +3 -3
  115. package/tableView.js +5 -0
  116. package/tableView.mjs +5 -0
  117. package/themes/static/variables/tokens/main.js +2 -2
  118. package/themes/static/variables/tokens/main.mjs +2 -2
  119. package/utils/parseTable.js +86 -13
  120. package/utils/parseTable.mjs +85 -13
@@ -462,13 +462,13 @@ class Overlays {
462
462
  }
463
463
  const topHolder = this.topOverlay.clone.wtTable.holder; // todo rethink
464
464
  const leftHolder = this.inlineStartOverlay.clone.wtTable.holder; // todo rethink
465
-
465
+ const preventOverflow = this.wtSettings.getSetting('preventOverflow');
466
466
  let scrollX = this.scrollableElement.scrollLeft;
467
467
  let scrollY = this.scrollableElement.scrollTop;
468
- if (this.wot.wtViewport.isHorizontallyScrollableByWindow()) {
468
+ if (this.wot.wtViewport.isHorizontallyScrollableByWindow() && (typeof preventOverflow === 'boolean' && preventOverflow || preventOverflow !== 'horizontal')) {
469
469
  scrollX = this.scrollableElement.scrollX;
470
470
  }
471
- if (this.wot.wtViewport.isVerticallyScrollableByWindow()) {
471
+ if (this.wot.wtViewport.isVerticallyScrollableByWindow() && (typeof preventOverflow === 'boolean' && preventOverflow || preventOverflow !== 'vertical')) {
472
472
  scrollY = this.scrollableElement.scrollY;
473
473
  }
474
474
  this.horizontalScrolling = this.lastScrollX !== scrollX;
@@ -459,13 +459,13 @@ class Overlays {
459
459
  }
460
460
  const topHolder = this.topOverlay.clone.wtTable.holder; // todo rethink
461
461
  const leftHolder = this.inlineStartOverlay.clone.wtTable.holder; // todo rethink
462
-
462
+ const preventOverflow = this.wtSettings.getSetting('preventOverflow');
463
463
  let scrollX = this.scrollableElement.scrollLeft;
464
464
  let scrollY = this.scrollableElement.scrollTop;
465
- if (this.wot.wtViewport.isHorizontallyScrollableByWindow()) {
465
+ if (this.wot.wtViewport.isHorizontallyScrollableByWindow() && (typeof preventOverflow === 'boolean' && preventOverflow || preventOverflow !== 'horizontal')) {
466
466
  scrollX = this.scrollableElement.scrollX;
467
467
  }
468
- if (this.wot.wtViewport.isVerticallyScrollableByWindow()) {
468
+ if (this.wot.wtViewport.isVerticallyScrollableByWindow() && (typeof preventOverflow === 'boolean' && preventOverflow || preventOverflow !== 'vertical')) {
469
469
  scrollY = this.scrollableElement.scrollY;
470
470
  }
471
471
  this.horizontalScrolling = this.lastScrollX !== scrollX;
@@ -206,24 +206,16 @@ class Border {
206
206
  * Create multiple selector handler for mobile devices.
207
207
  */
208
208
  createMultipleSelectorHandles() {
209
+ const hitAreaWidth = 40;
209
210
  const {
210
- rootDocument,
211
- wtSettings
211
+ rootDocument
212
212
  } = this.wot;
213
- const stylesHandler = wtSettings.getSetting('stylesHandler');
214
- const cellMobileHandleSize = stylesHandler.getCSSVariableValue('cell-mobile-handle-size');
215
- const cellMobileHandleBorderRadius = stylesHandler.getCSSVariableValue('cell-mobile-handle-border-radius');
216
- const cellMobileHandleBackgroundColor = stylesHandler.getCSSVariableValue('cell-mobile-handle-background-color');
217
- const cellMobileHandleBackgroundOpacity = stylesHandler.getCSSVariableValue('cell-mobile-handle-background-opacity');
218
- const cellMobileHandleBorderWidth = stylesHandler.getCSSVariableValue('cell-mobile-handle-border-width');
219
- const cellMobileHandleBorderColor = stylesHandler.getCSSVariableValue('cell-mobile-handle-border-color');
220
213
  this.selectionHandles = {
221
214
  top: rootDocument.createElement('DIV'),
222
215
  topHitArea: rootDocument.createElement('DIV'),
223
216
  bottom: rootDocument.createElement('DIV'),
224
217
  bottomHitArea: rootDocument.createElement('DIV')
225
218
  };
226
- const hitAreaWidth = 40;
227
219
  this.selectionHandles.top.className = 'topSelectionHandle topLeftSelectionHandle';
228
220
  this.selectionHandles.topHitArea.className = 'topSelectionHandle-HitArea topLeftSelectionHandle-HitArea';
229
221
  this.selectionHandles.bottom.className = 'bottomSelectionHandle bottomRightSelectionHandle';
@@ -244,23 +236,42 @@ class Border {
244
236
  this.selectionHandles.styles.bottomHitArea[key] = value;
245
237
  this.selectionHandles.styles.topHitArea[key] = value;
246
238
  });
239
+ this.updateMultipleSelectorHandlesStyles();
240
+ this.main.appendChild(this.selectionHandles.top);
241
+ this.main.appendChild(this.selectionHandles.bottom);
242
+ this.main.appendChild(this.selectionHandles.topHitArea);
243
+ this.main.appendChild(this.selectionHandles.bottomHitArea);
244
+ }
245
+
246
+ /**
247
+ * Updates the multiple selector handle styles from the current theme after a theme change.
248
+ * Rereads CSS variables and applies them to existing handle elements.
249
+ */
250
+ updateMultipleSelectorHandlesStyles() {
251
+ if (!this.selectionHandles) {
252
+ return;
253
+ }
254
+ const wtSettings = this.wot.wtSettings;
255
+ const stylesHandler = wtSettings.getSetting('stylesHandler');
256
+ const cellMobileHandleSize = stylesHandler.getCSSVariableValue('cell-mobile-handle-size');
257
+ const cellMobileHandleBorderRadius = stylesHandler.getCSSVariableValue('cell-mobile-handle-border-radius');
258
+ const cellMobileHandleBackgroundColor = stylesHandler.getCSSVariableValue('cell-mobile-handle-background-color');
259
+ const cellMobileHandleBackgroundOpacity = stylesHandler.getCSSVariableValue('cell-mobile-handle-background-opacity');
260
+ const cellMobileHandleBorderWidth = stylesHandler.getCSSVariableValue('cell-mobile-handle-border-width');
261
+ const cellMobileHandleBorderColor = stylesHandler.getCSSVariableValue('cell-mobile-handle-border-color');
247
262
  const handleStyle = {
248
263
  position: 'absolute',
249
264
  height: `${cellMobileHandleSize}px`,
250
265
  width: `${cellMobileHandleSize}px`,
251
266
  'border-radius': `${cellMobileHandleBorderRadius}px`,
252
267
  // eslint-disable-next-line max-len
253
- background: `color-mix(in srgb, ${cellMobileHandleBackgroundColor} ${cellMobileHandleBackgroundOpacity}, transparent)`,
268
+ background: `color-mix(in srgb, ${cellMobileHandleBackgroundColor} ${cellMobileHandleBackgroundOpacity}%, transparent)`,
254
269
  border: `${cellMobileHandleBorderWidth}px solid ${cellMobileHandleBorderColor}`
255
270
  };
256
271
  (0, _object.objectEach)(handleStyle, (value, key) => {
257
272
  this.selectionHandles.styles.bottom[key] = value;
258
273
  this.selectionHandles.styles.top[key] = value;
259
274
  });
260
- this.main.appendChild(this.selectionHandles.top);
261
- this.main.appendChild(this.selectionHandles.bottom);
262
- this.main.appendChild(this.selectionHandles.topHitArea);
263
- this.main.appendChild(this.selectionHandles.bottomHitArea);
264
275
  }
265
276
 
266
277
  /**
@@ -203,24 +203,16 @@ class Border {
203
203
  * Create multiple selector handler for mobile devices.
204
204
  */
205
205
  createMultipleSelectorHandles() {
206
+ const hitAreaWidth = 40;
206
207
  const {
207
- rootDocument,
208
- wtSettings
208
+ rootDocument
209
209
  } = this.wot;
210
- const stylesHandler = wtSettings.getSetting('stylesHandler');
211
- const cellMobileHandleSize = stylesHandler.getCSSVariableValue('cell-mobile-handle-size');
212
- const cellMobileHandleBorderRadius = stylesHandler.getCSSVariableValue('cell-mobile-handle-border-radius');
213
- const cellMobileHandleBackgroundColor = stylesHandler.getCSSVariableValue('cell-mobile-handle-background-color');
214
- const cellMobileHandleBackgroundOpacity = stylesHandler.getCSSVariableValue('cell-mobile-handle-background-opacity');
215
- const cellMobileHandleBorderWidth = stylesHandler.getCSSVariableValue('cell-mobile-handle-border-width');
216
- const cellMobileHandleBorderColor = stylesHandler.getCSSVariableValue('cell-mobile-handle-border-color');
217
210
  this.selectionHandles = {
218
211
  top: rootDocument.createElement('DIV'),
219
212
  topHitArea: rootDocument.createElement('DIV'),
220
213
  bottom: rootDocument.createElement('DIV'),
221
214
  bottomHitArea: rootDocument.createElement('DIV')
222
215
  };
223
- const hitAreaWidth = 40;
224
216
  this.selectionHandles.top.className = 'topSelectionHandle topLeftSelectionHandle';
225
217
  this.selectionHandles.topHitArea.className = 'topSelectionHandle-HitArea topLeftSelectionHandle-HitArea';
226
218
  this.selectionHandles.bottom.className = 'bottomSelectionHandle bottomRightSelectionHandle';
@@ -241,23 +233,42 @@ class Border {
241
233
  this.selectionHandles.styles.bottomHitArea[key] = value;
242
234
  this.selectionHandles.styles.topHitArea[key] = value;
243
235
  });
236
+ this.updateMultipleSelectorHandlesStyles();
237
+ this.main.appendChild(this.selectionHandles.top);
238
+ this.main.appendChild(this.selectionHandles.bottom);
239
+ this.main.appendChild(this.selectionHandles.topHitArea);
240
+ this.main.appendChild(this.selectionHandles.bottomHitArea);
241
+ }
242
+
243
+ /**
244
+ * Updates the multiple selector handle styles from the current theme after a theme change.
245
+ * Rereads CSS variables and applies them to existing handle elements.
246
+ */
247
+ updateMultipleSelectorHandlesStyles() {
248
+ if (!this.selectionHandles) {
249
+ return;
250
+ }
251
+ const wtSettings = this.wot.wtSettings;
252
+ const stylesHandler = wtSettings.getSetting('stylesHandler');
253
+ const cellMobileHandleSize = stylesHandler.getCSSVariableValue('cell-mobile-handle-size');
254
+ const cellMobileHandleBorderRadius = stylesHandler.getCSSVariableValue('cell-mobile-handle-border-radius');
255
+ const cellMobileHandleBackgroundColor = stylesHandler.getCSSVariableValue('cell-mobile-handle-background-color');
256
+ const cellMobileHandleBackgroundOpacity = stylesHandler.getCSSVariableValue('cell-mobile-handle-background-opacity');
257
+ const cellMobileHandleBorderWidth = stylesHandler.getCSSVariableValue('cell-mobile-handle-border-width');
258
+ const cellMobileHandleBorderColor = stylesHandler.getCSSVariableValue('cell-mobile-handle-border-color');
244
259
  const handleStyle = {
245
260
  position: 'absolute',
246
261
  height: `${cellMobileHandleSize}px`,
247
262
  width: `${cellMobileHandleSize}px`,
248
263
  'border-radius': `${cellMobileHandleBorderRadius}px`,
249
264
  // eslint-disable-next-line max-len
250
- background: `color-mix(in srgb, ${cellMobileHandleBackgroundColor} ${cellMobileHandleBackgroundOpacity}, transparent)`,
265
+ background: `color-mix(in srgb, ${cellMobileHandleBackgroundColor} ${cellMobileHandleBackgroundOpacity}%, transparent)`,
251
266
  border: `${cellMobileHandleBorderWidth}px solid ${cellMobileHandleBorderColor}`
252
267
  };
253
268
  objectEach(handleStyle, (value, key) => {
254
269
  this.selectionHandles.styles.bottom[key] = value;
255
270
  this.selectionHandles.styles.top[key] = value;
256
271
  });
257
- this.main.appendChild(this.selectionHandles.top);
258
- this.main.appendChild(this.selectionHandles.bottom);
259
- this.main.appendChild(this.selectionHandles.topHitArea);
260
- this.main.appendChild(this.selectionHandles.bottomHitArea);
261
272
  }
262
273
 
263
274
  /**
@@ -151,6 +151,18 @@ class SelectionManager {
151
151
  _classPrivateFieldGet(_selectionBorders, this).delete(selection);
152
152
  }
153
153
 
154
+ /**
155
+ * Refreshes the multiple selector handle styles on all border instances after a theme change.
156
+ */
157
+ refreshAllBorderHandleStyles() {
158
+ _classPrivateFieldGet(_selectionBorders, this).forEach(bordersMap => {
159
+ bordersMap.forEach(border => {
160
+ var _border$updateMultipl;
161
+ (_border$updateMultipl = border.updateMultipleSelectorHandlesStyles) === null || _border$updateMultipl === void 0 || _border$updateMultipl.call(border);
162
+ });
163
+ });
164
+ }
165
+
154
166
  /**
155
167
  * Renders all the selections (add CSS classes to cells and draw borders).
156
168
  *
@@ -147,6 +147,18 @@ export class SelectionManager {
147
147
  _classPrivateFieldGet(_selectionBorders, this).delete(selection);
148
148
  }
149
149
 
150
+ /**
151
+ * Refreshes the multiple selector handle styles on all border instances after a theme change.
152
+ */
153
+ refreshAllBorderHandleStyles() {
154
+ _classPrivateFieldGet(_selectionBorders, this).forEach(bordersMap => {
155
+ bordersMap.forEach(border => {
156
+ var _border$updateMultipl;
157
+ (_border$updateMultipl = border.updateMultipleSelectorHandlesStyles) === null || _border$updateMultipl === void 0 || _border$updateMultipl.call(border);
158
+ });
159
+ });
160
+ }
161
+
150
162
  /**
151
163
  * Renders all the selections (add CSS classes to cells and draw borders).
152
164
  *
package/CHANGELOG.md CHANGED
@@ -9,7 +9,26 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
9
9
 
10
10
  <!-- UNVERSIONED -->
11
11
 
12
- ## [17.0.0-rc15] - 2026-03-05
12
+ ## [17.0.1-rc1] - 2026-03-19
13
+
14
+ ### Fixed
15
+ - Fix UndoRedo crash when nullified changes [#12000](https://github.com/handsontable/handsontable/pull/12000)
16
+ - Fix UndoRedo beforeChange order [#12001](https://github.com/handsontable/handsontable/pull/12001)
17
+ - Fixed a bug where the editor does not receive the user inputs [#12042](https://github.com/handsontable/handsontable/pull/12042)
18
+ - Fixed scrollbar width calcualtion on Safari >=26. [#12047](https://github.com/handsontable/handsontable/pull/12047)
19
+ - Added missing typings for Core [#12048](https://github.com/handsontable/handsontable/pull/12048)
20
+ - Fixed rounded corners that may be applied in incorrect use cases [#12052](https://github.com/handsontable/handsontable/pull/12052)
21
+ - Fixed undo/redo stack desync with formulas engine [#12056](https://github.com/handsontable/handsontable/pull/12056)
22
+ - Fixed column width calculation for collapsed columns [#12059](https://github.com/handsontable/handsontable/pull/12059)
23
+ - Fixed and issue with table backround overflow [#12063](https://github.com/handsontable/handsontable/pull/12063)
24
+ - Fixed column header misalignment when nestedRow is enabled [#12081](https://github.com/handsontable/handsontable/pull/12081)
25
+ - Fixed an issue with mobile select handles styles [#12083](https://github.com/handsontable/handsontable/pull/12083)
26
+ - Improved clipboard processing after paste [#12084](https://github.com/handsontable/handsontable/pull/12084)
27
+ - Fixed an issue with scrolling issue that occurs when preventOverflow is enabled [#12086](https://github.com/handsontable/handsontable/pull/12086)
28
+ - Fixed date picker positioning near viewport edges [#12087](https://github.com/handsontable/handsontable/pull/12087)
29
+ - Fixed an issue with main theme hover on menu icon background color [#12159](https://github.com/handsontable/handsontable/pull/12159)
30
+
31
+ ## [17.0.0] - 2026-03-09
13
32
 
14
33
  ### Added
15
34
  - **Breaking change**: Added the Theme API. [#11950](https://github.com/handsontable/handsontable/pull/11950)
@@ -34,7 +53,7 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
34
53
  - Deprecated **moment.js** for date parsing and display. Replace it with the `Intl.DateTimeFormat` API. [Migration guide](https://handsontable.com/docs/javascript-data-grid/migration-from-16.2-to-17.0#_4-migrate-from-moment-js-format-to-intl-datetimeformat)
35
54
  - Deprecated **DOMPurify** as a built-in XSS sanitizer. Use the new `sanitizer` option or convert content to plain text. [Migration guide](https://handsontable.com/docs/javascript-data-grid/migration-from-16.2-to-17.0#_5-migrate-from-built-in-dompurify-to-the-sanitizer-option)
36
55
  - Deprecated **core-js** polyfills for ECMAScript features. [Migration guide](https://handsontable.com/docs/javascript-data-grid/migration-from-16.2-to-17.0#_6-core-js-dependency-removed)
37
- - Deprecated bundling **HyperFormula** as a Handsontable dependency. Starting from version 18.0, install and import it separately, then pass it to the Formulas plugin with `licenseKey: 'internal-use'`. [Formula calculation](https://handsontable.com/docs/javascript-data-grid/formula-calculation)
56
+ - Deprecated bundling **HyperFormula** as a Handsontable dependency. Starting from version 18.0, install and import it separately, then pass it to the Formulas plugin with `licenseKey: 'internal-use-in-handsontable'`. [Formula calculation](https://handsontable.com/docs/javascript-data-grid/formula-calculation)
38
57
 
39
58
  ### Removed
40
59
  - **Breaking change**: Removed deprecated wrapper packages for Angular, React, and Vue, the `PersistentState` plugin, and the legacy undo/redo methods. [#12015](https://github.com/handsontable/handsontable/pull/12015)
package/base.js CHANGED
@@ -49,8 +49,8 @@ Handsontable.hooks = _hooks.Hooks.getSingleton();
49
49
  Handsontable.CellCoords = _src.CellCoords;
50
50
  Handsontable.CellRange = _src.CellRange;
51
51
  Handsontable.packageName = 'handsontable';
52
- Handsontable.buildDate = "09/03/2026 08:59:13";
53
- Handsontable.version = "17.0.0";
52
+ Handsontable.buildDate = "23/03/2026 11:47:53";
53
+ Handsontable.version = "17.0.1-rc2";
54
54
  Handsontable.languages = {
55
55
  dictionaryKeys: _registry.dictionaryKeys,
56
56
  getLanguageDictionary: _registry.getLanguageDictionary,
package/base.mjs CHANGED
@@ -39,8 +39,8 @@ Handsontable.hooks = Hooks.getSingleton();
39
39
  Handsontable.CellCoords = CellCoords;
40
40
  Handsontable.CellRange = CellRange;
41
41
  Handsontable.packageName = 'handsontable';
42
- Handsontable.buildDate = "09/03/2026 08:59:20";
43
- Handsontable.version = "17.0.0";
42
+ Handsontable.buildDate = "23/03/2026 11:48:00";
43
+ Handsontable.version = "17.0.1-rc2";
44
44
  Handsontable.languages = {
45
45
  dictionaryKeys,
46
46
  getLanguageDictionary,
package/core.d.ts CHANGED
@@ -121,6 +121,7 @@ export default class Core {
121
121
  getTableHeight(): number;
122
122
  getTranslatedPhrase(dictionaryKey: string, extraArguments: any): string | null;
123
123
  getValue(): CellValue;
124
+ guid: string;
124
125
  hasColHeaders(): boolean;
125
126
  hasHook(key: keyof Events): boolean;
126
127
  hasRowHeaders(): boolean;
@@ -145,9 +146,13 @@ export default class Core {
145
146
  render(): void;
146
147
  resumeExecution(): void;
147
148
  resumeRender(): void;
149
+ rootContainer: HTMLElement;
148
150
  rootDocument: Document;
149
151
  rootElement: HTMLElement;
152
+ rootGridElement: HTMLElement;
153
+ rootPortalElement: HTMLElement;
150
154
  rootWindow: Window;
155
+ rootWrapperElement: HTMLElement;
151
156
  rowIndexMapper: IndexMapper;
152
157
  runHooks(key: keyof Events, p1?: any, p2?: any, p3?: any, p4?: any, p5?: any, p6?: any): any;
153
158
  scrollViewportTo(options: { row?: number, col?: number, verticalSnap?: 'top' | 'bottom', horizontalSnap?: 'start' | 'end', considerHiddenIndexes?: boolean }, callback?: () => void): boolean;
package/core.js CHANGED
@@ -325,6 +325,7 @@ function Core(rootContainer, userSettings) {
325
325
  rootDocument: instance.rootDocument,
326
326
  onThemeChange: themeName => {
327
327
  if ((0, _rootInstance.isRootInstance)(this)) {
328
+ var _this$view;
328
329
  (0, _element.removeClass)(this.rootWrapperElement, /ht-theme-.*/g);
329
330
  (0, _element.removeClass)(this.rootPortalElement, /ht-theme-.*/g);
330
331
  if (themeName) {
@@ -334,6 +335,7 @@ function Core(rootContainer, userSettings) {
334
335
  (0, _console.warn)(`The "${themeName}" theme is enabled, but its stylesheets are missing` + ' or not imported correctly. Import the correct CSS files in order to use that theme.');
335
336
  }
336
337
  }
338
+ (_this$view = this.view) === null || _this$view === void 0 || (_this$view = _this$view._wt) === null || _this$view === void 0 || (_this$view = _this$view.selectionManager) === null || _this$view === void 0 || _this$view.refreshAllBorderHandleStyles();
337
339
  }
338
340
  },
339
341
  injectCoreCss: typeof (userSettings === null || userSettings === void 0 ? void 0 : userSettings.injectCoreCss) === 'boolean' ? userSettings.injectCoreCss : true
@@ -1211,7 +1213,7 @@ function Core(rootContainer, userSettings) {
1211
1213
  focusGridManager.init();
1212
1214
  if ((0, _rootInstance.isRootInstance)(this)) {
1213
1215
  (0, _a11yAnnouncer.install)(instance.rootPortalElement);
1214
- (0, _mixed._injectProductInfo)(mergedUserSettings.licenseKey, this.rootWrapperElement, "09/03/2026");
1216
+ (0, _mixed._injectProductInfo)(mergedUserSettings.licenseKey, this.rootWrapperElement, "23/03/2026");
1215
1217
  }
1216
1218
  instance.runHooks('init');
1217
1219
  this.render();
@@ -2616,7 +2618,13 @@ function Core(rootContainer, userSettings) {
2616
2618
  initializeThemeManager(settings.theme);
2617
2619
  instance.useTheme(instance.themeManager.getClassName());
2618
2620
  } else {
2619
- instance.themeManager.update(settings.theme);
2621
+ let themeObject;
2622
+ if (typeof settings.theme.getThemeConfig !== 'function') {
2623
+ themeObject = (0, _themes2.registerTheme)(settings.theme);
2624
+ } else {
2625
+ themeObject = settings.theme;
2626
+ }
2627
+ instance.themeManager.update(themeObject);
2620
2628
  instance.useTheme(instance.themeManager.getClassName());
2621
2629
  }
2622
2630
  }
package/core.mjs CHANGED
@@ -320,6 +320,7 @@ export default function Core(rootContainer, userSettings) {
320
320
  rootDocument: instance.rootDocument,
321
321
  onThemeChange: themeName => {
322
322
  if (isRootInstance(this)) {
323
+ var _this$view;
323
324
  removeClass(this.rootWrapperElement, /ht-theme-.*/g);
324
325
  removeClass(this.rootPortalElement, /ht-theme-.*/g);
325
326
  if (themeName) {
@@ -329,6 +330,7 @@ export default function Core(rootContainer, userSettings) {
329
330
  warn(`The "${themeName}" theme is enabled, but its stylesheets are missing` + ' or not imported correctly. Import the correct CSS files in order to use that theme.');
330
331
  }
331
332
  }
333
+ (_this$view = this.view) === null || _this$view === void 0 || (_this$view = _this$view._wt) === null || _this$view === void 0 || (_this$view = _this$view.selectionManager) === null || _this$view === void 0 || _this$view.refreshAllBorderHandleStyles();
332
334
  }
333
335
  },
334
336
  injectCoreCss: typeof (userSettings === null || userSettings === void 0 ? void 0 : userSettings.injectCoreCss) === 'boolean' ? userSettings.injectCoreCss : true
@@ -1206,7 +1208,7 @@ export default function Core(rootContainer, userSettings) {
1206
1208
  focusGridManager.init();
1207
1209
  if (isRootInstance(this)) {
1208
1210
  installAccessibilityAnnouncer(instance.rootPortalElement);
1209
- _injectProductInfo(mergedUserSettings.licenseKey, this.rootWrapperElement, "09/03/2026");
1211
+ _injectProductInfo(mergedUserSettings.licenseKey, this.rootWrapperElement, "23/03/2026");
1210
1212
  }
1211
1213
  instance.runHooks('init');
1212
1214
  this.render();
@@ -2611,7 +2613,13 @@ export default function Core(rootContainer, userSettings) {
2611
2613
  initializeThemeManager(settings.theme);
2612
2614
  instance.useTheme(instance.themeManager.getClassName());
2613
2615
  } else {
2614
- instance.themeManager.update(settings.theme);
2616
+ let themeObject;
2617
+ if (typeof settings.theme.getThemeConfig !== 'function') {
2618
+ themeObject = registerTheme(settings.theme);
2619
+ } else {
2620
+ themeObject = settings.theme;
2621
+ }
2622
+ instance.themeManager.update(themeObject);
2615
2623
  instance.useTheme(instance.themeManager.getClassName());
2616
2624
  }
2617
2625
  }
@@ -4953,7 +4953,7 @@ var _default = () => {
4953
4953
  * @description
4954
4954
  * The `skipColumnOnPaste` option determines whether you can paste data into a given column.
4955
4955
  *
4956
- * You can only apply the `skipColumnOnPaste` option to an entire column, using the [`columns`](#columns) option.
4956
+ * You can only apply the `skipColumnOnPaste` option to an entire column, using the [`columns`](#columns) option. This option is not supported for the global table level settings.
4957
4957
  *
4958
4958
  * You can set the `skipColumnOnPaste` option to one of the following:
4959
4959
  *
@@ -4986,7 +4986,7 @@ var _default = () => {
4986
4986
  *
4987
4987
  * The `skipRowOnPaste` option determines whether you can paste data into a given row.
4988
4988
  *
4989
- * You can only apply the `skipRowOnPaste` option to an entire row, using the [`cells`](#cells) option.
4989
+ * You can only apply the `skipRowOnPaste` option to an entire row, using the [`cells`](#cells) option. This option is not supported for the global table level settings.
4990
4990
  *
4991
4991
  * You can set the `skipRowOnPaste` option to one of the following:
4992
4992
  *
@@ -4950,7 +4950,7 @@ export default () => {
4950
4950
  * @description
4951
4951
  * The `skipColumnOnPaste` option determines whether you can paste data into a given column.
4952
4952
  *
4953
- * You can only apply the `skipColumnOnPaste` option to an entire column, using the [`columns`](#columns) option.
4953
+ * You can only apply the `skipColumnOnPaste` option to an entire column, using the [`columns`](#columns) option. This option is not supported for the global table level settings.
4954
4954
  *
4955
4955
  * You can set the `skipColumnOnPaste` option to one of the following:
4956
4956
  *
@@ -4983,7 +4983,7 @@ export default () => {
4983
4983
  *
4984
4984
  * The `skipRowOnPaste` option determines whether you can paste data into a given row.
4985
4985
  *
4986
- * You can only apply the `skipRowOnPaste` option to an entire row, using the [`cells`](#cells) option.
4986
+ * You can only apply the `skipRowOnPaste` option to an entire row, using the [`cells`](#cells) option. This option is not supported for the global table level settings.
4987
4987
  *
4988
4988
  * You can set the `skipRowOnPaste` option to one of the following:
4989
4989
  *