apex-grid 3.5.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/CHANGELOG.md +338 -0
  2. package/LICENSE.md +125 -0
  3. package/README.md +64 -10
  4. package/components/cell.d.ts +30 -4
  5. package/components/cell.js +199 -53
  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 +146 -36
  11. package/components/grid.js +442 -103
  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 +7 -5
  16. package/components/header-row.js +11 -9
  17. package/components/header-row.js.map +1 -1
  18. package/components/header.js +32 -20
  19. package/components/header.js.map +1 -1
  20. package/components/row.d.ts +19 -3
  21. package/components/row.js +69 -23
  22. package/components/row.js.map +1 -1
  23. package/components/toolbar.js +2 -1
  24. package/components/toolbar.js.map +1 -1
  25. package/components/virtualizer.d.ts +40 -0
  26. package/components/virtualizer.js +116 -5
  27. package/components/virtualizer.js.map +1 -1
  28. package/controllers/dom.d.ts +4 -4
  29. package/controllers/dom.js +40 -22
  30. package/controllers/dom.js.map +1 -1
  31. package/controllers/editing.d.ts +104 -37
  32. package/controllers/editing.js +230 -78
  33. package/controllers/editing.js.map +1 -1
  34. package/controllers/expansion.d.ts +14 -5
  35. package/controllers/expansion.js +24 -1
  36. package/controllers/expansion.js.map +1 -1
  37. package/controllers/filter.d.ts +10 -2
  38. package/controllers/filter.js +62 -17
  39. package/controllers/filter.js.map +1 -1
  40. package/controllers/history.d.ts +45 -8
  41. package/controllers/history.js +58 -20
  42. package/controllers/history.js.map +1 -1
  43. package/controllers/navigation.d.ts +67 -13
  44. package/controllers/navigation.js +191 -51
  45. package/controllers/navigation.js.map +1 -1
  46. package/controllers/pagination.d.ts +6 -0
  47. package/controllers/pagination.js +10 -2
  48. package/controllers/pagination.js.map +1 -1
  49. package/controllers/reorder.js +38 -22
  50. package/controllers/reorder.js.map +1 -1
  51. package/controllers/resize.d.ts +17 -2
  52. package/controllers/resize.js +16 -6
  53. package/controllers/resize.js.map +1 -1
  54. package/controllers/row-pin.d.ts +20 -2
  55. package/controllers/row-pin.js +16 -9
  56. package/controllers/row-pin.js.map +1 -1
  57. package/controllers/row-reorder.d.ts +31 -11
  58. package/controllers/row-reorder.js +117 -37
  59. package/controllers/row-reorder.js.map +1 -1
  60. package/controllers/selection.d.ts +11 -4
  61. package/controllers/selection.js +26 -11
  62. package/controllers/selection.js.map +1 -1
  63. package/controllers/sort.d.ts +22 -2
  64. package/controllers/sort.js +29 -23
  65. package/controllers/sort.js.map +1 -1
  66. package/controllers/state.d.ts +56 -7
  67. package/controllers/state.js +76 -6
  68. package/controllers/state.js.map +1 -1
  69. package/controllers/tree.d.ts +15 -10
  70. package/controllers/tree.js +0 -0
  71. package/controllers/tree.js.map +1 -1
  72. package/custom-elements.json +3156 -277
  73. package/docs/assets/hierarchy.js +1 -1
  74. package/docs/assets/highlight.css +15 -8
  75. package/docs/assets/navigation.js +1 -1
  76. package/docs/assets/search.js +1 -1
  77. package/docs/classes/ApexGrid.html +155 -81
  78. package/docs/classes/ApexGridPaginator.html +1 -1
  79. package/docs/classes/ApexGridToolbar.html +3 -3
  80. package/docs/functions/localize.html +2 -2
  81. package/docs/hierarchy.html +1 -1
  82. package/docs/index.html +74 -11
  83. package/docs/interfaces/ApexCellValidationFailedEvent.html +3 -2
  84. package/docs/interfaces/ApexCellValueChangedEvent.html +4 -2
  85. package/docs/interfaces/ApexCellValueChangingEvent.html +3 -2
  86. package/docs/interfaces/ApexGridEventMap.html +5 -2
  87. package/docs/interfaces/ApexRowEditEndedEvent.html +2 -1
  88. package/docs/interfaces/ApexRowEditStartedEvent.html +2 -1
  89. package/docs/interfaces/ApexRowMovingEvent.html +9 -3
  90. package/docs/interfaces/ApexRowPinningEvent.html +1 -1
  91. package/docs/interfaces/ApexTreeRowExpandedEvent.html +4 -1
  92. package/docs/interfaces/ApexTreeRowExpandingEvent.html +4 -1
  93. package/docs/interfaces/BaseApexCellContext.html +1 -1
  94. package/docs/interfaces/BaseApexEditorContext.html +1 -1
  95. package/docs/interfaces/BaseFilterExpression.html +2 -2
  96. package/docs/interfaces/CSVExportOptions.html +12 -4
  97. package/docs/interfaces/DataPipelineConfiguration.html +1 -1
  98. package/docs/interfaces/ExportOptions.html +5 -3
  99. package/docs/interfaces/GridExpansionConfiguration.html +6 -2
  100. package/docs/interfaces/GridRowReorderingConfiguration.html +1 -1
  101. package/docs/interfaces/PaginationState.html +1 -1
  102. package/docs/interfaces/SetStateResult.html +3 -2
  103. package/docs/interfaces/ValidatorContext.html +7 -2
  104. package/docs/media/_token-values.scss +118 -0
  105. package/docs/media/package.json +3 -2
  106. package/docs/modules.html +1 -1
  107. package/docs/types/ExportSource.html +6 -4
  108. package/docs/types/OperandKeys.html +9 -0
  109. package/docs/variables/EN_LOCALE.html +2 -2
  110. package/i18n/en.d.ts +5 -0
  111. package/i18n/en.js +5 -0
  112. package/i18n/en.js.map +1 -1
  113. package/i18n/es.js +5 -0
  114. package/i18n/es.js.map +1 -1
  115. package/internal/column-types.d.ts +27 -1
  116. package/internal/column-types.js +75 -49
  117. package/internal/column-types.js.map +1 -1
  118. package/internal/constants.d.ts +1 -2
  119. package/internal/constants.js +0 -4
  120. package/internal/constants.js.map +1 -1
  121. package/internal/document-styles.d.ts +30 -0
  122. package/internal/document-styles.js +48 -0
  123. package/internal/document-styles.js.map +1 -0
  124. package/internal/export.d.ts +39 -7
  125. package/internal/export.js +35 -34
  126. package/internal/export.js.map +1 -1
  127. package/internal/feature-module.d.ts +60 -3
  128. package/internal/feature-module.js +6 -0
  129. package/internal/feature-module.js.map +1 -1
  130. package/internal/index.d.ts +6 -4
  131. package/internal/index.js +5 -3
  132. package/internal/index.js.map +1 -1
  133. package/internal/normalize-case.d.ts +8 -1
  134. package/internal/normalize-case.js +3 -2
  135. package/internal/normalize-case.js.map +1 -1
  136. package/internal/state-snapshot.d.ts +60 -8
  137. package/internal/state-snapshot.js +87 -19
  138. package/internal/state-snapshot.js.map +1 -1
  139. package/internal/types.d.ts +17 -2
  140. package/internal/types.js.map +1 -1
  141. package/internal/utils.d.ts +34 -6
  142. package/internal/utils.js +30 -21
  143. package/internal/utils.js.map +1 -1
  144. package/internal/validators.js +1 -0
  145. package/internal/validators.js.map +1 -1
  146. package/operations/filter/operands/number.js +9 -6
  147. package/operations/filter/operands/number.js.map +1 -1
  148. package/operations/filter/operands/string.js +2 -3
  149. package/operations/filter/operands/string.js.map +1 -1
  150. package/operations/filter/types.d.ts +13 -1
  151. package/operations/filter/types.js.map +1 -1
  152. package/operations/filter.d.ts +6 -0
  153. package/operations/filter.js +23 -7
  154. package/operations/filter.js.map +1 -1
  155. package/operations/quick-filter.js +2 -5
  156. package/operations/quick-filter.js.map +1 -1
  157. package/operations/sort.d.ts +21 -2
  158. package/operations/sort.js +41 -14
  159. package/operations/sort.js.map +1 -1
  160. package/package.json +8 -3
  161. package/setup.d.ts +11 -0
  162. package/setup.js +9 -19
  163. package/setup.js.map +1 -1
  164. package/styles/_token-values.css.d.ts +1 -0
  165. package/styles/_token-values.css.js +3 -0
  166. package/styles/_token-values.css.js.map +1 -0
  167. package/styles/_tokens.css.js +1 -1
  168. package/styles/_tokens.css.js.map +1 -1
  169. package/styles/body-cell/body-cell.css.js +1 -1
  170. package/styles/body-cell/body-cell.css.js.map +1 -1
  171. package/styles/body-cell/cell-renderers.css.js +1 -1
  172. package/styles/body-cell/cell-renderers.css.js.map +1 -1
  173. package/styles/body-row/body-row.css.js +1 -1
  174. package/styles/body-row/body-row.css.js.map +1 -1
  175. package/styles/document-tokens.css.d.ts +1 -0
  176. package/styles/document-tokens.css.js +3 -0
  177. package/styles/document-tokens.css.js.map +1 -0
  178. package/styles/filter-row/filter-row.css.js +1 -1
  179. package/styles/filter-row/filter-row.css.js.map +1 -1
  180. package/styles/grid/_grid.base.css.js +1 -1
  181. package/styles/grid/_grid.base.css.js.map +1 -1
  182. package/styles/grid/grid.css.js +1 -1
  183. package/styles/grid/grid.css.js.map +1 -1
  184. package/styles/group-header-row/group-header-row.css.js +1 -1
  185. package/styles/group-header-row/group-header-row.css.js.map +1 -1
  186. package/styles/header-cell/header-cell.css.js +1 -1
  187. package/styles/header-cell/header-cell.css.js.map +1 -1
  188. package/styles/header-row/header-row.base.css.js +1 -1
  189. package/styles/header-row/header-row.base.css.js.map +1 -1
  190. package/styles/paginator/paginator.css.js +1 -1
  191. package/styles/paginator/paginator.css.js.map +1 -1
  192. package/styles/toolbar/toolbar.css.js +1 -1
  193. package/styles/toolbar/toolbar.css.js.map +1 -1
  194. package/tokens.css +78 -0
  195. package/LICENSE +0 -80
  196. package/docs/media/LICENSE +0 -80
  197. package/docs/media/_tokens.scss +0 -214
