apex-grid 3.5.0 → 4.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/CHANGELOG.md +573 -0
  2. package/LICENSE.md +125 -0
  3. package/README.md +143 -23
  4. package/components/cell.d.ts +43 -5
  5. package/components/cell.js +225 -64
  6. package/components/cell.js.map +1 -1
  7. package/components/filter-row.d.ts +2 -0
  8. package/components/filter-row.js +82 -28
  9. package/components/filter-row.js.map +1 -1
  10. package/components/grid.d.ts +281 -49
  11. package/components/grid.js +600 -159
  12. package/components/grid.js.map +1 -1
  13. package/components/group-header-row.js +28 -25
  14. package/components/group-header-row.js.map +1 -1
  15. package/components/header-row.d.ts +6 -10
  16. package/components/header-row.js +12 -28
  17. package/components/header-row.js.map +1 -1
  18. package/components/header.js +71 -22
  19. package/components/header.js.map +1 -1
  20. package/components/paginator.d.ts +20 -5
  21. package/components/paginator.js +54 -17
  22. package/components/paginator.js.map +1 -1
  23. package/components/row.d.ts +19 -3
  24. package/components/row.js +211 -56
  25. package/components/row.js.map +1 -1
  26. package/components/toolbar.d.ts +5 -1
  27. package/components/toolbar.js +6 -2
  28. package/components/toolbar.js.map +1 -1
  29. package/components/virtualize-rows.d.ts +38 -0
  30. package/components/virtualize-rows.js +286 -0
  31. package/components/virtualize-rows.js.map +1 -0
  32. package/components/virtualizer.d.ts +111 -1
  33. package/components/virtualizer.js +192 -5
  34. package/components/virtualizer.js.map +1 -1
  35. package/controllers/dom.d.ts +14 -7
  36. package/controllers/dom.js +45 -27
  37. package/controllers/dom.js.map +1 -1
  38. package/controllers/editing.d.ts +104 -37
  39. package/controllers/editing.js +230 -78
  40. package/controllers/editing.js.map +1 -1
  41. package/controllers/expansion.d.ts +14 -5
  42. package/controllers/expansion.js +24 -1
  43. package/controllers/expansion.js.map +1 -1
  44. package/controllers/filter.d.ts +10 -2
  45. package/controllers/filter.js +62 -17
  46. package/controllers/filter.js.map +1 -1
  47. package/controllers/history.d.ts +45 -8
  48. package/controllers/history.js +58 -20
  49. package/controllers/history.js.map +1 -1
  50. package/controllers/navigation.d.ts +130 -13
  51. package/controllers/navigation.js +474 -60
  52. package/controllers/navigation.js.map +1 -1
  53. package/controllers/pagination.d.ts +13 -6
  54. package/controllers/pagination.js +29 -22
  55. package/controllers/pagination.js.map +1 -1
  56. package/controllers/reorder.js +38 -22
  57. package/controllers/reorder.js.map +1 -1
  58. package/controllers/resize.d.ts +17 -2
  59. package/controllers/resize.js +16 -6
  60. package/controllers/resize.js.map +1 -1
  61. package/controllers/row-pin.d.ts +26 -2
  62. package/controllers/row-pin.js +24 -11
  63. package/controllers/row-pin.js.map +1 -1
  64. package/controllers/row-reorder.d.ts +40 -12
  65. package/controllers/row-reorder.js +137 -43
  66. package/controllers/row-reorder.js.map +1 -1
  67. package/controllers/selection.d.ts +33 -4
  68. package/controllers/selection.js +49 -11
  69. package/controllers/selection.js.map +1 -1
  70. package/controllers/sort.d.ts +22 -2
  71. package/controllers/sort.js +30 -25
  72. package/controllers/sort.js.map +1 -1
  73. package/controllers/state.d.ts +74 -7
  74. package/controllers/state.js +117 -36
  75. package/controllers/state.js.map +1 -1
  76. package/controllers/tree.d.ts +15 -10
  77. package/controllers/tree.js +0 -0
  78. package/controllers/tree.js.map +1 -1
  79. package/custom-elements.json +5091 -1023
  80. package/docs/assets/hierarchy.js +1 -1
  81. package/docs/assets/highlight.css +15 -8
  82. package/docs/assets/navigation.js +1 -1
  83. package/docs/assets/search.js +1 -1
  84. package/docs/classes/ApexGrid.html +233 -87
  85. package/docs/classes/ApexGridPaginator.html +39 -8
  86. package/docs/classes/ApexGridToolbar.html +11 -7
  87. package/docs/functions/localize.html +2 -2
  88. package/docs/hierarchy.html +1 -1
  89. package/docs/index.html +159 -26
  90. package/docs/interfaces/ApexCellValidationFailedEvent.html +3 -2
  91. package/docs/interfaces/ApexCellValueChangedEvent.html +4 -2
  92. package/docs/interfaces/ApexCellValueChangingEvent.html +3 -2
  93. package/docs/interfaces/ApexGridEventMap.html +5 -2
  94. package/docs/interfaces/ApexPageChangedEvent.html +3 -1
  95. package/docs/interfaces/ApexRowEditEndedEvent.html +2 -1
  96. package/docs/interfaces/ApexRowEditStartedEvent.html +2 -1
  97. package/docs/interfaces/ApexRowMovingEvent.html +9 -3
  98. package/docs/interfaces/ApexRowPinningEvent.html +1 -1
  99. package/docs/interfaces/ApexTreeRowExpandedEvent.html +4 -1
  100. package/docs/interfaces/ApexTreeRowExpandingEvent.html +4 -1
  101. package/docs/interfaces/BaseApexCellContext.html +18 -5
  102. package/docs/interfaces/BaseApexEditorContext.html +20 -5
  103. package/docs/interfaces/BaseFilterExpression.html +3 -3
  104. package/docs/interfaces/CSVExportOptions.html +12 -4
  105. package/docs/interfaces/DataPipelineConfiguration.html +4 -9
  106. package/docs/interfaces/ExportOptions.html +5 -3
  107. package/docs/interfaces/GridExpansionConfiguration.html +6 -2
  108. package/docs/interfaces/GridRowReorderingConfiguration.html +1 -1
  109. package/docs/interfaces/GridSelectionConfiguration.html +19 -4
  110. package/docs/interfaces/PaginationConfiguration.html +11 -4
  111. package/docs/interfaces/PaginationState.html +4 -2
  112. package/docs/interfaces/SetStateResult.html +3 -2
  113. package/docs/interfaces/ValidatorContext.html +7 -2
  114. package/docs/media/_token-values.scss +118 -0
  115. package/docs/media/package.json +9 -5
  116. package/docs/modules.html +1 -1
  117. package/docs/types/DataPipelineParams.html +2 -2
  118. package/docs/types/ExportSource.html +6 -4
  119. package/docs/types/OperandKeys.html +9 -0
  120. package/docs/types/PaginationMode.html +5 -4
  121. package/docs/variables/EN_LOCALE.html +2 -2
  122. package/i18n/en.d.ts +7 -0
  123. package/i18n/en.js +7 -0
  124. package/i18n/en.js.map +1 -1
  125. package/i18n/es.js +7 -0
  126. package/i18n/es.js.map +1 -1
  127. package/internal/column-types.d.ts +38 -2
  128. package/internal/column-types.js +83 -55
  129. package/internal/column-types.js.map +1 -1
  130. package/internal/constants.d.ts +1 -2
  131. package/internal/constants.js +0 -4
  132. package/internal/constants.js.map +1 -1
  133. package/internal/document-styles.d.ts +30 -0
  134. package/internal/document-styles.js +48 -0
  135. package/internal/document-styles.js.map +1 -0
  136. package/internal/export.d.ts +45 -7
  137. package/internal/export.js +39 -34
  138. package/internal/export.js.map +1 -1
  139. package/internal/feature-module.d.ts +67 -3
  140. package/internal/feature-module.js +6 -0
  141. package/internal/feature-module.js.map +1 -1
  142. package/internal/flip.d.ts +28 -16
  143. package/internal/flip.js +42 -10
  144. package/internal/flip.js.map +1 -1
  145. package/internal/index.d.ts +7 -5
  146. package/internal/index.js +6 -4
  147. package/internal/index.js.map +1 -1
  148. package/internal/normalize-case.d.ts +8 -1
  149. package/internal/normalize-case.js +3 -2
  150. package/internal/normalize-case.js.map +1 -1
  151. package/internal/state-snapshot.d.ts +60 -8
  152. package/internal/state-snapshot.js +87 -19
  153. package/internal/state-snapshot.js.map +1 -1
  154. package/internal/types.d.ts +92 -21
  155. package/internal/types.js.map +1 -1
  156. package/internal/utils.d.ts +34 -6
  157. package/internal/utils.js +30 -21
  158. package/internal/utils.js.map +1 -1
  159. package/internal/validators.js +1 -0
  160. package/internal/validators.js.map +1 -1
  161. package/operations/filter/operands/number.js +9 -6
  162. package/operations/filter/operands/number.js.map +1 -1
  163. package/operations/filter/operands/string.js +2 -3
  164. package/operations/filter/operands/string.js.map +1 -1
  165. package/operations/filter/types.d.ts +14 -2
  166. package/operations/filter/types.js.map +1 -1
  167. package/operations/filter.d.ts +6 -0
  168. package/operations/filter.js +23 -7
  169. package/operations/filter.js.map +1 -1
  170. package/operations/quick-filter.js +2 -5
  171. package/operations/quick-filter.js.map +1 -1
  172. package/operations/sort.d.ts +21 -2
  173. package/operations/sort.js +41 -14
  174. package/operations/sort.js.map +1 -1
  175. package/package.json +9 -4
  176. package/setup.d.ts +11 -0
  177. package/setup.js +9 -19
  178. package/setup.js.map +1 -1
  179. package/styles/_token-values.css.d.ts +1 -0
  180. package/styles/_token-values.css.js +3 -0
  181. package/styles/_token-values.css.js.map +1 -0
  182. package/styles/_tokens.css.js +1 -1
  183. package/styles/_tokens.css.js.map +1 -1
  184. package/styles/body-cell/body-cell.css.js +1 -1
  185. package/styles/body-cell/body-cell.css.js.map +1 -1
  186. package/styles/body-cell/cell-renderers.css.js +1 -1
  187. package/styles/body-cell/cell-renderers.css.js.map +1 -1
  188. package/styles/body-row/body-row.css.js +1 -1
  189. package/styles/body-row/body-row.css.js.map +1 -1
  190. package/styles/document-tokens.css.d.ts +1 -0
  191. package/styles/document-tokens.css.js +3 -0
  192. package/styles/document-tokens.css.js.map +1 -0
  193. package/styles/filter-row/filter-row.css.js +1 -1
  194. package/styles/filter-row/filter-row.css.js.map +1 -1
  195. package/styles/grid/_grid.base.css.js +1 -1
  196. package/styles/grid/_grid.base.css.js.map +1 -1
  197. package/styles/grid/grid.css.js +1 -1
  198. package/styles/grid/grid.css.js.map +1 -1
  199. package/styles/group-header-row/group-header-row.css.js +1 -1
  200. package/styles/group-header-row/group-header-row.css.js.map +1 -1
  201. package/styles/header-cell/header-cell.css.js +1 -1
  202. package/styles/header-cell/header-cell.css.js.map +1 -1
  203. package/styles/header-row/header-row.base.css.js +1 -1
  204. package/styles/header-row/header-row.base.css.js.map +1 -1
  205. package/styles/paginator/paginator.css.js +1 -1
  206. package/styles/paginator/paginator.css.js.map +1 -1
  207. package/styles/toolbar/toolbar.css.js +1 -1
  208. package/styles/toolbar/toolbar.css.js.map +1 -1
  209. package/tokens.css +78 -0
  210. package/LICENSE +0 -80
  211. package/docs/media/LICENSE +0 -80
  212. package/docs/media/_tokens.scss +0 -214
  213. package/operations/pagination.d.ts +0 -19
  214. package/operations/pagination.js +0 -12
  215. package/operations/pagination.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,579 @@ All notable changes to the `apex-grid` (community) package are documented here.
