handsontable 0.0.0-next-ff10728-20250410 → 0.0.0-next-9410a76-20250416

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 (106) hide show
  1. package/3rdparty/walkontable/src/cell/range.js +14 -0
  2. package/3rdparty/walkontable/src/cell/range.mjs +14 -0
  3. package/3rdparty/walkontable/src/renderer/rowHeaders.js +4 -1
  4. package/3rdparty/walkontable/src/renderer/rowHeaders.mjs +4 -1
  5. package/3rdparty/walkontable/src/selection/border/border.js +5 -0
  6. package/3rdparty/walkontable/src/selection/border/border.mjs +5 -0
  7. package/3rdparty/walkontable/src/table/mixin/stickyColumnsStart.js +3 -4
  8. package/3rdparty/walkontable/src/table/mixin/stickyColumnsStart.mjs +3 -4
  9. package/3rdparty/walkontable/src/table/mixin/stickyRowsBottom.js +8 -10
  10. package/3rdparty/walkontable/src/table/mixin/stickyRowsBottom.mjs +8 -10
  11. package/3rdparty/walkontable/src/table/mixin/stickyRowsTop.js +3 -4
  12. package/3rdparty/walkontable/src/table/mixin/stickyRowsTop.mjs +3 -4
  13. package/3rdparty/walkontable/src/table.js +5 -2
  14. package/3rdparty/walkontable/src/table.mjs +5 -2
  15. package/CHANGELOG.md +39 -0
  16. package/base.js +2 -2
  17. package/base.mjs +2 -2
  18. package/core/focusCatcher/focusDetector.js +1 -1
  19. package/core/focusCatcher/focusDetector.mjs +2 -2
  20. package/core/hooks/constants.js +8 -0
  21. package/core/hooks/constants.mjs +8 -0
  22. package/core/hooks/index.d.ts +1 -0
  23. package/core.d.ts +0 -1
  24. package/core.js +32 -25
  25. package/core.mjs +33 -26
  26. package/dataMap/dataMap.js +0 -7
  27. package/dataMap/dataMap.mjs +0 -7
  28. package/dataMap/metaManager/metaSchema.js +1 -0
  29. package/dataMap/metaManager/metaSchema.mjs +1 -0
  30. package/dist/handsontable.css +3 -3
  31. package/dist/handsontable.full.css +3 -3
  32. package/dist/handsontable.full.js +437 -350
  33. package/dist/handsontable.full.min.css +3 -3
  34. package/dist/handsontable.full.min.js +17 -17
  35. package/dist/handsontable.js +437 -350
  36. package/dist/handsontable.min.css +3 -3
  37. package/dist/handsontable.min.js +18 -18
  38. package/editorManager.js +1 -7
  39. package/editorManager.mjs +1 -7
  40. package/editors/autocompleteEditor/autocompleteEditor.js +14 -4
  41. package/editors/autocompleteEditor/autocompleteEditor.mjs +14 -4
  42. package/editors/textEditor/textEditor.js +1 -1
  43. package/editors/textEditor/textEditor.mjs +2 -2
  44. package/helpers/browser.js +1 -1
  45. package/helpers/browser.mjs +1 -1
  46. package/helpers/dom/element.js +2 -2
  47. package/helpers/dom/element.mjs +1 -1
  48. package/helpers/mixed.js +2 -2
  49. package/helpers/mixed.mjs +2 -2
  50. package/helpers/object.js +3 -0
  51. package/helpers/object.mjs +3 -0
  52. package/package.json +1 -1
  53. package/plugins/autoColumnSize/autoColumnSize.js +1 -1
  54. package/plugins/autoColumnSize/autoColumnSize.mjs +1 -1
  55. package/plugins/autoRowSize/autoRowSize.js +1 -6
  56. package/plugins/autoRowSize/autoRowSize.mjs +1 -6
  57. package/plugins/columnSorting/columnSorting.js +0 -4
  58. package/plugins/columnSorting/columnSorting.mjs +0 -4
  59. package/plugins/comments/comments.js +1 -0
  60. package/plugins/comments/comments.mjs +1 -0
  61. package/plugins/contextMenu/menu/defaultShortcutsList.js +2 -2
  62. package/plugins/contextMenu/menu/defaultShortcutsList.mjs +2 -2
  63. package/plugins/contextMenu/menu/menu.js +1 -0
  64. package/plugins/contextMenu/menu/menu.mjs +1 -0
  65. package/plugins/contextMenu/menu/positioner.js +10 -2
  66. package/plugins/contextMenu/menu/positioner.mjs +10 -2
  67. package/plugins/copyPaste/copyPaste.js +12 -15
  68. package/plugins/copyPaste/copyPaste.mjs +13 -16
  69. package/plugins/copyPaste/pasteEvent.js +3 -0
  70. package/plugins/copyPaste/pasteEvent.mjs +3 -0
  71. package/plugins/filters/filters.js +25 -24
  72. package/plugins/filters/filters.mjs +25 -24
  73. package/plugins/filters/ui/multipleSelect.js +7 -1
  74. package/plugins/filters/ui/multipleSelect.mjs +7 -1
  75. package/plugins/hiddenColumns/hiddenColumns.js +1 -1
  76. package/plugins/hiddenColumns/hiddenColumns.mjs +1 -1
  77. package/plugins/hiddenRows/hiddenRows.js +1 -1
  78. package/plugins/hiddenRows/hiddenRows.mjs +1 -1
  79. package/plugins/manualColumnResize/manualColumnResize.js +4 -6
  80. package/plugins/manualColumnResize/manualColumnResize.mjs +4 -6
  81. package/plugins/manualRowResize/manualRowResize.js +4 -6
  82. package/plugins/manualRowResize/manualRowResize.mjs +4 -6
  83. package/plugins/mergeCells/mergeCells.js +8 -29
  84. package/plugins/mergeCells/mergeCells.mjs +8 -29
  85. package/plugins/mergeCells/renderer.js +15 -0
  86. package/plugins/mergeCells/renderer.mjs +15 -0
  87. package/plugins/mergeCells/utils.js +31 -0
  88. package/plugins/mergeCells/utils.mjs +27 -0
  89. package/plugins/nestedRows/data/dataManager.js +2 -2
  90. package/plugins/nestedRows/data/dataManager.mjs +2 -2
  91. package/plugins/undoRedo/actions/removeColumn.js +19 -14
  92. package/plugins/undoRedo/actions/removeColumn.mjs +19 -14
  93. package/plugins/undoRedo/actions/removeRow.js +12 -4
  94. package/plugins/undoRedo/actions/removeRow.mjs +12 -4
  95. package/selection/selection.js +3 -1
  96. package/selection/selection.mjs +3 -1
  97. package/styles/handsontable.css +14 -15
  98. package/styles/handsontable.min.css +3 -3
  99. package/styles/ht-theme-horizon.css +2 -2
  100. package/styles/ht-theme-horizon.min.css +2 -2
  101. package/styles/ht-theme-main.css +2 -2
  102. package/styles/ht-theme-main.min.css +2 -2
  103. package/tableView.js +5 -8
  104. package/tableView.mjs +5 -8
  105. package/utils/ghostTable.js +3 -0
  106. package/utils/ghostTable.mjs +3 -0