package/CHANGELOG.md CHANGED
@@ -4,6 +4,344 @@ 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
+ ## [3.6.0] - 2026-10-21
8
+
9
+ A quality release built from a full code-quality audit, plus `--ag-*` tokens
10
+ that reach the grid from any ancestor (shadow roots included). No exported
11
+ symbol was removed or renamed. A few documented contracts now hold where they
12
+ did not: `sorted`, `filtered`, `pageChanged` and `quickFilterChanged` fire after
13
+ the pipeline run they caused, and a row move on a sorted grid clears the sort.
14
+ See Changed for what apps may notice.
15
+
16
+ ### Added
17
+ - **`--ag-*` tokens set on any ancestor now reach the grid**, including the
18
+ shadow host of a web component the grid renders in, however deep. Set a
19
+ theme once on your own host element and every grid inside follows it. See
20
+ "Changed" for how the defaults moved to make this work.
21
+ - **Dark-mode inputs `--ag-dark-*`** (`--ag-dark-surface`, `--ag-dark-text`,
22
+ `--ag-dark-hairline`, `--ag-dark-shadow-card`, …) for every colour
23
+ `theme="dark"` changes. Dark grids read these instead of the light inputs,
24
+ so a light palette set on a shared ancestor never puts a white surface under
25
+ the dark theme's light text. Sizing, typography, motion, the brand and the
26
+ base state colours stay shared by both modes.
27
+ - **`apex-grid/tokens.css`**: the token defaults as a plain stylesheet, for
28
+ server rendering or for apps that opt out of the adopted sheet.
29
+ - **`setup({ tokenStyles: false })`** skips adopting the token defaults into
30
+ the document.
31
+ - **`applyEdits()` entries can name their row by record**: `{ row, column,
32
+ value }` alongside the positional `{ rowIndex, column, value }`. A record
33
+ reaches rows on other pages, pinned rows and rows under a collapsed tree
34
+ parent, so `grid.selectedRows.map((row) => ({ row, column, value }))` now
35
+ edits the whole selection; with `rowIndex` such rows were skipped. Events
36
+ and validators for those rows see `rowIndex` -1.
37
+ - **`ApexGrid.ownsEvent(event)`** tells whether an event came from this grid
38
+ or from a grid nested in one of its master-detail panels. The grid uses it
39
+ so cells of a nested grid no longer drive the outer one (clicks, Space
40
+ selection, Ctrl+Z, pointer drags).
41
+ - `CSVExportOptions.escapeFormulas` (default `true`), see the change below.
42
+ - Locale keys `toolPanel.valueColumn` and `toolPanel.columnLabelColumn` for the
43
+ enterprise tool panel, in English and Spanish.
44
+ - Locale key `chart.mapCategoryNone` (English "None", Spanish "Ninguna") for
45
+ the enterprise chart panel's Category select.
46
+ - Locale key `setFilter.more` for the enterprise set filter's list cap, in
47
+ English and Spanish.
48
+ - `expansion.exportParts` lists parts inside your `detailTemplate` content
49
+ for the row to re-export, so `apex-grid::part(name)` can style them.
50
+ - Locale key `pivot.columnsTruncated` for the enterprise pivot column cap, in
51
+ English and Spanish.
52
+ - `rowMoving` and `rowMoved` carry `position` (`'before'` or `'after'`) and
53
+ `toIndex`, the view index the row lands at, next to `from`, `to` and
54
+ `data`.
55
+
56
+ ### Changed
57
+ - **Token defaults moved from the grid host to the document root.** The grid
58
+ used to declare every `--ag-*` default on its own host, and a declaration on
59
+ an element always beats an inherited value, so tokens set on an ancestor
60
+ never reached it (only `--ig-primary-500` did, through the brand fallback).
61
+ The defaults now sit on `:where(:root)`, adopted into the document through
62
+ `adoptedStyleSheets` when the first grid connects (no `<style>` element, so
63
+ no CSP nonce), and the grid resolves them into private copies on its host.
64
+ Reading tokens keeps working: `var(--ag-surface)` resolves to the default
65
+ anywhere on the page, and `apex-grid { --ag-grid-shadow:
66
+ var(--ag-shadow-card); }` still gives the card shadow. Changes to look for:
67
+ - `var(--ag-brand)` and `var(--ag-brand-strong)` have no root default, so an
68
+ Ignite UI palette set on any ancestor keeps tinting the grid. Page CSS
69
+ reading them needs its own fallback.
70
+ - Derived tokens (`--ag-brand-soft`, `--ag-brand-softer`, `--ag-brand-line`,
71
+ `--ag-row-hover`, `--ag-surface-alt`) are mixed on the grid host so they
72
+ follow a brand set lower down. They still accept an override but have no
73
+ root default to read.
74
+ - A value set on an element above a dark grid (`--ag-surface`, …) no longer
75
+ applies to it; set `--ag-dark-surface` and the other `--ag-dark-*` inputs.
76
+ A value set on the dark grid element itself is now ignored the same way.
77
+ On a dark grid, use `--ag-grid-shadow: var(--ag-dark-shadow-card)` for the
78
+ card shadow recipe.
79
+ - `theme="tinted"` mixes the brand into the surface, gridline and
80
+ muted-text values in effect (yours, if you set them) instead of replacing
81
+ them. Its row separators stay the brand alone, so a tinted grid ignores
82
+ `--ag-border` and `--ag-dark-border`, including on the grid element itself.
83
+ The default tinted look is unchanged.
84
+ - **CSV export neutralises spreadsheet formulas.** A text cell or header that
85
+ starts with `=`, `+`, `-`, `@`, a tab or a carriage return is written with a
86
+ leading apostrophe, so a spreadsheet opening the file shows it as text
87
+ instead of running it. Numbers are untouched, and so is text that is a plain
88
+ signed decimal such as `-5` or `+1.5`. Other text with a leading sign gains
89
+ the apostrophe too: a lone `-`, a phone number such as `+44 20 7946 0958`,
90
+ `-1,234.50`, or formatter output such as `-$5.00`. Pass
91
+ `exportToCSV({ escapeFormulas: false })` for the old output.
92
+ - **An open editor stays on the record it was opened for** when the data,
93
+ sort, filter or page changes while editing; before, it could jump to another
94
+ row and Enter wrote into the wrong record. The edit is cancelled when its
95
+ record leaves the view (removed, filtered out or replaced by a copy).
96
+ `editingCell`, `editingRow` and the `rowIndex` of the edit events are -1 when
97
+ the edited record is in a pinned row or on another page.
98
+ - **Row mode edits the staged row.** Cells of the row in edit show their staged
99
+ values, validators receive the staged row as `ctx.data` (a copy of the
100
+ record with the staged values applied), and a row commit re-validates the
101
+ row's other editable cells. A checkbox click, or any `cellTemplate` calling
102
+ `ctx.commit`, joins the row edit instead of writing straight to the record,
103
+ so `cancelEdit()` rolls it back.
104
+ - **After tabbing into the grid body, the first arrow, Home or Page Up key
105
+ activates the first cell** instead of moving past it (ArrowDown lands on
106
+ row 0). Page Up and Page Down move by the rows that fit in the body instead
107
+ of a fixed 10.
108
+ - **Tree data under a filter shows the ancestors of a match as their own
109
+ rows** (selectable and exportable, not matching the filter themselves)
110
+ instead of blank placeholders, and matches under a non-matching parent are
111
+ no longer hidden.
112
+ - **Replacing `grid.data` prunes tree and detail expansion** of rows that left
113
+ the data, so `getState()` no longer lists rows that are gone.
114
+ - **Exports write booleans as the checkbox shows them**: true only for
115
+ `true`, so strings such as `'false'` or `'yes'` and the number 1 export as
116
+ false. **Currency and progress columns export as numbers**, like number and
117
+ rating columns; text that is not a number exports as an empty cell.
118
+ - **Replacing `grid.data` with new records clears the undo history** (and
119
+ fires `historyChanged`). Undo used to replay onto the old, detached records:
120
+ nothing changed on screen, yet `cellValueChanged` fired for a row the grid no
121
+ longer held. New data that keeps the edited records, such as an appended
122
+ row, keeps its history, and so do in-place edits. A subclass that keeps
123
+ records outside `data` (a row model that drops collapsed rows and restores
124
+ the same objects) overrides the protected `historyRecords()` to keep their
125
+ history too.
126
+ - **`cellValueChanged` from undo or redo reports `rowIndex` -1 when the
127
+ record is pinned or on another page**, instead of the index it had when
128
+ edited (which could name a different row). Listeners that read `rowIndex`
129
+ from a replayed change should identify the row by the event's `data`.
130
+ - `applyEdits()` skips an entry whose `rowIndex` is negative or fractional,
131
+ as it already skipped one past the end of the page. 3.5.0 counted a negative
132
+ index from the end of the page and rounded a fraction down.
133
+ - **`sortExpressions` and `filterExpressions` replace the current sort or filter
134
+ instead of merging into it**, as documented and as `setState` already did;
135
+ `[]`, `null` or `undefined` clears it. Apps that assigned one expression at a
136
+ time to build up a multi-column sort or filter should assign the full list (or
137
+ call `sort()` / `filter()`, which still add). The assigned list applies at the
138
+ next update, against the columns as they are then, so a template may bind it
139
+ before `columns`; reading it back before that returns what was assigned. Sort
140
+ entries for an unknown column or with an unknown direction are dropped with a
141
+ console warning (an omitted direction still sorts ascending).
142
+ - **`currency`, `rating` and `progress` columns filter as numbers**, as their
143
+ type docs already said. The filter panel offers the number conditions (Equals,
144
+ Greater than, Less than and so on) and reads the typed value as a number, and
145
+ `getSchema()` lists the number operands for these columns. A saved state or
146
+ `filter()` call that names a text condition such as `contains` on one of them
147
+ is now dropped with a console warning; in 3.5.0 those conditions threw inside
148
+ the pipeline and filtered nothing. Feature modules read the rule through
149
+ `StateController.isNumericColumn(column)`.
150
+ - Feature modules can reveal a cell, read the inline direction of an arrow key
151
+ under RTL and read the scroll insets through the grid's navigation
152
+ controller (`revealCell`, `towardsInlineEnd`, `scrollInsets`), so the
153
+ enterprise range selection follows core navigation instead of a copy.
154
+ - **Cell templates run again after each data pipeline run.** A column
155
+ `cellTemplate` is called for every visible row, body and pinned, after each
156
+ pipeline run (sort, filter, quick filter, edit, tree toggle), so a template
157
+ that reads other fields of its row stays current. Keep templates light;
158
+ plain and typed cells are not re-rendered by this.
159
+ - Start-pinned columns freeze after the leading checkbox, expansion, reorder
160
+ grip and row-number columns instead of at the grid edge. The grid sets
161
+ `--apex-chrome-offset-row-number`, `--apex-chrome-offset-reorder`,
162
+ `--apex-chrome-offset-selection` and `--apex-chrome-offset-expansion` on the
163
+ host and no longer writes `--scrollbar-offset` inline (the stylesheet
164
+ default stays).
165
+ - A decoration-only change (range selection, formula reference highlight)
166
+ updates cell attributes without re-rendering cell content. For feature
167
+ modules, `bumpDecoration` now only re-applies `CellDecorator` attributes;
168
+ request a pipeline run when content must change.
169
+ - Feature modules can attach a command to a cell edit and its undo step, and
170
+ read the header's sortable rule through `SortController.isSortable`.
171
+ - Pressing on interactive content in a `headerTemplate` (an input, select,
172
+ textarea, button, link or editable element) no longer starts a column drag.
173
+ - When a `columnMoving` listener cancels a live swap, the event is raised once
174
+ per target crossing instead of on every pointer move.
175
+ - The filter panel's `filtering` event carries a proposed copy of the
176
+ expression for panel edits, and the change is applied only after the event
177
+ is not cancelled. Escape pressed outside the panel is no longer swallowed.
178
+ - Switching an applied Empty or Not empty filter to a condition that needs a
179
+ value removes the filter (with a cancellable remove) until a value is typed.
180
+ - `getState()` and `setState()` copy array filter terms, and feature modules
181
+ can register a filter operand by name (`registerFilterOperand` on
182
+ `apex-grid/internal`) so a saved filter that uses it restores.
183
+ - **`sorted`, `filtered`, `pageChanged` and `quickFilterChanged` fire after
184
+ the pipeline run they caused.** With an asynchronous
185
+ `dataPipelineConfiguration` hook, listeners now read the new rows and
186
+ totals. With synchronous hooks they still fire in the same task, after the
187
+ re-render of the new rows. The server-side and infinite row models fetch
188
+ outside the pipeline, so with them these events can still fire before the
189
+ new block arrives.
190
+ - `setQuickFilter` (and the toolbar search box) fires `pageChanged` and
191
+ announces the page when it moves the grid back to the first page. Setting
192
+ the `quickFilter` property or calling `setState` stays silent.
193
+ - Every part a row renders can be styled with `apex-grid::part()`:
194
+ selection, expansion, detail panel, row number and reorder parts, and the
195
+ parts of rows a module renders. Before, only `cell` was reachable, so
196
+ existing `::part()` rules for these names now take effect.
197
+ - **Moving a row on a sorted grid clears the sort** and keeps the moved order,
198
+ so the sort indicator goes away on the first move and `getState` never
199
+ reports a sort and a row order together. Before, an `applyToData` move was
200
+ undone by the re-sort.
201
+ - A move that leaves the row where it is (`moveRow(0, 1, 'before')`, for
202
+ example) returns `false` and fires no `rowMoving` or `rowMoved`.
203
+ - Escape after keyboard grab steps restores the original order (and
204
+ `grid.data` with `applyToData`, and any sort) and fires `rowMoved` for the
205
+ way back, so a persisted order stays in sync.
206
+
207
+ ### Fixed
208
+ - **`setup()` host sizing reaches grids inside a shadow root.** The
209
+ `min-height: 240px` default was adopted into the page only, which never
210
+ styles an element inside a shadow root. Each grid now adopts it into the
211
+ root it connects in, and for its own tag name, so `<apex-grid-enterprise>`
212
+ gets it too.
213
+ - **ArrowDown and ArrowUp in the toolbar's export menu move focus again.** The
214
+ menu looked up the focused item through `document.activeElement`, which is
215
+ the grid's host because the menu lives in the grid's shadow root, so focus
216
+ never moved past the first item.
217
+ - **Text filter conditions no longer throw on a cell that is not a string.**
218
+ Contains, Starts with, Equals and the rest read `null` and `undefined` as
219
+ empty text and match numbers, dates and other values on their text. In 3.5.0
220
+ one such cell (an optional field left empty, a Date in a date column) made
221
+ the filter apply nothing and stopped later sorts until it was removed.
222
+ - `OperandKeys` offers the text operand names for a field whose type is not
223
+ number, string or boolean (a `Date`, for example), matching what the grid
224
+ applies, instead of accepting the value type itself as a condition. A
225
+ nullable or optional field keeps the names of its type (`number | null`
226
+ still takes the number names only), and a field typed `unknown`, such as
227
+ every field of a `Record<string, unknown>` row, keeps every operand name.
228
+ - **Number range conditions no longer match an empty cell.** Greater than, Less
229
+ than and their or equal forms read `null`, and an empty string from CSV or
230
+ JSON data, as 0, so Less than 20 included rows with no value. Empty now
231
+ matches those cells instead.
232
+ - **OR filter criteria are evaluated per column.** An OR on one column no longer
233
+ cancels the OR conditions of other columns, so `(city is Paris OR Oslo) AND
234
+ (id is 3 OR 99)` keeps the matching rows instead of none, and a column left
235
+ with only OR conditions (after removing its first chip) filters again instead
236
+ of keeping every row.
237
+ - A filter entry without `caseSensitive` (from `setState`, `filter()` or an AI
238
+ patch) keeps the column's `filter.caseSensitive` instead of turning case
239
+ sensitivity off.
240
+ - Removing a filter that is not applied, such as typing `-` or `.` first into a
241
+ number filter, or clearing a column that has no filter, no longer fires
242
+ `filtering` and `filtered` or announces that the filter was cleared.
243
+ - When the filter changes (a condition or term is added, edited or removed, or
244
+ the filter is cleared), the grid scrolls back to its first row. The reset
245
+ targeted an element that does not scroll, so a deep scroll offset was clamped
246
+ to the end of the shorter result and the first matches sat above the fold.
247
+ Reapplying the same filter, such as `setState(getState())` or assigning an
248
+ equal `filterExpressions` list on every render, keeps the scroll position.
249
+ - **A batch of edits made inside another undoes as one step.** A
250
+ `cellValueChanged` listener that calls `applyEdits()` during a row commit,
251
+ a bulk edit or a paste no longer splits it, so one undo reverts all of it.
252
+ - Undoing or redoing a large batch reads the view once instead of once per
253
+ cell.
254
+ - `applyEdits()` shows the invalid state of its cells when every edit in the
255
+ batch is rejected by a validator, without waiting for another render.
256
+ - `pattern()` validators with a global (`g`) or sticky (`y`) RegExp give the
257
+ same result every time; repeated valid values no longer alternate between
258
+ passing and failing.
259
+ - Editing a pinned row edits the pinned record; a checkbox or double-click in
260
+ a pinned row used to act on a body row. In row mode a cancelled
261
+ `cellValueChanging` aborts the whole row before anything is written.
262
+ - Keyboard navigation keeps its position per grid: an arrow key in one grid no
263
+ longer continues from a cell clicked in another, and removing a grid no
264
+ longer resets the others. The active cell follows filters, sorting, paging,
265
+ data changes and hidden columns, so the body always keeps one tab stop.
266
+ - Ctrl+End and Ctrl+Home on large grids render the target rows and keep focus
267
+ on the target cell, and keyboard moves no longer leave the active cell
268
+ hidden behind the sticky header, pinned rows or pinned columns. Full-width
269
+ rows from feature modules (group headers, total rows) take keyboard focus.
270
+ - Tree data with `defaultExpanded` keeps a parent the user collapsed when
271
+ `grid.data` is reassigned. Two rows with the same path are both shown, and
272
+ `treeRowExpanding` and `treeRowExpanded` list parents that exist only as a
273
+ path segment.
274
+ - Exported dates stored as `'YYYY-MM-DD'` strings carry the calendar day the
275
+ grid shows; west of UTC they were written one day early. An invalid `Date`
276
+ exports as an empty cell instead of throwing (CSV) or writing a value the
277
+ spreadsheet app reports as corrupt (XLSX).
278
+ - Exporting with source `'selected'` writes rows in the order the grid shows
279
+ them, followed by selected rows the view hides.
280
+ - Date and currency cells reuse their `Intl` formatters, so re-renders and
281
+ large exports no longer build one per cell; a malformed locale, currency or
282
+ date format falls back to a default instead of failing every cell.
283
+ - Sparkline cells no longer show a misleading 0% delta when the series starts
284
+ at zero, and never print -0%.
285
+ - A `cellTemplate` that reads other fields of its row updates after an inline
286
+ edit, `applyEdits`, paste, fill or a data change, in body and pinned rows.
287
+ - The tree data chevron and `aria-expanded` follow every expand and collapse,
288
+ including `expandTreeRow` and `collapseTreeRow`.
289
+ - Range selection highlights stay on the selected positions after a sort,
290
+ filter or expand moves rows.
291
+ - With the row-number gutter, reorder grip, selection or expansion column
292
+ shown, a horizontal scroll no longer stacks those cells or a start-pinned
293
+ column on top of each other, in LTR and RTL.
294
+ - Column group headers sit over their member columns when the reorder grip or
295
+ the row-number gutter is shown, and the group row's cells over pinned and
296
+ leading columns stay frozen on horizontal scroll.
297
+ - Pinned columns inside a CSS-scaled container no longer overlap on
298
+ horizontal scroll.
299
+ - A header drag toward later columns (rightward, or leftward with
300
+ `dir="rtl"`) keeps following the pointer after the first swap and always
301
+ ends on release, including a release outside the header or a lost release.
302
+ - Column autosize no longer throws when group header, total or loading rows
303
+ are rendered.
304
+ - With `dir="rtl"` the column resize guide is drawn on the dragged edge.
305
+ - The filter panel: a cancelled change really leaves the filter unchanged, a
306
+ condition picked on a fresh panel is reported and announced as an add, the
307
+ filter icon of the open column closes it with a mouse, the panel stays under
308
+ its column on scroll, resize and keyboard column switches, and it no longer
309
+ runs off screen when the root font size is not 16px.
310
+ - `setState(getState())` keeps the scroll position when the filter holds an
311
+ array term, and the filter panel chip lists such a term's values.
312
+ - With an asynchronous pipeline hook, a slower earlier run no longer
313
+ overwrites the rows of a newer quick filter, filter or sort, and a run
314
+ replaced before it finishes no longer holds back the newer one's calls and
315
+ events.
316
+ - A `quickFilterChanging` listener that assigns `grid.quickFilter` (to trim
317
+ the input, for example) keeps its value, and `quickFilterChanged` reports
318
+ it. A substitute equal to the current filter changes nothing and
319
+ `setQuickFilter` returns `false`.
320
+ - Changing `grid.tree` after the first render (turning tree mode on or off,
321
+ or changing `getDataPath` or `defaultExpanded`) rebuilds the rows.
322
+ - `grid.data` set to `null` or `undefined` (a framework prop that is still
323
+ loading) shows an empty grid and reads back as `[]`, instead of throwing
324
+ and keeping the previous rows.
325
+ - `moveColumn` no longer fires `columnMoving` for a move that changes nothing.
326
+ - Shift+click and Ctrl or Cmd+click beside the checkbox in the selection cell
327
+ do the same range or additive selection as on the checkbox itself.
328
+ - When a `rowSelecting` listener cancels the change, the row and select-all
329
+ checkboxes show the real selection state instead of staying checked.
330
+ - Moving a row while a column filter or the quick filter is active keeps every
331
+ hidden row in its place, in the manual order and in `grid.data` with
332
+ `applyToData`. Before, hidden rows were pushed to the end.
333
+ - Replacing `grid.data` carries the manual row order and row pins onto the
334
+ new rows (by `rowId` when set, otherwise rows that left the data are
335
+ dropped). New rows can be move targets, a reload no longer disables
336
+ reordering and header sorting, and the pinned band no longer shows a stale
337
+ copy of a reloaded row.
338
+ - A row drag ends when the pointer is released or cancelled anywhere on the
339
+ page, or when a move arrives with no button held, and a grid removed
340
+ mid-drag drops its ghost.
341
+ - With `rowPinning.enabled` set to `false`, `pinnedRows` and the aria row
342
+ counts no longer report pinned rows and those rows get their reorder grip
343
+ back. The pins are kept for when pinning is turned back on.
344
+
7
345
  ## [3.5.0] - 2026-09-12