4
4
  This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
5
5
  and the format is based on [Keep a Changelog](https://keepachangelog.com/).
6
6
 
7
+ ## [Unreleased]
8
+
9
+ ## [4.0.0-beta.1] - 2026-10-29
10
+
11
+ A pre-release of 4.0.0, published under the `next` npm tag for trying out
12
+ before it becomes `latest`. It is a major version because these changes can
13
+ break existing code:
14
+
15
+ - **Body rows are recycled by default** (`rowRecycling`). A row element and
16
+ its cells can show another record after a scroll, sort or filter, so a
17
+ template that keeps state in the elements it creates, or code that holds
18
+ `grid.rows` elements or `ctx.row` across an `await`, can see another
19
+ record. Set `rowRecycling = false` (or `row-recycling="false"`) to keep
20
+ record-keyed rows.
21
+ - **The grid role moved off the host.** `role`, `aria-rowcount`,
22
+ `aria-colcount`, `aria-multiselectable` and the accessible name are on the
23
+ inner `[part~="table"]` element.
24
+ - **Types removed:** `DataPipelineConfiguration.pagination` and the
25
+ `'pagination'` value of `DataPipelineParams.type`.
26
+ - **Pagination:** pinned rows no longer count toward `totalItems` and the
27
+ pages, and a local page change no longer calls custom sort and filter hooks.
28
+ - **Smaller type and DOM changes:** `ctx.data` is a required field of the cell
29
+ context, `ApexGridPaginator.pageSizeOptions` is `readonly number[]`, the
30
+ paginator's previous button id is `prev`, and the protected
31
+ `ApexGrid.headerRowCount` and `ApexGridHeaderRow.renderDragGhost` are gone.
32
+
33
+ See Changed and Removed for the details and for other changes apps may notice.
34
+
35
+ ### Added
36
+ - The `ai.cancelled` locale key (English and Spanish), used by the enterprise
37
+ Ask AI panel.
38
+ - The `chart.rangeResized` locale key (English and Spanish), announced when an
39
+ enterprise charted range is resized from the keyboard.
40
+ - **`showHeader`** (attribute `show-header="false"`) leaves out the column
41
+ header row, the column group row and the filter panel, so a short grid such as
42
+ a dashboard tile gives that room to the data rows. Screen readers still
43
+ announce the column names, and sort, filter, column widths and the page come
44
+ back unchanged when the header returns. While it is hidden, resizing, the
45
+ column menu, drag reorder and the header filter button are unavailable.
46
+ ([#35](https://github.com/apexcharts/apex-grid/issues/35))
47
+ - **`selection.isRowSelectable(row)`** keeps chosen rows out of selection,
48
+ such as a pinned total row. Such a row shows no checkbox, and clicks, Shift
49
+ and Ctrl clicks, Space, Select all, `selectRow` and `setState` all skip it;
50
+ the header checkbox counts only selectable rows. Rows stay selectable by
51
+ default, pinned rows included.
52
+ ([#38](https://github.com/apexcharts/apex-grid/issues/38))
53
+ - `pagination.paginator: 'compact'` draws the small paginator: summary,
54
+ previous, page / count, next.
55
+ ([#36](https://github.com/apexcharts/apex-grid/issues/36))
56
+ - `apex-grid::part()` reaches every paginator and toolbar part
57
+ (`paginator-host`, `paginator`, `paginator-size`, `paginator-info`,
58
+ `paginator-controls`, `paginator-page`, `paginator-button`, `toolbar-host`,
59
+ `toolbar`, `toolbar-search`, `search-input`, `toolbar-actions`, `export`,
60
+ `export-menu` and the rest), and each paginator button also carries its own
61
+ part: `paginator-first`, `paginator-prev`, `paginator-next` or
62
+ `paginator-last`. ([#37](https://github.com/apexcharts/apex-grid/issues/37))
63
+ - **`ctx.data` in cell and editor templates.** The context handed to a
64
+ `cellTemplate` or an `editorTemplate` carries `data`, the record the cell
65
+ was rendered for. It stays that record after an `await`, while `ctx.row`
66
+ and `ctx.parent` are elements that a pinned row band can later re-bind to
67
+ another record. A `cellTemplate`'s `ctx.commit` writes to `ctx.data`. An
68
+ editor's `ctx.commit` still commits the cell in edit when it is called.
69
+
70
+ ### Changed
71
+ - **Body rows are now recycled by default** (new `rowRecycling` property,
72
+ default `true`). A scroll, sort or filter re-binds the rendered row elements
73
+ to the records now in view instead of building new rows, which makes long
74
+ jumps and sorts cheaper on large grids. A row element, and its cells, can
75
+ therefore show another record later. Cell and editor templates should
76
+ derive what they render from their context on each render, read
77
+ `ctx.data` for the record, and not hold `ctx.row` or `ctx.parent` across an
78
+ `await`; a template that keeps element state can wrap itself in
79
+ `keyed(ctx.data, ...)`. What you might notice: a template that keeps state
80
+ in the elements it creates (an input's typed value, a third-party widget)
81
+ can show it on another record after a scroll or a sort, and code that holds
82
+ `grid.rows` elements across a scroll or a sort finds them showing other
83
+ records. To keep record-keyed rows, set `grid.rowRecycling = false` (or the
84
+ `row-recycling="false"` attribute). Detail panel content follows its record
85
+ and keeps its DOM state across a scroll, a sort or a `moveRow`, so detail
86
+ templates need no change; a grid created inside a `detailTemplate` keeps its
87
+ own setting.
88
+ - **The grid role and its row counts moved from the `<apex-grid>` host to an
89
+ inner element** exposed as part `table`: `role` (`grid` or `treegrid`),
90
+ `aria-rowcount`, `aria-colcount`, `aria-multiselectable` and the accessible
91
+ name. It holds only the header rows and the body, so the toolbar, paginator,
92
+ filter panel and live region are no longer read as table content, and every
93
+ default grid passes the axe `aria-required-children` rule. Code or tests
94
+ that read these from the host should query
95
+ `grid.shadowRoot.querySelector('[part~="table"]')`. The layout is unchanged.
96
+ - An `aria-label` or `aria-labelledby` on the host still names the grid, and
97
+ `aria-describedby` becomes its description. References are resolved when the
98
+ grid renders: if the referenced element is added later, set the attribute
99
+ again.
100
+ - The grid uses the `treegrid` role whenever rows carry levels or expand: tree
101
+ data, master-detail with a detail template, and the enterprise row grouping,
102
+ expandable pivot and server-side grouping.
103
+ - `pageItems` keeps the same array between updates while nothing that shapes
104
+ it changes. Apps that compared it by identity now see a new array only when
105
+ the body rows change.
106
+ - The column header row is no longer a Tab stop.
107
+ - **The built-in column menu opens in the top layer** where the browser supports
108
+ the Popover API: pinned rows, the paginator and the grid's edge no longer
109
+ cover or clip it, it stays on its header cell through any scroll or layout
110
+ change, and it closes when its column scrolls out of the grid. It can now
111
+ extend outside the grid's box.
112
+ - The column drag ghost renders at the grid root, so it stays above the column
113
+ group header and pinned top rows, and `apex-grid::part(drag-ghost)` now
114
+ reaches it.
115
+ - `--filtering-header-background` applies to every filtered header, pinned and
116
+ dragged ones included, as a layer over the header's own background, and
117
+ filtered headers get the same hover highlight as other headers.
118
+ - The paginator follows `--header-background` and group header labels follow
119
+ `--header-text-color`, like the rest of the header area. The paginator text
120
+ size follows `--ag-fs-cell-secondary`, and the filter count badge uses
121
+ `--ag-text-on-brand` and `--ag-fw-semibold`. Default rendering is unchanged.
122
+ - Forced colors (Windows High Contrast): checkboxes are drawn by the browser in
123
+ system colours, so apps that styled them through parts see the native
124
+ checkbox in that mode only.
125
+ - With pagination, pinned rows no longer count toward the pages: `totalItems`,
126
+ `pageCount`, the paginator summary and the `pageChanged` detail count the
127
+ rows the pager pages through, and pinning a row on the last page moves to the
128
+ new last page. Without pagination `totalItems` still counts every row.
129
+ - A local page or page size change no longer reruns sorting, filtering and the
130
+ rest of the pipeline, so custom sort and filter hooks are no longer called on
131
+ a local page change. Remote mode still reruns it.
132
+ - `pageChanged` reports the change that was applied, so two quick
133
+ `nextPage()` calls report page 2 and then page 3.
134
+ - A page change is announced once ("Page X of N"); the paginator summary and
135
+ page label are no longer live regions.
136
+ - The first grid on a page adds one window `error` listener that cancels
137
+ "ResizeObserver loop" messages and hides them from listeners added after it.
138
+ Listeners registered before the first grid still receive them. Every other
139
+ error is untouched. ([#32](https://github.com/apexcharts/apex-grid/issues/32))
140
+ - `totalItems` is described one way everywhere: the post-filter, post-sort
141
+ row count, and in remote mode the `pagination.totalItems` you supply, the
142
+ number of rows the server holds for the current filter.
143
+ ([#34](https://github.com/apexcharts/apex-grid/issues/34))
144
+ - Type and DOM details for code that builds on the grid's internals:
145
+ `BaseApexCellContext.data` is required, so code that builds a cell context
146
+ by hand must supply it; `ApexGridPaginator.pageSizeOptions` is typed
147
+ `readonly number[]`; the paginator's previous button has the id `prev`
148
+ (it was `previous`) inside its shadow root; and the protected
149
+ `ApexGrid.headerRowCount` and `ApexGridHeaderRow.renderDragGhost` were
150
+ removed, which affects subclasses only.
151
+ - The `@lit-labs/virtualizer` dependency is pinned to exactly `2.1.1` (it was
152
+ `~2.1.0`). The grid body builds on parts of that package that are not public
153
+ API, so a newer version arrives only with a grid release that has been
154
+ checked against it.
155
+
156
+ ### Removed
157
+ - **Breaking (types):** `DataPipelineConfiguration.pagination` and the
158
+ `'pagination'` value of `DataPipelineParams.type`. The hook was documented
159
+ but never called, so no runtime behaviour changes; TypeScript code that sets
160
+ it stops compiling. For server paging use `pagination.mode: 'remote'`, fetch
161
+ the page on `pageChanged` and keep `pagination.totalItems` current.
162
+ ([#33](https://github.com/apexcharts/apex-grid/issues/33))
163
+
164
+ ### Fixed
165
+ - `aria-rowcount` no longer counts the floating filter panel as a row, and a
166
+ pinned top row no longer shares its `aria-rowindex` with the first body row.
167
+ - The master-detail panel sits in a full-width gridcell, and `aria-expanded` is
168
+ set only on rows and toggle buttons that can expand. Turning expansion off or
169
+ removing the detail template no longer leaves it on rows.
170
+ - Announcing the same message twice in a row (two identical `grid.announce()`
171
+ calls, for example) is read out again by screen readers.
172
+ - Invalid cells no longer set an `aria-errormessage` that pointed at an
173
+ unreachable id. The inline alert message is unchanged.
174
+ - Range and formula reference highlights on a pinned column no longer let
175
+ scrolled cells show through it.
176
+ - Alternating row stripes follow the row index, so they no longer flip or
177
+ flicker while you scroll.
178
+ - In the dark theme the default, muted and gold badges are readable and the
179
+ progress bar track is visible. The light theme is unchanged.
180
+ - The selected row accent bar sits on the leading edge in right to left
181
+ layouts, including a left to right grid nested in a right to left one.
182
+ - Forced colors: checked and indeterminate checkboxes no longer look unchecked;
183
+ in a selected row the cell text, row number, reorder grip and chevrons take
184
+ the selection text colour; an editor opened in a selected row uses the
185
+ system field colours; a keyboard-grabbed row shows an outline.
186
+ - The paginator no longer clips its controls in a narrow grid: it drops the
187
+ rows per page selector, then the first and last buttons, so previous and next
188
+ stay reachable. ([#36](https://github.com/apexcharts/apex-grid/issues/36))
189
+ - Grids whose cell text wraps no longer raise "ResizeObserver loop completed
190
+ with undelivered notifications" while they resize or scroll.
191
+ ([#32](https://github.com/apexcharts/apex-grid/issues/32))
192
+ - With row pinning and pagination, no trailing page is left empty.
193
+ - A `pageSize` missing from `pageSizeOptions` now appears in the rows per
194
+ page selector and is selected.
195
+ - A page size change or quick filter cancelled through `pageChanging` or
196
+ `quickFilterChanging` no longer leaves the selector or search box showing
197
+ the refused value.
198
+ - **Pinned rows no longer show the previous row's state.** When a pinned row
199
+ element switches to another record (a row above it is unpinned, say), it no
200
+ longer keeps the previous record's staged row edit value, validation error,
201
+ expanded state, detail panel content or selection checkbox state, even when
202
+ both records hold the same cell value.
203
+ - A `cellTemplate` that calls `ctx.commit` after an `await` writes to the
204
+ record it was rendered for, even if the rows changed in the meantime.
205
+ - Moving a keyboard grabbed row with the arrow keys keeps focus on that row
206
+ instead of dropping it to the page, and Escape puts the active cell and
207
+ focus back on the row at its original position.
208
+ - The floating ghost of a row drag shows the row's cell text. It showed empty
209
+ cells before.
210
+ - An editor rendered again for an edit already in progress (its row scrolled
211
+ back into view, for example) no longer scrolls the grid or selects the typed
212
+ draft when it takes focus.
213
+ - With an async `dataPipelineConfiguration` hook, moving a row with
214
+ `moveRow`, a keyboard grab or a drag animates the rows into their new places.
215
+ They jumped there without the animation before.
216
+ - A jump to a row far outside the view that is still waiting for the body to
217
+ finish scrolling no longer runs after a newer jump, so two quick jumps end on
218
+ the second row instead of snapping back to the first.
219
+ - A pinned row that switches to another record no longer animates from the
220
+ previous record's look: its selected wash, expand chevron, checkbox and
221
+ progress bars show the new record's state at once.
222
+ - An image cell whose URL changes (a pinned row switching records, an edit of
223
+ the URL) renders a new `<img>`, so the previous picture no longer stays on
224
+ screen while the new one loads.
225
+ - Arrow and Page keys in a grid whose rows grew after they were first shown
226
+ (detail panels expanded, wrapped text) keep the active cell in view and
227
+ focused. A quick run of key presses could leave it unrendered, with focus on
228
+ the body or on a cell of another record, until the next click.
229
+ - When a sort, a filter or a data change moves the record of an open cell
230
+ edit to another row that is still in view, the active cell moves with it, so
231
+ the keyboard carries on from the edited cell once the edit closes. It used to
232
+ stay on the old row, which then showed another record.
233
+
234
+ ### Performance
235
+ - Scrolling no longer re-renders the rows that stay in view; only the rows that
236
+ scroll in render.
237
+ - A grid with a pinned row or a page slice no longer rescans the whole view on
238
+ every key press or click.
239
+ - Grids without feature modules skip the per-cell decoration step, and grids
240
+ with modules no longer check every module on each row and cell render.
241
+
242
+ ## [3.6.0] - 2026-10-21
243
+
244
+ A quality release built from a full code-quality audit, plus `--ag-*` tokens
245
+ that reach the grid from any ancestor (shadow roots included). No exported
246
+ symbol was removed or renamed. A few documented contracts now hold where they
247
+ did not: `sorted`, `filtered`, `pageChanged` and `quickFilterChanged` fire after
248
+ the pipeline run they caused, and a row move on a sorted grid clears the sort.
249
+ See Changed for what apps may notice.
250
+
251
+ ### Added
252
+ - **`--ag-*` tokens set on any ancestor now reach the grid**, including the
253
+ shadow host of a web component the grid renders in, however deep. Set a
254
+ theme once on your own host element and every grid inside follows it. See
255
+ "Changed" for how the defaults moved to make this work.
256
+ - **Dark-mode inputs `--ag-dark-*`** (`--ag-dark-surface`, `--ag-dark-text`,
257
+ `--ag-dark-hairline`, `--ag-dark-shadow-card`, …) for every colour
258
+ `theme="dark"` changes. Dark grids read these instead of the light inputs,
259
+ so a light palette set on a shared ancestor never puts a white surface under
260
+ the dark theme's light text. Sizing, typography, motion, the brand and the
261
+ base state colours stay shared by both modes.
262
+ - **`apex-grid/tokens.css`**: the token defaults as a plain stylesheet, for
263
+ server rendering or for apps that opt out of the adopted sheet.
264
+ - **`setup({ tokenStyles: false })`** skips adopting the token defaults into
265
+ the document.
266
+ - **`applyEdits()` entries can name their row by record**: `{ row, column,
267
+ value }` alongside the positional `{ rowIndex, column, value }`. A record
268
+ reaches rows on other pages, pinned rows and rows under a collapsed tree
269
+ parent, so `grid.selectedRows.map((row) => ({ row, column, value }))` now
270
+ edits the whole selection; with `rowIndex` such rows were skipped. Events
271
+ and validators for those rows see `rowIndex` -1.
272
+ - **`ApexGrid.ownsEvent(event)`** tells whether an event came from this grid
273
+ or from a grid nested in one of its master-detail panels. The grid uses it
274
+ so cells of a nested grid no longer drive the outer one (clicks, Space
275
+ selection, Ctrl+Z, pointer drags).
276
+ - `CSVExportOptions.escapeFormulas` (default `true`), see the change below.
277
+ - Locale keys `toolPanel.valueColumn` and `toolPanel.columnLabelColumn` for the
278
+ enterprise tool panel, in English and Spanish.
279
+ - Locale key `chart.mapCategoryNone` (English "None", Spanish "Ninguna") for
280
+ the enterprise chart panel's Category select.
281
+ - Locale key `setFilter.more` for the enterprise set filter's list cap, in
282
+ English and Spanish.
283
+ - `expansion.exportParts` lists parts inside your `detailTemplate` content
284
+ for the row to re-export, so `apex-grid::part(name)` can style them.
285
+ - Locale key `pivot.columnsTruncated` for the enterprise pivot column cap, in
286
+ English and Spanish.
287
+ - `rowMoving` and `rowMoved` carry `position` (`'before'` or `'after'`) and
288
+ `toIndex`, the view index the row lands at, next to `from`, `to` and
289
+ `data`.
290
+
291
+ ### Changed
292
+ - **Token defaults moved from the grid host to the document root.** The grid
293
+ used to declare every `--ag-*` default on its own host, and a declaration on
294
+ an element always beats an inherited value, so tokens set on an ancestor
295
+ never reached it (only `--ig-primary-500` did, through the brand fallback).
296
+ The defaults now sit on `:where(:root)`, adopted into the document through
297
+ `adoptedStyleSheets` when the first grid connects (no `<style>` element, so
298
+ no CSP nonce), and the grid resolves them into private copies on its host.
299
+ Reading tokens keeps working: `var(--ag-surface)` resolves to the default
300
+ anywhere on the page, and `apex-grid { --ag-grid-shadow:
301
+ var(--ag-shadow-card); }` still gives the card shadow. Changes to look for:
302
+ - `var(--ag-brand)` and `var(--ag-brand-strong)` have no root default, so an
303
+ Ignite UI palette set on any ancestor keeps tinting the grid. Page CSS
304
+ reading them needs its own fallback.
305
+ - Derived tokens (`--ag-brand-soft`, `--ag-brand-softer`, `--ag-brand-line`,
306
+ `--ag-row-hover`, `--ag-surface-alt`) are mixed on the grid host so they
307
+ follow a brand set lower down. They still accept an override but have no
308
+ root default to read.
309
+ - A value set on an element above a dark grid (`--ag-surface`, …) no longer
310
+ applies to it; set `--ag-dark-surface` and the other `--ag-dark-*` inputs.
311
+ A value set on the dark grid element itself is now ignored the same way.
312
+ On a dark grid, use `--ag-grid-shadow: var(--ag-dark-shadow-card)` for the
313
+ card shadow recipe.
314
+ - `theme="tinted"` mixes the brand into the surface, gridline and
315
+ muted-text values in effect (yours, if you set them) instead of replacing
316
+ them. Its row separators stay the brand alone, so a tinted grid ignores
317
+ `--ag-border` and `--ag-dark-border`, including on the grid element itself.
318
+ The default tinted look is unchanged.
319
+ - **CSV export neutralises spreadsheet formulas.** A text cell or header that
320
+ starts with `=`, `+`, `-`, `@`, a tab or a carriage return is written with a
321
+ leading apostrophe, so a spreadsheet opening the file shows it as text
322
+ instead of running it. Numbers are untouched, and so is text that is a plain
323
+ signed decimal such as `-5` or `+1.5`. Other text with a leading sign gains
324
+ the apostrophe too: a lone `-`, a phone number such as `+44 20 7946 0958`,
325
+ `-1,234.50`, or formatter output such as `-$5.00`. Pass
326
+ `exportToCSV({ escapeFormulas: false })` for the old output.
327
+ - **An open editor stays on the record it was opened for** when the data,
328
+ sort, filter or page changes while editing; before, it could jump to another
329
+ row and Enter wrote into the wrong record. The edit is cancelled when its
330
+ record leaves the view (removed, filtered out or replaced by a copy).
331
+ `editingCell`, `editingRow` and the `rowIndex` of the edit events are -1 when
332
+ the edited record is in a pinned row or on another page.
333
+ - **Row mode edits the staged row.** Cells of the row in edit show their staged
334
+ values, validators receive the staged row as `ctx.data` (a copy of the
335
+ record with the staged values applied), and a row commit re-validates the
336
+ row's other editable cells. A checkbox click, or any `cellTemplate` calling
337
+ `ctx.commit`, joins the row edit instead of writing straight to the record,
338
+ so `cancelEdit()` rolls it back.
339
+ - **After tabbing into the grid body, the first arrow, Home or Page Up key
340
+ activates the first cell** instead of moving past it (ArrowDown lands on
341
+ row 0). Page Up and Page Down move by the rows that fit in the body instead
342
+ of a fixed 10.
343
+ - **Tree data under a filter shows the ancestors of a match as their own
344
+ rows** (selectable and exportable, not matching the filter themselves)
345
+ instead of blank placeholders, and matches under a non-matching parent are
346
+ no longer hidden.
347
+ - **Replacing `grid.data` prunes tree and detail expansion** of rows that left
348
+ the data, so `getState()` no longer lists rows that are gone.
349
+ - **Exports write booleans as the checkbox shows them**: true only for
350
+ `true`, so strings such as `'false'` or `'yes'` and the number 1 export as
351
+ false. **Currency and progress columns export as numbers**, like number and
352
+ rating columns; text that is not a number exports as an empty cell.
353
+ - **Replacing `grid.data` with new records clears the undo history** (and
354
+ fires `historyChanged`). Undo used to replay onto the old, detached records:
355
+ nothing changed on screen, yet `cellValueChanged` fired for a row the grid no
356
+ longer held. New data that keeps the edited records, such as an appended
357
+ row, keeps its history, and so do in-place edits. A subclass that keeps
358
+ records outside `data` (a row model that drops collapsed rows and restores
359
+ the same objects) overrides the protected `historyRecords()` to keep their
360
+ history too.
361
+ - **`cellValueChanged` from undo or redo reports `rowIndex` -1 when the
362
+ record is pinned or on another page**, instead of the index it had when
363
+ edited (which could name a different row). Listeners that read `rowIndex`
364
+ from a replayed change should identify the row by the event's `data`.
365
+ - `applyEdits()` skips an entry whose `rowIndex` is negative or fractional,
366
+ as it already skipped one past the end of the page. 3.5.0 counted a negative
367
+ index from the end of the page and rounded a fraction down.
368
+ - **`sortExpressions` and `filterExpressions` replace the current sort or filter
369
+ instead of merging into it**, as documented and as `setState` already did;
370
+ `[]`, `null` or `undefined` clears it. Apps that assigned one expression at a
371
+ time to build up a multi-column sort or filter should assign the full list (or
372
+ call `sort()` / `filter()`, which still add). The assigned list applies at the
373
+ next update, against the columns as they are then, so a template may bind it
374
+ before `columns`; reading it back before that returns what was assigned. Sort
375
+ entries for an unknown column or with an unknown direction are dropped with a
376
+ console warning (an omitted direction still sorts ascending).
377
+ - **`currency`, `rating` and `progress` columns filter as numbers**, as their
378
+ type docs already said. The filter panel offers the number conditions (Equals,
379
+ Greater than, Less than and so on) and reads the typed value as a number, and
380
+ `getSchema()` lists the number operands for these columns. A saved state or
381
+ `filter()` call that names a text condition such as `contains` on one of them
382
+ is now dropped with a console warning; in 3.5.0 those conditions threw inside
383
+ the pipeline and filtered nothing. Feature modules read the rule through
384
+ `StateController.isNumericColumn(column)`.
385
+ - Feature modules can reveal a cell, read the inline direction of an arrow key
386
+ under RTL and read the scroll insets through the grid's navigation
387
+ controller (`revealCell`, `towardsInlineEnd`, `scrollInsets`), so the
388
+ enterprise range selection follows core navigation instead of a copy.
389
+ - **Cell templates run again after each data pipeline run.** A column
390
+ `cellTemplate` is called for every visible row, body and pinned, after each
391
+ pipeline run (sort, filter, quick filter, edit, tree toggle), so a template
392
+ that reads other fields of its row stays current. Keep templates light;
393
+ plain and typed cells are not re-rendered by this.
394
+ - Start-pinned columns freeze after the leading checkbox, expansion, reorder
395
+ grip and row-number columns instead of at the grid edge. The grid sets
396
+ `--apex-chrome-offset-row-number`, `--apex-chrome-offset-reorder`,
397
+ `--apex-chrome-offset-selection` and `--apex-chrome-offset-expansion` on the
398
+ host and no longer writes `--scrollbar-offset` inline (the stylesheet
399
+ default stays).
400
+ - A decoration-only change (range selection, formula reference highlight)
401
+ updates cell attributes without re-rendering cell content. For feature
402
+ modules, `bumpDecoration` now only re-applies `CellDecorator` attributes;
403
+ request a pipeline run when content must change.
404
+ - Feature modules can attach a command to a cell edit and its undo step, and
405
+ read the header's sortable rule through `SortController.isSortable`.
406
+ - Pressing on interactive content in a `headerTemplate` (an input, select,
407
+ textarea, button, link or editable element) no longer starts a column drag.
408
+ - When a `columnMoving` listener cancels a live swap, the event is raised once
409
+ per target crossing instead of on every pointer move.
410
+ - The filter panel's `filtering` event carries a proposed copy of the
411
+ expression for panel edits, and the change is applied only after the event
412
+ is not cancelled. Escape pressed outside the panel is no longer swallowed.
413
+ - Switching an applied Empty or Not empty filter to a condition that needs a
414
+ value removes the filter (with a cancellable remove) until a value is typed.
415
+ - `getState()` and `setState()` copy array filter terms, and feature modules
416
+ can register a filter operand by name (`registerFilterOperand` on
417
+ `apex-grid/internal`) so a saved filter that uses it restores.
418
+ - **`sorted`, `filtered`, `pageChanged` and `quickFilterChanged` fire after
419
+ the pipeline run they caused.** With an asynchronous
420
+ `dataPipelineConfiguration` hook, listeners now read the new rows and
421
+ totals. With synchronous hooks they still fire in the same task, after the
422
+ re-render of the new rows. The server-side and infinite row models fetch
423
+ outside the pipeline, so with them these events can still fire before the
424
+ new block arrives.
425
+ - `setQuickFilter` (and the toolbar search box) fires `pageChanged` and
426
+ announces the page when it moves the grid back to the first page. Setting
427
+ the `quickFilter` property or calling `setState` stays silent.
428
+ - Every part a row renders can be styled with `apex-grid::part()`:
429
+ selection, expansion, detail panel, row number and reorder parts, and the
430
+ parts of rows a module renders. Before, only `cell` was reachable, so
431
+ existing `::part()` rules for these names now take effect.
432
+ - **Moving a row on a sorted grid clears the sort** and keeps the moved order,
433
+ so the sort indicator goes away on the first move and `getState` never
434
+ reports a sort and a row order together. Before, an `applyToData` move was
435
+ undone by the re-sort.
436
+ - A move that leaves the row where it is (`moveRow(0, 1, 'before')`, for
437
+ example) returns `false` and fires no `rowMoving` or `rowMoved`.
438
+ - Escape after keyboard grab steps restores the original order (and
439
+ `grid.data` with `applyToData`, and any sort) and fires `rowMoved` for the
440
+ way back, so a persisted order stays in sync.
441
+
442
+ ### Fixed
443
+ - **`setup()` host sizing reaches grids inside a shadow root.** The
444
+ `min-height: 240px` default was adopted into the page only, which never
445
+ styles an element inside a shadow root. Each grid now adopts it into the
446
+ root it connects in, and for its own tag name, so `<apex-grid-enterprise>`
447
+ gets it too.
448
+ - **ArrowDown and ArrowUp in the toolbar's export menu move focus again.** The
449
+ menu looked up the focused item through `document.activeElement`, which is
450
+ the grid's host because the menu lives in the grid's shadow root, so focus
451
+ never moved past the first item.
452
+ - **Text filter conditions no longer throw on a cell that is not a string.**
453
+ Contains, Starts with, Equals and the rest read `null` and `undefined` as
454
+ empty text and match numbers, dates and other values on their text. In 3.5.0
455
+ one such cell (an optional field left empty, a Date in a date column) made
456
+ the filter apply nothing and stopped later sorts until it was removed.
457
+ - `OperandKeys` offers the text operand names for a field whose type is not
458
+ number, string or boolean (a `Date`, for example), matching what the grid
459
+ applies, instead of accepting the value type itself as a condition. A
460
+ nullable or optional field keeps the names of its type (`number | null`
461
+ still takes the number names only), and a field typed `unknown`, such as
462
+ every field of a `Record<string, unknown>` row, keeps every operand name.
463
+ - **Number range conditions no longer match an empty cell.** Greater than, Less
464
+ than and their or equal forms read `null`, and an empty string from CSV or
465
+ JSON data, as 0, so Less than 20 included rows with no value. Empty now
466
+ matches those cells instead.
467
+ - **OR filter criteria are evaluated per column.** An OR on one column no longer
468
+ cancels the OR conditions of other columns, so `(city is Paris OR Oslo) AND
469
+ (id is 3 OR 99)` keeps the matching rows instead of none, and a column left
470
+ with only OR conditions (after removing its first chip) filters again instead
471
+ of keeping every row.
472
+ - A filter entry without `caseSensitive` (from `setState`, `filter()` or an AI
473
+ patch) keeps the column's `filter.caseSensitive` instead of turning case
474
+ sensitivity off.
475
+ - Removing a filter that is not applied, such as typing `-` or `.` first into a
476
+ number filter, or clearing a column that has no filter, no longer fires
477
+ `filtering` and `filtered` or announces that the filter was cleared.
478
+ - When the filter changes (a condition or term is added, edited or removed, or
479
+ the filter is cleared), the grid scrolls back to its first row. The reset
480
+ targeted an element that does not scroll, so a deep scroll offset was clamped
481
+ to the end of the shorter result and the first matches sat above the fold.
482
+ Reapplying the same filter, such as `setState(getState())` or assigning an
483
+ equal `filterExpressions` list on every render, keeps the scroll position.
484
+ - **A batch of edits made inside another undoes as one step.** A
485
+ `cellValueChanged` listener that calls `applyEdits()` during a row commit,
486
+ a bulk edit or a paste no longer splits it, so one undo reverts all of it.
487
+ - Undoing or redoing a large batch reads the view once instead of once per
488
+ cell.
489
+ - `applyEdits()` shows the invalid state of its cells when every edit in the
490
+ batch is rejected by a validator, without waiting for another render.
491
+ - `pattern()` validators with a global (`g`) or sticky (`y`) RegExp give the
492
+ same result every time; repeated valid values no longer alternate between
493
+ passing and failing.
494
+ - Editing a pinned row edits the pinned record; a checkbox or double-click in
495
+ a pinned row used to act on a body row. In row mode a cancelled
496
+ `cellValueChanging` aborts the whole row before anything is written.
497
+ - Keyboard navigation keeps its position per grid: an arrow key in one grid no
498
+ longer continues from a cell clicked in another, and removing a grid no
499
+ longer resets the others. The active cell follows filters, sorting, paging,
500
+ data changes and hidden columns, so the body always keeps one tab stop.
501
+ - Ctrl+End and Ctrl+Home on large grids render the target rows and keep focus
502
+ on the target cell, and keyboard moves no longer leave the active cell
503
+ hidden behind the sticky header, pinned rows or pinned columns. Full-width
504
+ rows from feature modules (group headers, total rows) take keyboard focus.
505
+ - Tree data with `defaultExpanded` keeps a parent the user collapsed when
506
+ `grid.data` is reassigned. Two rows with the same path are both shown, and
507
+ `treeRowExpanding` and `treeRowExpanded` list parents that exist only as a
508
+ path segment.
509
+ - Exported dates stored as `'YYYY-MM-DD'` strings carry the calendar day the
510
+ grid shows; west of UTC they were written one day early. An invalid `Date`
511
+ exports as an empty cell instead of throwing (CSV) or writing a value the
512
+ spreadsheet app reports as corrupt (XLSX).
513
+ - Exporting with source `'selected'` writes rows in the order the grid shows
514
+ them, followed by selected rows the view hides.
515
+ - Date and currency cells reuse their `Intl` formatters, so re-renders and
516
+ large exports no longer build one per cell; a malformed locale, currency or
517
+ date format falls back to a default instead of failing every cell.
518
+ - Sparkline cells no longer show a misleading 0% delta when the series starts
519
+ at zero, and never print -0%.
520
+ - A `cellTemplate` that reads other fields of its row updates after an inline
521
+ edit, `applyEdits`, paste, fill or a data change, in body and pinned rows.
522
+ - The tree data chevron and `aria-expanded` follow every expand and collapse,
523
+ including `expandTreeRow` and `collapseTreeRow`.
524
+ - Range selection highlights stay on the selected positions after a sort,
525
+ filter or expand moves rows.
526
+ - With the row-number gutter, reorder grip, selection or expansion column
527
+ shown, a horizontal scroll no longer stacks those cells or a start-pinned
528
+ column on top of each other, in LTR and RTL.
529
+ - Column group headers sit over their member columns when the reorder grip or
530
+ the row-number gutter is shown, and the group row's cells over pinned and
531
+ leading columns stay frozen on horizontal scroll.
532
+ - Pinned columns inside a CSS-scaled container no longer overlap on
533
+ horizontal scroll.
534
+ - A header drag toward later columns (rightward, or leftward with
535
+ `dir="rtl"`) keeps following the pointer after the first swap and always
536
+ ends on release, including a release outside the header or a lost release.
537
+ - Column autosize no longer throws when group header, total or loading rows
538
+ are rendered.
539
+ - With `dir="rtl"` the column resize guide is drawn on the dragged edge.
540
+ - The filter panel: a cancelled change really leaves the filter unchanged, a
541
+ condition picked on a fresh panel is reported and announced as an add, the
542
+ filter icon of the open column closes it with a mouse, the panel stays under
543
+ its column on scroll, resize and keyboard column switches, and it no longer
544
+ runs off screen when the root font size is not 16px.
545
+ - `setState(getState())` keeps the scroll position when the filter holds an
546
+ array term, and the filter panel chip lists such a term's values.
547
+ - With an asynchronous pipeline hook, a slower earlier run no longer
548
+ overwrites the rows of a newer quick filter, filter or sort, and a run
549
+ replaced before it finishes no longer holds back the newer one's calls and
550
+ events.
551
+ - A `quickFilterChanging` listener that assigns `grid.quickFilter` (to trim
552
+ the input, for example) keeps its value, and `quickFilterChanged` reports
553
+ it. A substitute equal to the current filter changes nothing and
554
+ `setQuickFilter` returns `false`.
555
+ - Changing `grid.tree` after the first render (turning tree mode on or off,
556
+ or changing `getDataPath` or `defaultExpanded`) rebuilds the rows.
557
+ - `grid.data` set to `null` or `undefined` (a framework prop that is still
558
+ loading) shows an empty grid and reads back as `[]`, instead of throwing
559
+ and keeping the previous rows.
560
+ - `moveColumn` no longer fires `columnMoving` for a move that changes nothing.
561
+ - Shift+click and Ctrl or Cmd+click beside the checkbox in the selection cell
562
+ do the same range or additive selection as on the checkbox itself.
563
+ - When a `rowSelecting` listener cancels the change, the row and select-all
564
+ checkboxes show the real selection state instead of staying checked.
565
+ - Moving a row while a column filter or the quick filter is active keeps every
566
+ hidden row in its place, in the manual order and in `grid.data` with
567
+ `applyToData`. Before, hidden rows were pushed to the end.
568
+ - Replacing `grid.data` carries the manual row order and row pins onto the
569
+ new rows (by `rowId` when set, otherwise rows that left the data are
570
+ dropped). New rows can be move targets, a reload no longer disables
571
+ reordering and header sorting, and the pinned band no longer shows a stale
572
+ copy of a reloaded row.
573
+ - A row drag ends when the pointer is released or cancelled anywhere on the
574
+ page, or when a move arrives with no button held, and a grid removed
575
+ mid-drag drops its ghost.
576
+ - With `rowPinning.enabled` set to `false`, `pinnedRows` and the aria row
577
+ counts no longer report pinned rows and those rows get their reorder grip
578
+ back. The pins are kept for when pinning is turned back on.
579
+
7
580
  ## [3.5.0] - 2026-09-12
8
581
 
9
582
  A feature and accessibility release. No exported symbol was removed or renamed.