@@ -105,6 +105,20 @@ class CellRange {
105
105
  return this;
106
106
  }
107
107
 
108
+ /**
109
+ * Normalizes the coordinates in your `CellRange` instance to the nearest valid position.
110
+ *
111
+ * Coordinates that point to headers (negative values) are normalized to `0`.
112
+ *
113
+ * @returns {CellRange}
114
+ */
115
+ normalize() {
116
+ this.highlight.normalize();
117
+ this.from.normalize();
118
+ this.to.normalize();
119
+ return this;
120
+ }
121
+
108
122
  /**
109
123
  * Checks if the coordinates in your `CellRange` instance are valid
110
124
  * in the context of given table parameters.
@@ -101,6 +101,20 @@ class CellRange {
101
101
  return this;
102
102
  }
103
103
 
104
+ /**
105
+ * Normalizes the coordinates in your `CellRange` instance to the nearest valid position.
106
+ *
107
+ * Coordinates that point to headers (negative values) are normalized to `0`.
108
+ *
109
+ * @returns {CellRange}
110
+ */
111
+ normalize() {
112
+ this.highlight.normalize();
113
+ this.from.normalize();
114
+ this.to.normalize();
115
+ return this;
116
+ }
117
+
104
118
  /**
105
119
  * Checks if the coordinates in your `CellRange` instance are valid
106
120
  * in the context of given table parameters.
@@ -74,7 +74,10 @@ class RowHeadersRenderer extends _base.BaseRenderer {
74
74
  const orderView = this.obtainOrderView(TR);
75
75
  const cellsView = cells.obtainOrderView(TR);
76
76
  orderView.appendView(cellsView).setSize(rowHeadersCount).setOffset(0).start();
77
- for (let visibleColumnIndex = 0; visibleColumnIndex < rowHeadersCount; visibleColumnIndex++) {
77
+
78
+ // Reading the row header renderers in reverse because of how the Eco Renderers handle rendering
79
+ // (prepending the nodes when rendering row headers).
80
+ for (let visibleColumnIndex = rowHeadersCount - 1; visibleColumnIndex >= 0; visibleColumnIndex--) {
78
81
  orderView.render();
79
82
  const TH = orderView.getCurrentNode();
80
83
  TH.className = '';
@@ -71,7 +71,10 @@ export class RowHeadersRenderer extends BaseRenderer {
71
71
  const orderView = this.obtainOrderView(TR);
72
72
  const cellsView = cells.obtainOrderView(TR);
73
73
  orderView.appendView(cellsView).setSize(rowHeadersCount).setOffset(0).start();
74
- for (let visibleColumnIndex = 0; visibleColumnIndex < rowHeadersCount; visibleColumnIndex++) {
74
+
75
+ // Reading the row header renderers in reverse because of how the Eco Renderers handle rendering
76
+ // (prepending the nodes when rendering row headers).
77
+ for (let visibleColumnIndex = rowHeadersCount - 1; visibleColumnIndex >= 0; visibleColumnIndex--) {
75
78
  orderView.render();
76
79
  const TH = orderView.getCurrentNode();
77
80
  TH.className = '';
@@ -352,6 +352,11 @@ class Border {
352
352
  const lastRenderedRow = wtTable.getLastRenderedRow();
353
353
  const firstRenderedColumn = wtTable.getFirstRenderedColumn();
354
354
  const lastRenderedColumn = wtTable.getLastRenderedColumn();
355
+ if (firstRenderedColumn < 0 && lastRenderedColumn < 0 || firstRenderedRow < 0 && lastRenderedRow < 0) {
356
+ // ...also when overlays have rendered only headers skip it
357
+ this.disappear();
358
+ return;
359
+ }
355
360
  let fromTD;
356
361
  if (isMultiple) {
357
362
  fromColumn = Math.max(fromColumn, firstRenderedColumn);
@@ -349,6 +349,11 @@ class Border {
349
349
  const lastRenderedRow = wtTable.getLastRenderedRow();
350
350
  const firstRenderedColumn = wtTable.getFirstRenderedColumn();
351
351
  const lastRenderedColumn = wtTable.getLastRenderedColumn();
352
+ if (firstRenderedColumn < 0 && lastRenderedColumn < 0 || firstRenderedRow < 0 && lastRenderedRow < 0) {
353
+ // ...also when overlays have rendered only headers skip it
354
+ this.disappear();
355
+ return;
356
+ }
352
357
  let fromTD;
353
358
  if (isMultiple) {
354
359
  fromColumn = Math.max(fromColumn, firstRenderedColumn);
@@ -20,8 +20,8 @@ const stickyColumnsStart = {
20
20
  * @this Table
21
21
  */