8
346
 
9
347
  A feature and accessibility release. No exported symbol was removed or renamed.
package/LICENSE.md ADDED
@@ -0,0 +1,125 @@
1
+ ## 📄 License Options for ApexGrid
2
+
3
+ ApexGrid is offered under a **dual-license model** to support individuals, startups, and commercial products of all sizes.
4
+
5
+ One license key works across every Apex product, so a customer of one does not buy a second for another.
6
+
7
+ ---
8
+
9
+ ### 🔓 Community License (Free)
10
+
11
+ For individuals, non-profits, educators, and small businesses whose **annual revenue, operating budget, funding, or equivalent financial resources are less than $2 million USD**.
12
+
13
+ ✅ What's allowed:
14
+
15
+ - Personal, educational, or non-profit use
16
+ - Commercial use by small orgs (under $2M in annual revenue, budget or funding)
17
+ - Modifications, and redistribution of the library itself (with attribution)
18
+
19
+ 🚫 Not allowed:
20
+
21
+ - Use by companies or entities at or above $2M in annual revenue, budget or funding
22
+ - Use in competing data grid or charting products
23
+ - Sublicensing under different terms
24
+ - Embedding in a product or platform used by other people, which needs the OEM License below whatever your size
25
+
26
+ ➡ By using ApexGrid under this license, you confirm that **you qualify as a Small Organization**.
27
+
28
+ ---
29
+
30
+ ### 💼 Commercial License (Paid)
31
+
32
+ The Commercial License applies to all paid SKUs (Pro, Premium and OEM).
33
+
34
+ The **Community License** is available at no cost **only** to organizations whose **annual revenue, operating budget, funding, or equivalent financial resources are under $2M USD**.
35
+ If you, your parent company, affiliates, or any entity under common control reach $2M USD or more on any of those measures, you must purchase one of our paid licenses.
36
+
37
+ Pro and Premium are priced per developer, and priced on the features you use rather than on your size.
38
+
39
+ ✅ What's included:
40
+
41
+ - Use in internal tools and commercial applications
42
+ - Modifications and app-level distribution
43
+ - 12-month subscription with updates & support
44
+
45
+ 🚫 Not allowed:
46
+
47
+ - Redistribution in toolkits, SDKs, or platforms
48
+ - Use by unlicensed developers
49
+ - Competing data grid or charting products
50
+
51
+ ---
52
+
53
+ ### 🎚 What each plan includes for ApexGrid
54
+
55
+ - **Free, at Community and above:** ApexGrid itself.
56
+ - **Premium and above:** the ApexGrid Enterprise add-on, a separate package (`apex-grid-enterprise`) covering Row Grouping, Aggregations, Pivoting, the Server-Side Row Model, Advanced Filter Builder, Range Selection, Integrated Charts, Formula Engine, AI Toolkit and Companion Elements.
57
+
58
+ For the full comparison see [https://apexcharts.com/pricing](https://apexcharts.com/pricing).
59
+
60
+ ---
61
+
62
+ ### 🏛 Non-Profit Tiers
63
+
64
+ Non-Profits are treated like commercial entities:
65
+
66
+ - Annual operating budget **less than $2 million USD**
67
+ - _Eligible for Community licenses_
68
+
69
+ or
70
+
71
+ - Annual operating budget **greater than $2 million USD**
72
+ - _Commercial license with possible discounts for Community, Pro, Premium and OEM products_
73
+
74
+ ---
75
+
76
+ ### 🔄 OEM / Redistribution License (Paid)
77
+
78
+ Required if you are **embedding ApexGrid into a product or platform used by other people**, such as:
79
+
80
+ - No-code dashboards
81
+ - Developer platforms
82
+ - Embedded BI tools
83
+ - SDKs
84
+
85
+ ✅ What's included:
86
+
87
+ - Redistribution rights for **one application or product**
88
+ - **Unlimited developer seats** on that application, with no separate per-developer license to buy
89
+ - **Every licensed feature**, at the highest tier
90
+ - 12-month subscription with updates & support
91
+
92
+ Your end users need no license of their own. A second application needs a second OEM license. White-label, resale, and affiliate arrangements are quoted separately.
93
+
94
+ ✅ OEM **not required** if your app simply renders static grids and users **cannot** configure or interact with them.
95
+
96
+ ---
97
+
98
+ ### ⏳ When a subscription ends
99
+
100
+ Your license key expires, and **licensed features render with a trial watermark**. You no longer receive updates, new features, or technical support. Work built only from free features is unaffected, and your existing deployment keeps running.
101
+
102
+ For OEM licenses, continued redistribution requires an active subscription.
103
+
104
+ ---
105
+
106
+ ### ⚠️ License Acceptance
107
+
108
+ By installing ApexGrid (e.g., via `npm install apex-grid`), you are agreeing to the applicable license based on your usage:
109
+
110
+ - Community License (if under $2M in annual revenue, budget or funding)
111
+ - Commercial License (if at or above $2M in annual revenue, budget or funding)
112
+ - OEM License (if redistributing to third-party users)
113
+
114
+ ---
115
+
116
+ ### 🛠 Need a License or Have Questions?
117
+
118
+ 📧 Contact us at [sales@apexcharts.com](mailto:sales@apexcharts.com)
119
+ 📚 Read full license agreements here: [https://apexcharts.com/license](https://apexcharts.com/license)
120
+
121
+ ---
122
+
123
+ Copyright © 2026 ApexCharts. All rights reserved.
124
+
125
+ Thank you for supporting ApexGrid! Your licensing keeps the Community License available to individuals and small teams.
package/README.md CHANGED
@@ -45,7 +45,7 @@ import { setup } from 'apex-grid';
45
45
  setup();
46
46
  ```
47
47
 
48
- That single call registers `<apex-grid>` and adopts a default host stylesheet (`height: 100%; min-height: 240px`). The grid is styled out-of-the-box; no theme CSS import is required.
48
+ That single call registers `<apex-grid>` and adopts a default host stylesheet (`height: 100%; min-height: 240px`), also inside any shadow root a grid connects in. The grid is styled out-of-the-box; no theme CSS import is required.
49
49
 
50
50
  ### Render the grid
51
51
 
@@ -111,7 +111,7 @@ Without this, `<apex-grid>` is an inert unknown element.
111
111
 
112
112
  ### 3. (Optional) Customize the look
113
113
 
114
- The grid is styled out-of-the-box; there is no theme to import. Customize it by overriding the `--ag-*` CSS custom properties on `apex-grid` (or any ancestor); a one-line brand override cascades to every tint:
114
+ The grid is styled out-of-the-box; there is no theme to import. Customize it by overriding the `--ag-*` CSS custom properties on `apex-grid` or on any ancestor: `:root`, a dashboard panel, or the shadow host of a web component the grid renders in (custom properties inherit through shadow boundaries, however deep). A one-line brand override cascades to every tint:
115
115
 
116
116
  ```css
117
117
  apex-grid {
@@ -122,16 +122,40 @@ apex-grid {
122
122
  }
123
123
  ```
124
124
 
125
- See [`src/styles/_tokens.scss`](src/styles/_tokens.scss) for the full token list (brand, surfaces, text, semantic state colors, typography, spacing, motion).
125
+ See [`src/styles/_token-values.scss`](src/styles/_token-values.scss) for the full token list and defaults (surfaces, text, semantic state colors, typography, spacing, motion).
126
+
127
+ **Where the defaults live.** The first grid to connect adopts the token defaults into its document, on `:where(:root)`, through `adoptedStyleSheets` (no `<style>` element, so no CSP nonce). Zero specificity means any value you set wins, and `var(--ag-*)` reads in your own CSS resolve to the defaults. If you manage your own CSS, opt out with `setup({ tokenStyles: false })` and import the same defaults as a file (also the way to get them into server-rendered HTML):
128
+
129
+ ```ts
130
+ import 'apex-grid/tokens.css';
131
+ ```
132
+
133
+ The grid renders the same without the sheet; it carries the defaults as fallbacks. Three groups have no root default, so page CSS reading them needs its own fallback:
134
+
135
+ - `--ag-brand` and `--ag-brand-strong`, so an Ignite UI palette set on any ancestor keeps tinting the grid (see below).
136
+ - The derived washes `--ag-brand-soft`, `--ag-brand-softer`, `--ag-brand-line`, `--ag-row-hover` and `--ag-surface-alt`. The grid mixes them from the brand and surface in effect, so they follow a brand set on an ancestor. Set them only to override the mix.
137
+
138
+ **Dark mode.** `theme="dark"` reads its own colour inputs, `--ag-dark-*` (`--ag-dark-surface`, `--ag-dark-text`, `--ag-dark-hairline`, `--ag-dark-shadow-card`, and so on), so a light palette set on a shared ancestor never puts a white surface under the dark theme's light text. Sizing, typography, motion, the brand and the base state colours are shared by both modes. To theme both from one place, set both palettes:
139
+
140
+ ```css
141
+ .dashboard {
142
+ --ag-brand: #7c3aed;
143
+ --ag-surface: #fdfcff;
144
+ --ag-dark-surface: #140f24;
145
+ }
146
+ ```
147
+
148
+ `theme="tinted"` mixes a little brand into the surface, gridline and muted-text values in effect, including any you set. Its row separators are the brand alone, so a tinted grid ignores `--ag-border` and `--ag-dark-border`.
126
149
 
127
150
  **Grid edge / shadow.** By default the grid shows a flat 1px hairline edge (no drop shadow). Control it with the `--ag-grid-shadow` hook, an opt-in override that is not one of the `_tokens.scss` defaults:
128
151
 
129
152
  ```css
130
- apex-grid { --ag-grid-shadow: var(--ag-shadow-card); } /* elevated floating-card look */
131
- apex-grid { --ag-grid-shadow: none; } /* remove the edge entirely */
153
+ apex-grid { --ag-grid-shadow: var(--ag-shadow-card); } /* elevated floating-card look */
154
+ apex-grid[theme~='dark'] { --ag-grid-shadow: var(--ag-dark-shadow-card); } /* the same, for dark grids */
155
+ apex-grid { --ag-grid-shadow: none; } /* remove the edge entirely */
132
156
  ```
133
157
 
134
- If you embed the grid in an Ignite UI app, the brand tokens automatically re-tint from its palette: they read `--ig-primary-500` with the grid's own default as the fallback. That is a plain CSS variable lookup, so the grid neither depends on nor bundles Ignite UI.
158
+ If you embed the grid in an Ignite UI app, the brand tokens automatically re-tint from its palette, wherever it is declared: they read `--ig-primary-500` with the grid's own default as the fallback. That is a plain CSS variable lookup, so the grid neither depends on nor bundles Ignite UI.
135
159
 
136
160
  ### 4. Size the host
137
161
 
@@ -334,6 +358,14 @@ re-renders. The returned tally splits `applied` from `unchanged`, `invalid`,
334
358
  one. Edits naming a row out of range or a column the user could not edit either
335
359
  are skipped rather than throwing.
336
360
 
361
+ `rowIndex` is view-relative, so it reaches the current page only. To edit rows
362
+ anywhere (other pages, pinned rows, rows under a collapsed tree parent), name
363
+ the record itself with `row`:
364
+
365
+ ```ts
366
+ grid.applyEdits(grid.selectedRows.map((row) => ({ row, column: 'discount', value: 0 })));
367
+ ```
368
+
337
369
  ### Keyboard navigation and accessibility
338
370
 
339
371
  The grid body is a single tab stop: Tab enters the grid, and the active cell
@@ -416,7 +448,7 @@ grid.unpinRow(data[0]);
416
448
  grid.pinnedRows; // { top: T[], bottom: T[] }
417
449
  ```
418
450
 
419
- Pinned rows render in sticky top / bottom bands outside the virtualizer and are lifted out of the scrollable set (no duplication). Selection and styling work by row reference. Events: `rowPinning` (cancellable), `rowPinned`.
451
+ Pinned rows render in sticky top / bottom bands outside the virtualizer and are lifted out of the scrollable set (no duplication). Selection and styling work by row reference. Replacing `grid.data` carries the pins onto the new rows (by `rowId` when set, else a pin whose row left the data is dropped). While `rowPinning.enabled` is false the pinned rows return to the body and `pinnedRows` reports none, but the pins are kept (`getState` still saves them) and re-enabling brings them back. Events: `rowPinning` (cancellable), `rowPinned`.
420
452
 
421
453
  ### Row drag-reorder
422
454
 
@@ -426,7 +458,7 @@ grid.rowReordering = { enabled: true }; // add applyToData: true to splice gri
426
458
  grid.moveRow(0, 4, 'after');
427
459
  ```
428
460
 
429
- By default (`rowReordering.handle`, boolean, default `true`) a six-dot grip handle shows at the start of every reorderable row, and dragging lifts a full-row floating ghost that follows the cursor. Both interaction modes are supported: **handle mode** (only the grip starts a drag, so the rest of the row stays free for selection and editing) and **whole-row mode** (set `handle: false` to drag from anywhere on the row, excluding interactive sub-parts like inputs and buttons). Or use the keyboard: focus a row, **Space** to grab, **Arrow** keys to move, **Space / Enter** to drop, **Esc** to cancel. The grid holds a manual order that is mutually exclusive with sorting (applying a sort clears it). By default the app persists the order via the `rowMoved` event; set `applyToData: true` to splice `grid.data` directly. Events: `rowMoving` (cancellable), `rowMoved { from, to, data }`.
461
+ By default (`rowReordering.handle`, boolean, default `true`) a six-dot grip handle shows at the start of every reorderable row, and dragging lifts a full-row floating ghost that follows the cursor. Both interaction modes are supported: **handle mode** (only the grip starts a drag, so the rest of the row stays free for selection and editing) and **whole-row mode** (set `handle: false` to drag from anywhere on the row, excluding interactive sub-parts like inputs and buttons). Or use the keyboard: focus a row, **Space** to grab, **Arrow** keys to move, **Space / Enter** to drop, **Esc** to cancel. The grid holds a manual order that is mutually exclusive with sorting (applying a sort clears it, and a move clears the sort). Rows hidden by a filter keep their place when a visible row moves, and the order follows `grid.data` when it is replaced (by `rowId` when set). By default the app persists the order via the `rowMoved` event; set `applyToData: true` to splice `grid.data` directly. Events: `rowMoving` (cancellable), `rowMoved { from, to, position, toIndex, data }`, where `toIndex` is the view index the row lands at.
430
462
 
431
463
  ### Row expansion (master-detail)
432
464
 
@@ -514,7 +546,7 @@ Search input has a `debounce` attribute (default `200`ms).
514
546
 
515
547
  ### Theming
516
548
 
517
- The grid styles itself through `--ag-*` CSS custom properties; override them on `apex-grid` (or any ancestor) to rebrand; see [`src/styles/_tokens.scss`](src/styles/_tokens.scss) for the full list. Where an Ignite UI palette is present, the brand tokens auto-tint from `--ig-primary-500`.
549
+ The grid styles itself through `--ag-*` CSS custom properties; override them on `apex-grid` or any ancestor (shadow hosts included) to rebrand, and use `--ag-dark-*` for dark grids. See [Customize the look](#3-optional-customize-the-look) and [`src/styles/_token-values.scss`](src/styles/_token-values.scss) for the full list. Where an Ignite UI palette is present, the brand tokens auto-tint from `--ig-primary-500`.
518
550
 
519
551
  Style with CSS parts on the grid, paginator, and toolbar:
520
552
 
@@ -523,6 +555,28 @@ apex-grid::part(paginator) { background: var(--surface-2); }
523
555
  apex-grid-toolbar::part(search-input) { font-family: var(--font-mono); }
524
556
  ```
525
557
 
558
+ Body rows re-export these parts, so `apex-grid::part(name)` reaches them:
559
+
560
+ | Part | What |
561
+ |---|---|
562
+ | `cell` | A data cell |
563
+ | `selection-cell`, `selection-checkbox` | Selection column cell and its checkbox |
564
+ | `expansion-cell`, `expansion-toggle`, `expansion-chevron` | Expansion column cell, its button and the chevron icon |
565
+ | `detail-panel` | Expanded detail panel container |
566
+ | `row-number-cell`, `row-number` | Row-number gutter cell and its label (`coordinateHints`) |
567
+ | `reorder-handle-cell`, `reorder-handle`, `reorder-grip` | Row-reorder grip cell, handle and icon (handle mode) |
568
+ | `group-row` | Default container of a row a feature module renders full width |
569
+
570
+ Parts used inside your own `detailTemplate` content are re-exported when you list them in `expansion.exportParts`:
571
+
572
+ ```js
573
+ grid.expansion = {
574
+ enabled: true,
575
+ exportParts: ['order-summary'],
576
+ detailTemplate: ({ data }) => html`<div part="order-summary">${data.total}</div>`,
577
+ };
578
+ ```
579
+
526
580
  ### Right-to-left
527
581
 
528
582
  Set `dir="rtl"` on the grid or any ancestor and the whole grid mirrors — no
@@ -688,7 +742,7 @@ All events bubble and are composed across shadow boundaries. Names ending in `-i
688
742
  | `historyChanged` | no | `{ canUndo, canRedo }` |
689
743
  | `rowSelecting` / `rowSelected` | yes / no | `{ added, removed }` |
690
744
  | `rowPinning` / `rowPinned` | yes / no | `{ row, position }` |
691
- | `rowMoving` / `rowMoved` | yes / no | `{ from, to, data }` |
745
+ | `rowMoving` / `rowMoved` | yes / no | `{ from, to, position, toIndex, data }` |
692
746
  | `rowExpanding` / `rowExpanded` | yes / no | row context |
693
747
  | `treeRowExpanding` / `treeRowExpanded` | yes / no | row context |
694
748
  | `stateChanged` | no | `{ state }` (debounced; only while listened) |