22
22
  getFirstRenderedColumn() {
23
- const totalColumns = this.wtSettings.getSetting('totalColumns');
24
- if (totalColumns === 0) {
23
+ const allStickyColumns = this.getRenderedColumnsCount();
24
+ if (allStickyColumns === 0) {
25
25
  return -1;
26
26
  }
27
27
  return 0;
@@ -82,8 +82,7 @@ const stickyColumnsStart = {
82
82
  * @this Table
83
83
  */
84
84
  getRenderedColumnsCount() {
85
- const totalColumns = this.wtSettings.getSetting('totalColumns');
86
- return Math.min(this.wtSettings.getSetting('fixedColumnsStart'), totalColumns);
85
+ return Math.min(this.wtSettings.getSetting('totalColumns'), this.wtSettings.getSetting('fixedColumnsStart'));
87
86
  },
88
87
  /**
89
88
  * Get the number of fully visible columns in the viewport.
@@ -17,8 +17,8 @@ const stickyColumnsStart = {
17
17
  * @this Table
18
18
  */
19
19
  getFirstRenderedColumn() {
20
- const totalColumns = this.wtSettings.getSetting('totalColumns');
21
- if (totalColumns === 0) {
20
+ const allStickyColumns = this.getRenderedColumnsCount();
21
+ if (allStickyColumns === 0) {
22
22
  return -1;
23
23
  }
24
24
  return 0;
@@ -79,8 +79,7 @@ const stickyColumnsStart = {
79
79
  * @this Table
80
80
  */
81
81
  getRenderedColumnsCount() {
82
- const totalColumns = this.wtSettings.getSetting('totalColumns');
83
- return Math.min(this.wtSettings.getSetting('fixedColumnsStart'), totalColumns);
82
+ return Math.min(this.wtSettings.getSetting('totalColumns'), this.wtSettings.getSetting('fixedColumnsStart'));
84
83
  },
85
84
  /**
86
85
  * Get the number of fully visible columns in the viewport.
@@ -20,16 +20,11 @@ const stickyRowsBottom = {
20
20
  * @this Table
21
21
  */
22
22
  getFirstRenderedRow() {
23
- const totalRows = this.wtSettings.getSetting('totalRows');
24
- const fixedRowsBottom = this.wtSettings.getSetting('fixedRowsBottom');
25
- const index = totalRows - fixedRowsBottom;
26
- if (totalRows === 0 || fixedRowsBottom === 0) {
23
+ const allStickyRows = this.getRenderedRowsCount();
24
+ if (allStickyRows === 0) {
27
25
  return -1;
28
26
  }
29
- if (index < 0) {
30
- return 0;
31
- }
32
- return index;
27
+ return this.wtSettings.getSetting('totalRows') - allStickyRows;
33
28
  },
34
29
  /**
35
30
  * Get the source index of the first row fully visible in the viewport. If no rows are fully visible, returns an error code: -1.
@@ -58,6 +53,10 @@ const stickyRowsBottom = {
58
53
  * @this Table
59
54
  */
60
55
  getLastRenderedRow() {
56
+ const allStickyRows = this.getRenderedRowsCount();
57
+ if (allStickyRows === 0) {
58
+ return -1;
59
+ }
61
60
  return this.wtSettings.getSetting('totalRows') - 1;
62
61
  },
63
62
  /**
@@ -87,8 +86,7 @@ const stickyRowsBottom = {
87
86
  * @this Table
88
87
  */
89
88
  getRenderedRowsCount() {
90
- const totalRows = this.wtSettings.getSetting('totalRows');
91
- return Math.min(this.wtSettings.getSetting('fixedRowsBottom'), totalRows);
89
+ return Math.min(this.wtSettings.getSetting('totalRows'), this.wtSettings.getSetting('fixedRowsBottom'));
92
90
  },
93
91
  /**
94
92
  * Get the number of fully visible rows in the viewport.
@@ -17,16 +17,11 @@ const stickyRowsBottom = {
17
17
  * @this Table
18
18
  */
19
19
  getFirstRenderedRow() {
20
- const totalRows = this.wtSettings.getSetting('totalRows');
21
- const fixedRowsBottom = this.wtSettings.getSetting('fixedRowsBottom');
22
- const index = totalRows - fixedRowsBottom;
23
- if (totalRows === 0 || fixedRowsBottom === 0) {
20
+ const allStickyRows = this.getRenderedRowsCount();
21
+ if (allStickyRows === 0) {
24
22
  return -1;
25
23
  }
26
- if (index < 0) {
27
- return 0;
28
- }
29
- return index;
24
+ return this.wtSettings.getSetting('totalRows') - allStickyRows;
30
25
  },
31
26
  /**
32
27
  * Get the source index of the first row fully visible in the viewport. If no rows are fully visible, returns an error code: -1.
@@ -55,6 +50,10 @@ const stickyRowsBottom = {
55
50
  * @this Table
56
51
  */
57
52
  getLastRenderedRow() {
53
+ const allStickyRows = this.getRenderedRowsCount();
54
+ if (allStickyRows === 0) {
55
+ return -1;
56
+ }
58
57
  return this.wtSettings.getSetting('totalRows') - 1;
59
58
  },
60
59
  /**
@@ -84,8 +83,7 @@ const stickyRowsBottom = {
84
83
  * @this Table
85
84
  */
86
85
  getRenderedRowsCount() {
87
- const totalRows = this.wtSettings.getSetting('totalRows');
88
- return Math.min(this.wtSettings.getSetting('fixedRowsBottom'), totalRows);
86
+ return Math.min(this.wtSettings.getSetting('totalRows'), this.wtSettings.getSetting('fixedRowsBottom'));
89
87
  },
90
88
  /**
91
89
  * Get the number of fully visible rows in the viewport.
@@ -20,8 +20,8 @@ const stickyRowsTop = {
20
20
  * @this Table
21
21
  */
22
22
  getFirstRenderedRow() {
23
- const totalRows = this.wtSettings.getSetting('totalRows');
24
- if (totalRows === 0) {
23
+ const allStickyRows = this.getRenderedRowsCount();
24
+ if (allStickyRows === 0) {
25
25
  return -1;
26
26
  }
27
27
  return 0;
@@ -82,8 +82,7 @@ const stickyRowsTop = {
82
82
  * @this Table
83
83
  */
84
84
  getRenderedRowsCount() {
85
- const totalRows = this.wtSettings.getSetting('totalRows');
86
- return Math.min(this.wtSettings.getSetting('fixedRowsTop'), totalRows);
85
+ return Math.min(this.wtSettings.getSetting('totalRows'), this.wtSettings.getSetting('fixedRowsTop'));
87
86
  },
88
87
  /**
89
88
  * Get the number of fully visible rows in the viewport.
@@ -17,8 +17,8 @@ const stickyRowsTop = {
17
17
  * @this Table
18
18
  */
19
19
  getFirstRenderedRow() {
20
- const totalRows = this.wtSettings.getSetting('totalRows');
21
- if (totalRows === 0) {
20
+ const allStickyRows = this.getRenderedRowsCount();
21
+ if (allStickyRows === 0) {
22
22
  return -1;
23
23
  }
24
24
  return 0;
@@ -79,8 +79,7 @@ const stickyRowsTop = {
79
79
  * @this Table
80
80
  */
81
81
  getRenderedRowsCount() {
82
- const totalRows = this.wtSettings.getSetting('totalRows');
83
- return Math.min(this.wtSettings.getSetting('fixedRowsTop'), totalRows);
82
+ return Math.min(this.wtSettings.getSetting('totalRows'), this.wtSettings.getSetting('fixedRowsTop'));
84
83
  },
85
84
  /**
86
85
  * Get the number of fully visible rows in the viewport.
@@ -279,8 +279,8 @@ class Table {
279
279
  } else {
280
280
  this.tableOffset = this.dataAccessObject.parentTableOffset;
281
281
  }
282
- const startRow = totalRows > 0 ? this.getFirstRenderedRow() : 0;
283
- const startColumn = totalColumns > 0 ? this.getFirstRenderedColumn() : 0;
282
+ const startRow = Math.max(this.getFirstRenderedRow(), 0);
283
+ const startColumn = Math.max(this.getFirstRenderedColumn(), 0);
284
284
  this.rowFilter = new _row.default(startRow, totalRows, columnHeadersCount);
285
285
  this.columnFilter = new _column.default(startColumn, totalColumns, rowHeadersCount);
286
286
  let performRedraw = true;
@@ -598,6 +598,9 @@ class Table {
598
598
  return null;
599
599
  }
600
600
  const TR = cellElement.parentNode;
601
+ if (!TR) {
602
+ return null;
603
+ }
601
604
  const CONTAINER = TR.parentNode;
602
605
  let row = (0, _element.index)(TR);
603
606
  let col = cellElement.cellIndex;
@@ -275,8 +275,8 @@ class Table {
275
275
  } else {
276
276
  this.tableOffset = this.dataAccessObject.parentTableOffset;
277
277
  }
278
- const startRow = totalRows > 0 ? this.getFirstRenderedRow() : 0;
279
- const startColumn = totalColumns > 0 ? this.getFirstRenderedColumn() : 0;
278
+ const startRow = Math.max(this.getFirstRenderedRow(), 0);
279
+ const startColumn = Math.max(this.getFirstRenderedColumn(), 0);
280
280
  this.rowFilter = new RowFilter(startRow, totalRows, columnHeadersCount);
281
281
  this.columnFilter = new ColumnFilter(startColumn, totalColumns, rowHeadersCount);
282
282
  let performRedraw = true;
@@ -594,6 +594,9 @@ class Table {
594
594
  return null;
595
595
  }
596
596
  const TR = cellElement.parentNode;
597
+ if (!TR) {
598
+ return null;
599
+ }
597
600
  const CONTAINER = TR.parentNode;
598
601
  let row = index(TR);
599
602
  let col = cellElement.cellIndex;
package/CHANGELOG.md CHANGED
@@ -9,6 +9,45 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
9
9
 
10
10
  <!-- UNVERSIONED -->
11
11
 
12
+ ## [15.2.0] - 2025-03-19
13
+
14
+ ### Added
15
+ - Added the Farsi translation. [#11388](https://github.com/handsontable/handsontable/pull/11388)
16
+ - Added support for class names passed as an array for the numeric cell renderer. [#11420](https://github.com/handsontable/handsontable/pull/11420)
17
+ - Updated the Italian translation for the new Context Menu labels. [#11436](https://github.com/handsontable/handsontable/pull/11436)
18
+ - Updated the Serbian translation for the new Context Menu labels. [#11437](https://github.com/handsontable/handsontable/pull/11437)
19
+ - Added the mobile cell handle CSS variables to the themes. [#11479](https://github.com/handsontable/handsontable/pull/11479)
20
+ - Improved the execution flow of the Filters plugin and added two new methods (`importConditions` and `exportConditions`). [#11488](https://github.com/handsontable/handsontable/pull/11488)
21
+
22
+ ### Changed
23
+ - Sped up the rendering performance for themes. [#11443](https://github.com/handsontable/handsontable/pull/11443)
24
+ - Improved the table UI behavior after removing all rows and/or columns. [#11477](https://github.com/handsontable/handsontable/pull/11477)
25
+ - Reverted the removal of `actionType` class field for UndoRedo actions. [#11495](https://github.com/handsontable/handsontable/pull/11495)
26
+
27
+ ### Removed
28
+ - Removed the broken, unsupported and undocumented `rendererTemplate` Text Renderer option. [#11424](https://github.com/handsontable/handsontable/pull/11424)
29
+
30
+ ### Fixed
31
+ - Fixed the default (fallback) date format for the Date editor. [#11419](https://github.com/handsontable/handsontable/pull/11419)
32
+ - Fixed the Context Menu's items' state rendering. [#11422](https://github.com/handsontable/handsontable/pull/11422)
33
+ - Corrected the checkbox visibility in the no-theme variant. [#11427](https://github.com/handsontable/handsontable/pull/11427)
34
+ - Fixed problems with the cell content reading with `imeFastEdit` enabled. [#11442](https://github.com/handsontable/handsontable/pull/11442)
35
+ - Fixed `hasVerticalScroll` and `hasHorizontalScroll` methods. [#11455](https://github.com/handsontable/handsontable/pull/11455)
36
+ - Fixed the editor border radius on mobile devices. [#11457](https://github.com/handsontable/handsontable/pull/11457)
37
+ - Fixed the wrong height of the first row. [#11458](https://github.com/handsontable/handsontable/pull/11458)
38
+ - Fixed the single cell selection inside iframes. [#11460](https://github.com/handsontable/handsontable/pull/11460)
39
+ - Fixed an issue with row header misalignment. [#11465](https://github.com/handsontable/handsontable/pull/11465)
40
+ - Improved the Autocomplete/Dropdown Editor list behavior. [#11469](https://github.com/handsontable/handsontable/pull/11469)
41
+ - Fixed a problem with horizontal scrollbar in the Autocomplete dropdown. [#11473](https://github.com/handsontable/handsontable/pull/11473)
42
+ - Fixed the merged cells height for custom row heights. [#11478](https://github.com/handsontable/handsontable/pull/11478)
43
+ - Fixed a problem with the Comments editor not flipping direction when overlapping the window's scrollbars. [#11481](https://github.com/handsontable/handsontable/pull/11481)
44
+ - Fixed a problem with the focus trap during `Shift` + `Tab` navigation. [#11483](https://github.com/handsontable/handsontable/pull/11483)
45
+ - Fixed an issue with the Context Menu opening on Classic theme. [#11486](https://github.com/handsontable/handsontable/pull/11486)
46
+ - Fixed a problem with `rowHeights` when the provided value was lower than the default/minimal row height. [#11487](https://github.com/handsontable/handsontable/pull/11487)
47
+ - Fixed an issue with the data source modification for row values passed as strings. [#11491](https://github.com/handsontable/handsontable/pull/11491)
48
+ - Fixed the conflicts between ColumnSorting and MultiColumnSorting plugins. [#11492](https://github.com/handsontable/handsontable/pull/11492)
49
+ - Fixed copy, cut an paste actions on tables with a selection reaching outside of the rendered viewport. [#11504](https://github.com/handsontable/handsontable/pull/11504)
50
+
12
51
  ## [15.1.0] - 2025-02-20
13
52
 
14
53
  ### Added
package/base.js CHANGED
@@ -45,8 +45,8 @@ Handsontable.hooks = _hooks.Hooks.getSingleton();
45
45
  Handsontable.CellCoords = _src.CellCoords;
46
46
  Handsontable.CellRange = _src.CellRange;
47
47
  Handsontable.packageName = 'handsontable';
48
- Handsontable.buildDate = "10/04/2025 06:43:46";
49
- Handsontable.version = "0.0.0-next-ff10728-20250410";
48
+ Handsontable.buildDate = "16/04/2025 06:18:04";
49
+ Handsontable.version = "0.0.0-next-9410a76-20250416";
50
50
  Handsontable.languages = {
51
51
  dictionaryKeys: _registry.dictionaryKeys,
52
52
  getLanguageDictionary: _registry.getLanguageDictionary,
package/base.mjs CHANGED
@@ -35,8 +35,8 @@ Handsontable.hooks = Hooks.getSingleton();
35
35
  Handsontable.CellCoords = CellCoords;
36
36
  Handsontable.CellRange = CellRange;
37
37
  Handsontable.packageName = 'handsontable';
38
- Handsontable.buildDate = "10/04/2025 06:43:52";
39
- Handsontable.version = "0.0.0-next-ff10728-20250410";
38
+ Handsontable.buildDate = "16/04/2025 06:18:10";
39
+ Handsontable.version = "0.0.0-next-9410a76-20250416";
40
40
  Handsontable.languages = {
41
41
  dictionaryKeys,
42
42
  getLanguageDictionary,
@@ -58,7 +58,7 @@ function createInputElement(hot) {
58
58
  input.name = '__htFocusCatcher';
59
59
  input.classList.add('htFocusCatcher');
60
60
  if (hot.getSettings().ariaTags) {
61
- (0, _element.setAttribute)(input, [(0, _a11y.A11Y_PRESENTATION)(), (0, _a11y.A11Y_HIDDEN)()]);
61
+ (0, _element.setAttribute)(input, [(0, _a11y.A11Y_LABEL)('Focus catcher')]);
62
62
  }
63
63
  return input;
64
64
  }
@@ -1,5 +1,5 @@
1
1
  import { setAttribute } from "../../helpers/dom/element.mjs";
2
- import { A11Y_PRESENTATION, A11Y_HIDDEN } from "../../helpers/a11y.mjs";
2
+ import { A11Y_LABEL } from "../../helpers/a11y.mjs";
3
3
  /**
4
4
  * Installs a focus detector module. The module appends two input elements into the DOM side by side.
5
5
  * When the first input is focused, then it means that a user entered to the component using the TAB key
@@ -54,7 +54,7 @@ function createInputElement(hot) {
54
54
  input.name = '__htFocusCatcher';
55
55
  input.classList.add('htFocusCatcher');
56
56
  if (hot.getSettings().ariaTags) {
57
- setAttribute(input, [A11Y_PRESENTATION(), A11Y_HIDDEN()]);
57
+ setAttribute(input, [A11Y_LABEL('Focus catcher')]);
58
58
  }
59
59
  return input;
60
60
  }
@@ -336,6 +336,14 @@ const REGISTERED_HOOKS = exports.REGISTERED_HOOKS = [/* eslint-disable jsdoc/req
336
336
  * @returns {string[]|undefined} Can return an `Array` of `String`s. Each of these strings will act like class names to be removed from all the cells in the table.
337
337
  */
338
338
  'beforeRemoveCellClassNames',
339
+ /**
340
+ * Hook fired after `compositionstart` event is handled.
341
+ *
342
+ * @event Hooks#beforeCompositionStart
343
+ * @since 15.3.0
344
+ * @param {Event} event A native `composition` event object.
345
+ */
346
+ 'beforeCompositionStart',
339
347
  /**
340
348
  * Fired after getting the cell settings.
341
349
  *
@@ -333,6 +333,14 @@ export const REGISTERED_HOOKS = [/* eslint-disable jsdoc/require-description-com
333
333
  * @returns {string[]|undefined} Can return an `Array` of `String`s. Each of these strings will act like class names to be removed from all the cells in the table.
334
334
  */
335
335
  'beforeRemoveCellClassNames',
336
+ /**
337
+ * Hook fired after `compositionstart` event is handled.
338
+ *
339
+ * @event Hooks#beforeCompositionStart
340
+ * @since 15.3.0
341
+ * @param {Event} event A native `composition` event object.
342
+ */
343
+ 'beforeCompositionStart',
336
344
  /**
337
345
  * Fired after getting the cell settings.
338
346
  *
@@ -167,6 +167,7 @@ export interface Events {
167
167
  beforeColumnSort?: (currentSortConfig: ColumnSortingConfig[], destinationSortConfigs: ColumnSortingConfig[]) => void | boolean;
168
168
  beforeColumnWrap?: (isActionInterrupted: { value: boolean }, newCoords: CellCoords, isColumnFlipped: boolean) => void;
169
169
  beforeColumnUnfreeze?: (columnIndex: number, isUnfreezingPerformed: boolean) => void | boolean;
170
+ beforeCompositionStart?: (event: CompositionEvent) => void;
170
171
  beforeContextMenuSetItems?: (menuItems: ContextMenuMenuItemConfig[]) => void;
171
172
  beforeContextMenuShow?: (context: ContextMenu) => void;
172
173
  beforeCopy?: (data: CellValue[][], coords: RangeType[], copiedHeadersCount: { columnHeadersCount: number }) => void | boolean;
package/core.d.ts CHANGED
@@ -140,7 +140,6 @@ export default class Core {
140
140
  removeCellMeta(row: number, column: number, key: (keyof CellMeta) | string): void;
141
141
  removeHook<K extends keyof Events>(key: K, callback: Events[K]): void;
142
142
  render(): void;
143
- renderCall: boolean;
144
143
  resumeExecution(): void;
145
144
  resumeRender(): void;
146
145
  rootDocument: Document;
package/core.js CHANGED
@@ -231,7 +231,6 @@ function Core(rootElement, userSettings) {
231
231
  const globalMeta = metaManager.getGlobalMeta();
232
232
  const pluginsRegistry = (0, _uniqueMap.createUniqueMap)();
233
233
  this.container = this.rootDocument.createElement('div');
234
- this.renderCall = false;
235
234
  rootElement.insertBefore(this.container, rootElement.firstChild);
236
235
  if ((0, _rootInstance.isRootInstance)(this)) {
237
236
  (0, _mixed._injectProductInfo)(userSettings.licenseKey, rootElement);
@@ -263,6 +262,9 @@ function Core(rootElement, userSettings) {
263
262
  this.rowIndexMapper.addLocalHook('indexesSequenceChange', source => {
264
263
  instance.runHooks('afterRowSequenceChange', source);
265
264
  });
265
+ eventManager.addEventListener(this.rootDocument.documentElement, 'compositionstart', event => {
266
+ instance.runHooks('beforeCompositionStart', event);
267
+ });
266
268
  dataSource = new _dataSource.default(instance);
267
269
  if (!this.rootElement.id || this.rootElement.id.substring(0, 3) === 'ht_') {
268
270
  this.rootElement.id = this.guid; // if root element does not have an id, assign a random id
@@ -342,6 +344,7 @@ function Core(rootElement, userSettings) {
342
344
  let {
343
345
  hiddenIndexesChanged
344
346
  } = _ref;
347
+ this.forceFullRender = true;
345
348
  if (hiddenIndexesChanged) {
346
349
  this.selection.commit();
347
350
  }
@@ -625,13 +628,6 @@ function Core(rootElement, userSettings) {
625
628
  }
626
629
  }
627
630
  const totalRows = instance.countRows();
628
- if (totalRows === 0) {
629
- selection.deselect();
630
- } else if (source === 'ContextMenu.removeRow') {
631
- selection.refresh();
632
- } else {
633
- selection.shiftRows(groupIndex, -groupAmount);
634
- }
635
631
  const fixedRowsTop = tableMeta.fixedRowsTop;
636
632
  if (fixedRowsTop >= calcIndex + 1) {
637
633
  tableMeta.fixedRowsTop -= Math.min(groupAmount, fixedRowsTop - calcIndex);
@@ -640,6 +636,13 @@ function Core(rootElement, userSettings) {
640
636
  if (fixedRowsBottom && calcIndex >= totalRows - fixedRowsBottom) {
641
637
  tableMeta.fixedRowsBottom -= Math.min(groupAmount, fixedRowsBottom);
642
638
  }
639
+ if (totalRows === 0) {
640
+ selection.deselect();
641
+ } else if (source === 'ContextMenu.removeRow') {
642
+ selection.refresh();
643
+ } else {
644
+ selection.shiftRows(groupIndex, -groupAmount);
645
+ }
643
646
  offset += groupAmount;
644
647
  });
645
648
  };
@@ -1049,8 +1052,7 @@ function Core(rootElement, userSettings) {
1049
1052
  (0, _index.installFocusCatcher)(instance);
1050
1053
  }
1051
1054
  instance.runHooks('init');
1052
- this.forceFullRender = true; // used when data was changed
1053
- this.view.render();
1055
+ this.render();
1054
1056
 
1055
1057
  // Run the logic only if it's the table's initialization and the root element is not visible.
1056
1058
  if (!!firstRun && instance.rootElement.offsetParent === null) {
@@ -1223,13 +1225,11 @@ function Core(rootElement, userSettings) {
1223
1225
  datamap.set(changes[i][0], changes[i][1], changes[i][3]);
1224
1226
  }
1225
1227
  const hasChanges = changes.length > 0;
1226
- instance.forceFullRender = true; // used when data was changed or when all cells need to be re-rendered
1227
-
1228
1228
  if (hasChanges) {
1229
1229
  grid.adjustRowsAndCols();
1230
1230
  instance.runHooks('beforeChangeRender', changes, source);
1231
1231
  editorManager.closeEditor();
1232
- instance.view.render();
1232
+ instance.render();
1233
1233
  editorManager.prepareEditor();
1234
1234
  instance.view.adjustElementsSize();
1235
1235
  instance.runHooks('afterChange', changes, source || 'edit');
@@ -1238,7 +1238,7 @@ function Core(rootElement, userSettings) {
1238
1238
  activeEditor.refreshValue();
1239
1239
  }
1240
1240
  } else {
1241
- instance.view.render();
1241
+ instance.render();
1242
1242
  }
1243
1243
  }
1244
1244
 
@@ -1388,9 +1388,22 @@ function Core(rootElement, userSettings) {
1388
1388
  ...tableMeta
1389
1389
  };
1390
1390
  }
1391
- if (cellProperties.type === 'numeric' && typeof newValue === 'string' && (0, _number.isNumericLike)(newValue)) {
1391
+ const {
1392
+ type,
1393
+ checkedTemplate,
1394
+ uncheckedTemplate
1395
+ } = cellProperties;
1396
+ if (type === 'numeric' && typeof newValue === 'string' && (0, _number.isNumericLike)(newValue)) {
1392
1397
  filteredChanges[i][3] = getParsedNumber(newValue);
1393
1398
  }
1399
+ if (type === 'checkbox') {
1400
+ const stringifiedValue = (0, _mixed.stringify)(newValue);
1401
+ const isChecked = stringifiedValue === (0, _mixed.stringify)(checkedTemplate);
1402
+ const isUnchecked = stringifiedValue === (0, _mixed.stringify)(uncheckedTemplate);
1403
+ if (isChecked || isUnchecked) {
1404
+ filteredChanges[i][3] = isChecked ? checkedTemplate : uncheckedTemplate;
1405
+ }
1406
+ }
1394
1407
  }
1395
1408
  return filteredChanges;
1396
1409
  }
@@ -1792,11 +1805,7 @@ function Core(rootElement, userSettings) {
1792
1805
  const nextValue = this.renderSuspendedCounter - 1;
1793
1806
  this.renderSuspendedCounter = Math.max(nextValue, 0);
1794
1807
  if (!this.isRenderSuspended() && nextValue === this.renderSuspendedCounter) {
1795
- if (this.renderCall) {
1796
- this.render();
1797
- } else {
1798
- instance.view.render();
1799
- }
1808
+ instance.view.render();
1800
1809
  }
1801
1810
  };
1802
1811
 
@@ -1812,9 +1821,8 @@ function Core(rootElement, userSettings) {
1812
1821
  */
1813
1822
  this.render = function () {
1814
1823
  if (this.view) {
1815
- this.renderCall = true;
1816
- this.forceFullRender = true; // used when data was changed or when all cells need to be re-rendered
1817
-
1824
+ // used when data was changed or when all cells need to be re-rendered (slow render)
1825
+ this.forceFullRender = true;
1818
1826
  if (!this.isRenderSuspended()) {
1819
1827
  instance.view.render();
1820
1828
  }
@@ -2427,8 +2435,7 @@ function Core(rootElement, userSettings) {
2427
2435
  }
2428
2436
  grid.adjustRowsAndCols();
2429
2437
  if (instance.view && !firstRun) {
2430
- instance.forceFullRender = true; // used when data was changed
2431
- instance.view.render();
2438
+ instance.render();
2432
2439
  instance.view._wt.wtOverlays.adjustElementsSize();
2433
2440
  }
2434
2441
  if (!init && instance.view && (currentHeight === '' || height === '' || height === undefined) && currentHeight !== height) {