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
@@ -41,11 +41,38 @@ export interface GridFeatureModule<T extends object = any> {
41
41
  * Controllers that don't implement this are skipped, so the community grid
42
42
  * (which registers no modules) is unaffected.
43
43
  *
44
+ * Transforms run in a fixed order set by {@link transformOrder}, not in the
45
+ * order modules were registered, because they do not commute (a grand-total
46
+ * row appended before grouping would itself be grouped).
47
+ *
44
48
  * @remarks Unstable; part of `apex-grid/internal`.
45
49
  */
46
50
  export interface RowTransformer<T extends object = any> {
47
51
  /** Return the transformed rows. Must not mutate the input array in place. */
48
52
  processRows(rows: ReadonlyArray<T>): T[];
53
+ /**
54
+ * Position of this transform in the module chain: lower runs first, ties keep
55
+ * registration order, unset means `0`. First-party values: grouping `100`,
56
+ * pivot `200`, grand-total row `1000`. Transforms that restructure the view
57
+ * come first; rows that summarise the finished view come last. Leaving it
58
+ * unset (`0`) runs the transform before grouping.
59
+ */
60
+ readonly transformOrder?: number;
61
+ /**
62
+ * Whether `row` is one this transform synthesized (a group header, a total
63
+ * row) rather than a data record. Synthesized rows are kept out of row
64
+ * selection and export (see {@link StateController.isDataRow}). Leave it
65
+ * unset when the transform's output rows are the view's records (pivot).
66
+ */
67
+ isSyntheticRow?(row: T): boolean;
68
+ /**
69
+ * The data records a synthesized `row` stands for that the view does not
70
+ * also hold, in view order: a collapsed group header's leaves. Return an
71
+ * empty array when they are in the view too (an expanded group) or the row
72
+ * stands for none (a total row). Lets Select all and a `'view'` export
73
+ * cover collapsed groups (see {@link StateController.viewRecords}).
74
+ */
75
+ hiddenRecords?(row: T): ReadonlyArray<T>;
49
76
  }
50
77
  /** Runtime type-guard for {@link RowTransformer}. */
51
78
  export declare function isRowTransformer<T extends object>(controller: unknown): controller is RowTransformer<T>;
@@ -69,6 +96,12 @@ export interface PresentedRow {
69
96
  readonly expanded?: boolean;
70
97
  /** Shadow part for the container element; defaults to `group-row`. */
71
98
  readonly part?: string;
99
+ /**
100
+ * Parts used inside {@link content} that the row should re-export, so
101
+ * `apex-grid::part(name)` can style them. The container {@link part} is
102
+ * exported without being listed here.
103
+ */
104
+ readonly exportParts?: ReadonlyArray<string>;
72
105
  }
73
106
  /**
74
107
  * Optional capability a feature module's controller may implement to render
@@ -104,11 +137,34 @@ export interface RowAriaProvider<T extends object = any> {
104
137
  }
105
138
  /** Runtime type-guard for {@link RowAriaProvider}. */
106
139
  export declare function isRowAriaProvider<T extends object>(controller: unknown): controller is RowAriaProvider<T>;
140
+ /**
141
+ * Optional capability a module's controller may implement to label the
142
+ * spreadsheet coordinate hints (`coordinateHints`): the row-number gutter and
143
+ * the column letters. A module whose references address the data rather than
144
+ * the view (the enterprise formula engine) answers with the coordinates a
145
+ * reference reads, so the hints stay true through sort, filter, paging, hidden
146
+ * columns and column moves. Return `null` for no label, or `undefined` to leave
147
+ * the default (view position) in place.
148
+ *
149
+ * @remarks Unstable; part of `apex-grid/internal`.
150
+ */
151
+ export interface CoordinateLabeler<T extends object = any> {
152
+ /** The gutter label for a row. */
153
+ rowCoordinate(row: T): string | null | undefined;
154
+ /** The letter label for a column key. */
155
+ columnCoordinate(key: string): string | null | undefined;
156
+ }
157
+ /** Runtime type-guard for {@link CoordinateLabeler}. */
158
+ export declare function isCoordinateLabeler<T extends object>(controller: unknown): controller is CoordinateLabeler<T>;
107
159
  /** Per-cell context handed to a {@link CellDecorator}. */
108
160
  export interface CellDecoratorContext<T extends object = any> {
109
161
  /** The row's backing data (or a synthesized row object). */
110
162
  readonly row: T;
111
- /** Index of the row within the current page/view (matches `host.pageItems`). */
163
+ /**
164
+ * Index of the row within the current page/view (matches `host.pageItems`),
165
+ * or `-1` for a row rendered in a pinned band, which is outside that view.
166
+ * Key pinned-row state by {@link row} instead.
167
+ */
112
168
  readonly rowIndex: number;
113
169
  /** The cell's column configuration. */
114
170
  readonly column: ColumnConfiguration<T>;
@@ -202,7 +258,8 @@ export declare function isCellInteractionHandler<T extends object>(controller: u
202
258
  * / {@link StateController.restoreModuleState}, driven by `ApexGrid.getState` /
203
259
  * `ApexGrid.setState`). The grid collects each implementer's
204
260
  * {@link serializeState} output under the module's id and dispatches the
205
- * matching value back via {@link restoreState} on restore.
261
+ * matching value back via {@link restoreState} on restore. Ids with no
262
+ * implementer are reported back by {@link StateController.restoreModuleState}.
206
263
  *
207
264
  * Controllers that don't implement this are skipped, so the community grid
208
265
  * (which registers no modules) contributes nothing to a snapshot.
@@ -212,7 +269,7 @@ export declare function isCellInteractionHandler<T extends object>(controller: u
212
269
  export interface SerializableModule {
213
270
  /**
214
271
  * Return a JSON-serializable snapshot of this module's restorable state
215
- * (e.g. grouping's `groupBy` + aggregations). Exclude transient/derived
272
+ * (e.g. the options the feature was configured with). Exclude transient/derived
216
273
  * state and anything not JSON-safe (functions, DOM nodes).
217
274
  */
218
275
  serializeState(): unknown;
@@ -13,6 +13,12 @@ export function isRowAriaProvider(controller) {
13
13
  controller !== null &&
14
14
  typeof controller.describeRow === 'function');
15
15
  }
16
+ export function isCoordinateLabeler(controller) {
17
+ return (typeof controller === 'object' &&
18
+ controller !== null &&
19
+ typeof controller.rowCoordinate === 'function' &&
20
+ typeof controller.columnCoordinate === 'function');
21
+ }
16
22
  export function isCellDecorator(controller) {
17
23
  return (typeof controller === 'object' &&
18
24
  controller !== null &&
@@ -1 +1 @@
1
- {"version":3,"file":"feature-module.js","sourceRoot":"","sources":["../../src/internal/feature-module.ts"],"names":[],"mappings":"AAwDA,MAAM,UAAU,gBAAgB,CAC9B,UAAmB;IAEnB,OAAO,CACL,OAAO,UAAU,KAAK,QAAQ;QAC9B,UAAU,KAAK,IAAI;QACnB,OAAQ,UAAgC,CAAC,WAAW,KAAK,UAAU,CACpE,CAAC;AACJ,CAAC;AAqCD,MAAM,UAAU,cAAc,CAC5B,UAAmB;IAEnB,OAAO,CACL,OAAO,UAAU,KAAK,QAAQ;QAC9B,UAAU,KAAK,IAAI;QACnB,OAAQ,UAA8B,CAAC,UAAU,KAAK,UAAU,CACjE,CAAC;AACJ,CAAC;AAyBD,MAAM,UAAU,iBAAiB,CAC/B,UAAmB;IAEnB,OAAO,CACL,OAAO,UAAU,KAAK,QAAQ;QAC9B,UAAU,KAAK,IAAI;QACnB,OAAQ,UAAiC,CAAC,WAAW,KAAK,UAAU,CACrE,CAAC;AACJ,CAAC;AA+CD,MAAM,UAAU,eAAe,CAC7B,UAAmB;IAEnB,OAAO,CACL,OAAO,UAAU,KAAK,QAAQ;QAC9B,UAAU,KAAK,IAAI;QACnB,OAAQ,UAA+B,CAAC,YAAY,KAAK,UAAU,CACpE,CAAC;AACJ,CAAC;AAmBD,MAAM,UAAU,oBAAoB,CAAC,UAAmB;IACtD,OAAO,CACL,OAAO,UAAU,KAAK,QAAQ;QAC9B,UAAU,KAAK,IAAI;QACnB,OAAQ,UAAiC,CAAC,kBAAkB,KAAK,UAAU,CAC5E,CAAC;AACJ,CAAC;AAoCD,MAAM,UAAU,wBAAwB,CACtC,UAAmB;IAEnB,OAAO,CACL,OAAO,UAAU,KAAK,QAAQ;QAC9B,UAAU,KAAK,IAAI;QACnB,OAAQ,UAAwC,CAAC,qBAAqB,KAAK,UAAU,CACtF,CAAC;AACJ,CAAC;AA+BD,MAAM,UAAU,oBAAoB,CAAC,UAAmB;IACtD,OAAO,CACL,OAAO,UAAU,KAAK,QAAQ;QAC9B,UAAU,KAAK,IAAI;QACnB,OAAQ,UAAiC,CAAC,cAAc,KAAK,UAAU;QACvE,OAAQ,UAAiC,CAAC,YAAY,KAAK,UAAU,CACtE,CAAC;AACJ,CAAC","sourcesContent":["import type { ReactiveController, TemplateResult } from 'lit';\n// Type-only import — erased at runtime, so this does not create a runtime\n// import cycle with `controllers/state.ts` (which imports the guards below).\nimport type { StateController } from '../controllers/state.js';\nimport type { ColumnConfiguration, GridHost } from './types.js';\n\n/**\n * Extension point for layering optional features onto the grid without baking\n * them into the community build.\n *\n * A feature module contributes a {@link ReactiveController} that is constructed\n * alongside the grid's built-in controllers (see {@link StateController}). It is\n * the seam used by the first-party `@apexcharts/grid-enterprise` package; it is\n * intentionally minimal and additive so the community `<apex-grid>` element is\n * unaffected (it registers zero modules).\n *\n * @remarks This API lives under the unstable `apex-grid/internal` entry point.\n */\nexport interface GridFeatureModule<T extends object = any> {\n /**\n * Stable identifier, e.g. `'grouping'`. Used to de-duplicate modules and to\n * look the controller up later via {@link StateController.module}.\n */\n readonly id: string;\n\n /**\n * Construct the feature's controller. The controller is expected to register\n * itself with the host (typically `host.addController(this)` in its\n * constructor), exactly like the built-in controllers.\n *\n * @param host The grid host the controller drives.\n * @param state The owning {@link StateController}, passed so a module can\n * coordinate with cross-cutting grid state (e.g. request a cell-decoration\n * refresh via {@link StateController.bumpDecoration} or look up a peer\n * module). Modules that don't need it may ignore the argument.\n */\n create(host: GridHost<T>, state: StateController<T>): ReactiveController;\n}\n\n/**\n * Optional capability a feature module's controller may implement to transform\n * the row set *after* the built-in pipeline (filter → sort → tree) and *before*\n * pagination is sliced at render time. Lets a module inject or reorder rows —\n * e.g. synthesized group-header rows — in the rendered `dataView`.\n *\n * Controllers that don't implement this are skipped, so the community grid\n * (which registers no modules) is unaffected.\n *\n * @remarks Unstable; part of `apex-grid/internal`.\n */\nexport interface RowTransformer<T extends object = any> {\n /** Return the transformed rows. Must not mutate the input array in place. */\n processRows(rows: ReadonlyArray<T>): T[];\n}\n\n/** Runtime type-guard for {@link RowTransformer}. */\nexport function isRowTransformer<T extends object>(\n controller: unknown\n): controller is RowTransformer<T> {\n return (\n typeof controller === 'object' &&\n controller !== null &&\n typeof (controller as RowTransformer<T>).processRows === 'function'\n );\n}\n\n/** Per-row context handed to a {@link RowPresenter}. */\nexport interface RowPresenterContext<T extends object = any> {\n /** The grid's visible columns (post-reorder/visibility). */\n readonly columns: ReadonlyArray<ColumnConfiguration<T>>;\n /** Index of the row within the current page/view. */\n readonly rowIndex: number;\n}\n\n/**\n * A module-rendered, full-width row. Rendered inside a container that spans all\n * columns (`grid-column: 1 / -1`), mirroring the master-detail panel pattern.\n */\nexport interface PresentedRow {\n /** Template rendered inside the full-width container. */\n readonly content: TemplateResult;\n /** Optional 1-based `aria-level` applied to the row element. */\n readonly level?: number;\n /** Optional `aria-expanded` for the row element; omit for non-expandable rows. */\n readonly expanded?: boolean;\n /** Shadow part for the container element; defaults to `group-row`. */\n readonly part?: string;\n}\n\n/**\n * Optional capability a feature module's controller may implement to render\n * specific rows as full-width custom content (e.g. group headers) instead of\n * the normal cell grid. Returns `null` for rows the module does not own.\n *\n * @remarks Unstable; part of `apex-grid/internal`.\n */\nexport interface RowPresenter<T extends object = any> {\n presentRow(row: T, ctx: RowPresenterContext<T>): PresentedRow | null;\n}\n\n/** Runtime type-guard for {@link RowPresenter}. */\nexport function isRowPresenter<T extends object>(\n controller: unknown\n): controller is RowPresenter<T> {\n return (\n typeof controller === 'object' &&\n controller !== null &&\n typeof (controller as RowPresenter<T>).presentRow === 'function'\n );\n}\n\n/** ARIA tree/expansion semantics a module can attach to a normal (cell-rendered) row. */\nexport interface RowAria {\n /** 1-based `aria-level` for the row element. */\n readonly level?: number;\n /** `aria-expanded` for the row element; omit for non-expandable rows. */\n readonly expanded?: boolean;\n}\n\n/**\n * Optional capability a module's controller may implement to attach ARIA\n * tree/expansion semantics (`aria-level` / `aria-expanded`) to rows that still\n * render as a normal cell grid — unlike {@link RowPresenter}, which replaces the\n * cells with full-width content. Used by pivoting's expandable row groups, whose\n * parent rows must keep showing per-column aggregate values. Returns `null` for\n * rows the module does not own.\n *\n * @remarks Unstable; part of `apex-grid/internal`.\n */\nexport interface RowAriaProvider<T extends object = any> {\n describeRow(row: T, ctx: RowPresenterContext<T>): RowAria | null;\n}\n\n/** Runtime type-guard for {@link RowAriaProvider}. */\nexport function isRowAriaProvider<T extends object>(\n controller: unknown\n): controller is RowAriaProvider<T> {\n return (\n typeof controller === 'object' &&\n controller !== null &&\n typeof (controller as RowAriaProvider<T>).describeRow === 'function'\n );\n}\n\n/** Per-cell context handed to a {@link CellDecorator}. */\nexport interface CellDecoratorContext<T extends object = any> {\n /** The row's backing data (or a synthesized row object). */\n readonly row: T;\n /** Index of the row within the current page/view (matches `host.pageItems`). */\n readonly rowIndex: number;\n /** The cell's column configuration. */\n readonly column: ColumnConfiguration<T>;\n /** The cell's resolved value (`row[column.key]`). */\n readonly value: unknown;\n}\n\n/**\n * A set of host attributes a module asks the cell to reflect on itself. This is\n * the only thing a {@link CellDecorator} returns — styling is expressed by\n * keying CSS off these attributes (the cell ships an inert, variable-driven\n * `:host([data-range])` style; enterprise themes it via inheriting custom\n * properties).\n */\nexport interface CellDecoration {\n /**\n * Attributes to set on the decorated cell host. Use a `data-*` namespace\n * (e.g. `data-range`) so decoration never collides with the cell's built-in\n * reflected attributes (`active`, `editing`, `data-pinned`, …). A `null`/\n * `undefined` value removes the attribute. Attributes set on a previous\n * update that are absent here are removed automatically.\n */\n readonly attributes: Readonly<Record<string, string | null | undefined>>;\n}\n\n/**\n * Optional capability a feature module's controller may implement to decorate\n * individual cells with extra host attributes during the cell's own render\n * lifecycle — e.g. flagging the cells inside a selected range. Consulted per\n * cell via {@link StateController.decorateCell}; controllers that don't\n * implement it are skipped, so the community grid is unaffected.\n *\n * @remarks Unstable; part of `apex-grid/internal`.\n */\nexport interface CellDecorator<T extends object = any> {\n /** Return decoration for this cell, or `null` to leave it undecorated. */\n decorateCell(ctx: CellDecoratorContext<T>): CellDecoration | null;\n}\n\n/** Runtime type-guard for {@link CellDecorator}. */\nexport function isCellDecorator<T extends object>(\n controller: unknown\n): controller is CellDecorator<T> {\n return (\n typeof controller === 'object' &&\n controller !== null &&\n typeof (controller as CellDecorator<T>).decorateCell === 'function'\n );\n}\n\n/**\n * Optional capability a feature module's controller may implement to signal that\n * it provides a **column header menu** (opened from the header's kebab button).\n * The core header shows the kebab whenever a column is sortable or resizable, or\n * when a module reports `true` here, so a feature (e.g. the enterprise context\n * menu) can surface pin / hide / group / chart actions on any column. The module\n * owns the menu content itself (it opens on the `apex-grid-column-menu` event the\n * header dispatches). Inert for the community grid, which registers no modules.\n *\n * @remarks Unstable; part of `apex-grid/internal`.\n */\nexport interface ColumnMenuProvider {\n /** Whether the column-menu (kebab) button should be offered for columns. */\n providesColumnMenu(): boolean;\n}\n\n/** Runtime type-guard for {@link ColumnMenuProvider}. */\nexport function isColumnMenuProvider(controller: unknown): controller is ColumnMenuProvider {\n return (\n typeof controller === 'object' &&\n controller !== null &&\n typeof (controller as ColumnMenuProvider).providesColumnMenu === 'function'\n );\n}\n\n/** The kind of pointer interaction forwarded from a body cell. */\nexport type CellInteractionKind = 'down' | 'over' | 'up';\n\n/** A pointer interaction on a body cell, forwarded to feature modules. */\nexport interface CellInteraction<T extends object = any> {\n /** `down` on press, `over` while the pointer moves across cells, `up` on release. */\n readonly kind: CellInteractionKind;\n /** The cell's row data. */\n readonly row: T;\n /** Index of the row within the current page/view (matches `host.pageItems`). */\n readonly rowIndex: number;\n /** The cell's column configuration. */\n readonly column: ColumnConfiguration<T>;\n readonly shiftKey: boolean;\n readonly ctrlKey: boolean;\n readonly metaKey: boolean;\n /** The originating pointer event (e.g. to call `preventDefault`). */\n readonly originalEvent: PointerEvent;\n}\n\n/**\n * Optional capability a feature module's controller may implement to receive\n * pointer interactions on body cells (press / drag-over / release) — the seam a\n * range-selection feature uses to track a drag. The grid forwards interactions\n * only when at least one feature module is registered, so the community grid\n * pays nothing.\n *\n * @remarks Unstable; part of `apex-grid/internal`.\n */\nexport interface CellInteractionHandler<T extends object = any> {\n handleCellInteraction(interaction: CellInteraction<T>): void;\n}\n\n/** Runtime type-guard for {@link CellInteractionHandler}. */\nexport function isCellInteractionHandler<T extends object>(\n controller: unknown\n): controller is CellInteractionHandler<T> {\n return (\n typeof controller === 'object' &&\n controller !== null &&\n typeof (controller as CellInteractionHandler<T>).handleCellInteraction === 'function'\n );\n}\n\n/**\n * Optional capability a feature module's controller may implement so its state\n * participates in grid-state snapshots ({@link StateController.serializeModuleState}\n * / {@link StateController.restoreModuleState}, driven by `ApexGrid.getState` /\n * `ApexGrid.setState`). The grid collects each implementer's\n * {@link serializeState} output under the module's id and dispatches the\n * matching value back via {@link restoreState} on restore.\n *\n * Controllers that don't implement this are skipped, so the community grid\n * (which registers no modules) contributes nothing to a snapshot.\n *\n * @remarks Unstable; part of `apex-grid/internal`.\n */\nexport interface SerializableModule {\n /**\n * Return a JSON-serializable snapshot of this module's restorable state\n * (e.g. grouping's `groupBy` + aggregations). Exclude transient/derived\n * state and anything not JSON-safe (functions, DOM nodes).\n */\n serializeState(): unknown;\n /**\n * Restore module state from a value previously produced by\n * {@link serializeState}. Implementations should tolerate partial or\n * unexpected shapes (validate before applying) since the blob is opaque.\n */\n restoreState(data: unknown): void;\n}\n\n/** Runtime type-guard for {@link SerializableModule}. */\nexport function isSerializableModule(controller: unknown): controller is SerializableModule {\n return (\n typeof controller === 'object' &&\n controller !== null &&\n typeof (controller as SerializableModule).serializeState === 'function' &&\n typeof (controller as SerializableModule).restoreState === 'function'\n );\n}\n"]}
1
+ {"version":3,"file":"feature-module.js","sourceRoot":"","sources":["../../src/internal/feature-module.ts"],"names":[],"mappings":"AAsFA,MAAM,UAAU,gBAAgB,CAC9B,UAAmB;IAEnB,OAAO,CACL,OAAO,UAAU,KAAK,QAAQ;QAC9B,UAAU,KAAK,IAAI;QACnB,OAAQ,UAAgC,CAAC,WAAW,KAAK,UAAU,CACpE,CAAC;AACJ,CAAC;AA2CD,MAAM,UAAU,cAAc,CAC5B,UAAmB;IAEnB,OAAO,CACL,OAAO,UAAU,KAAK,QAAQ;QAC9B,UAAU,KAAK,IAAI;QACnB,OAAQ,UAA8B,CAAC,UAAU,KAAK,UAAU,CACjE,CAAC;AACJ,CAAC;AAyBD,MAAM,UAAU,iBAAiB,CAC/B,UAAmB;IAEnB,OAAO,CACL,OAAO,UAAU,KAAK,QAAQ;QAC9B,UAAU,KAAK,IAAI;QACnB,OAAQ,UAAiC,CAAC,WAAW,KAAK,UAAU,CACrE,CAAC;AACJ,CAAC;AAqBD,MAAM,UAAU,mBAAmB,CACjC,UAAmB;IAEnB,OAAO,CACL,OAAO,UAAU,KAAK,QAAQ;QAC9B,UAAU,KAAK,IAAI;QACnB,OAAQ,UAAmC,CAAC,aAAa,KAAK,UAAU;QACxE,OAAQ,UAAmC,CAAC,gBAAgB,KAAK,UAAU,CAC5E,CAAC;AACJ,CAAC;AAmDD,MAAM,UAAU,eAAe,CAC7B,UAAmB;IAEnB,OAAO,CACL,OAAO,UAAU,KAAK,QAAQ;QAC9B,UAAU,KAAK,IAAI;QACnB,OAAQ,UAA+B,CAAC,YAAY,KAAK,UAAU,CACpE,CAAC;AACJ,CAAC;AAmBD,MAAM,UAAU,oBAAoB,CAAC,UAAmB;IACtD,OAAO,CACL,OAAO,UAAU,KAAK,QAAQ;QAC9B,UAAU,KAAK,IAAI;QACnB,OAAQ,UAAiC,CAAC,kBAAkB,KAAK,UAAU,CAC5E,CAAC;AACJ,CAAC;AAoCD,MAAM,UAAU,wBAAwB,CACtC,UAAmB;IAEnB,OAAO,CACL,OAAO,UAAU,KAAK,QAAQ;QAC9B,UAAU,KAAK,IAAI;QACnB,OAAQ,UAAwC,CAAC,qBAAqB,KAAK,UAAU,CACtF,CAAC;AACJ,CAAC;AAgCD,MAAM,UAAU,oBAAoB,CAAC,UAAmB;IACtD,OAAO,CACL,OAAO,UAAU,KAAK,QAAQ;QAC9B,UAAU,KAAK,IAAI;QACnB,OAAQ,UAAiC,CAAC,cAAc,KAAK,UAAU;QACvE,OAAQ,UAAiC,CAAC,YAAY,KAAK,UAAU,CACtE,CAAC;AACJ,CAAC","sourcesContent":["import type { ReactiveController, TemplateResult } from 'lit';\n// Type-only import — erased at runtime, so this does not create a runtime\n// import cycle with `controllers/state.ts` (which imports the guards below).\nimport type { StateController } from '../controllers/state.js';\nimport type { ColumnConfiguration, GridHost } from './types.js';\n\n/**\n * Extension point for layering optional features onto the grid without baking\n * them into the community build.\n *\n * A feature module contributes a {@link ReactiveController} that is constructed\n * alongside the grid's built-in controllers (see {@link StateController}). It is\n * the seam used by the first-party `@apexcharts/grid-enterprise` package; it is\n * intentionally minimal and additive so the community `<apex-grid>` element is\n * unaffected (it registers zero modules).\n *\n * @remarks This API lives under the unstable `apex-grid/internal` entry point.\n */\nexport interface GridFeatureModule<T extends object = any> {\n /**\n * Stable identifier, e.g. `'grouping'`. Used to de-duplicate modules and to\n * look the controller up later via {@link StateController.module}.\n */\n readonly id: string;\n\n /**\n * Construct the feature's controller. The controller is expected to register\n * itself with the host (typically `host.addController(this)` in its\n * constructor), exactly like the built-in controllers.\n *\n * @param host The grid host the controller drives.\n * @param state The owning {@link StateController}, passed so a module can\n * coordinate with cross-cutting grid state (e.g. request a cell-decoration\n * refresh via {@link StateController.bumpDecoration} or look up a peer\n * module). Modules that don't need it may ignore the argument.\n */\n create(host: GridHost<T>, state: StateController<T>): ReactiveController;\n}\n\n/**\n * Optional capability a feature module's controller may implement to transform\n * the row set *after* the built-in pipeline (filter → sort → tree) and *before*\n * pagination is sliced at render time. Lets a module inject or reorder rows —\n * e.g. synthesized group-header rows — in the rendered `dataView`.\n *\n * Controllers that don't implement this are skipped, so the community grid\n * (which registers no modules) is unaffected.\n *\n * Transforms run in a fixed order set by {@link transformOrder}, not in the\n * order modules were registered, because they do not commute (a grand-total\n * row appended before grouping would itself be grouped).\n *\n * @remarks Unstable; part of `apex-grid/internal`.\n */\nexport interface RowTransformer<T extends object = any> {\n /** Return the transformed rows. Must not mutate the input array in place. */\n processRows(rows: ReadonlyArray<T>): T[];\n\n /**\n * Position of this transform in the module chain: lower runs first, ties keep\n * registration order, unset means `0`. First-party values: grouping `100`,\n * pivot `200`, grand-total row `1000`. Transforms that restructure the view\n * come first; rows that summarise the finished view come last. Leaving it\n * unset (`0`) runs the transform before grouping.\n */\n readonly transformOrder?: number;\n\n /**\n * Whether `row` is one this transform synthesized (a group header, a total\n * row) rather than a data record. Synthesized rows are kept out of row\n * selection and export (see {@link StateController.isDataRow}). Leave it\n * unset when the transform's output rows are the view's records (pivot).\n */\n isSyntheticRow?(row: T): boolean;\n\n /**\n * The data records a synthesized `row` stands for that the view does not\n * also hold, in view order: a collapsed group header's leaves. Return an\n * empty array when they are in the view too (an expanded group) or the row\n * stands for none (a total row). Lets Select all and a `'view'` export\n * cover collapsed groups (see {@link StateController.viewRecords}).\n */\n hiddenRecords?(row: T): ReadonlyArray<T>;\n}\n\n/** Runtime type-guard for {@link RowTransformer}. */\nexport function isRowTransformer<T extends object>(\n controller: unknown\n): controller is RowTransformer<T> {\n return (\n typeof controller === 'object' &&\n controller !== null &&\n typeof (controller as RowTransformer<T>).processRows === 'function'\n );\n}\n\n/** Per-row context handed to a {@link RowPresenter}. */\nexport interface RowPresenterContext<T extends object = any> {\n /** The grid's visible columns (post-reorder/visibility). */\n readonly columns: ReadonlyArray<ColumnConfiguration<T>>;\n /** Index of the row within the current page/view. */\n readonly rowIndex: number;\n}\n\n/**\n * A module-rendered, full-width row. Rendered inside a container that spans all\n * columns (`grid-column: 1 / -1`), mirroring the master-detail panel pattern.\n */\nexport interface PresentedRow {\n /** Template rendered inside the full-width container. */\n readonly content: TemplateResult;\n /** Optional 1-based `aria-level` applied to the row element. */\n readonly level?: number;\n /** Optional `aria-expanded` for the row element; omit for non-expandable rows. */\n readonly expanded?: boolean;\n /** Shadow part for the container element; defaults to `group-row`. */\n readonly part?: string;\n /**\n * Parts used inside {@link content} that the row should re-export, so\n * `apex-grid::part(name)` can style them. The container {@link part} is\n * exported without being listed here.\n */\n readonly exportParts?: ReadonlyArray<string>;\n}\n\n/**\n * Optional capability a feature module's controller may implement to render\n * specific rows as full-width custom content (e.g. group headers) instead of\n * the normal cell grid. Returns `null` for rows the module does not own.\n *\n * @remarks Unstable; part of `apex-grid/internal`.\n */\nexport interface RowPresenter<T extends object = any> {\n presentRow(row: T, ctx: RowPresenterContext<T>): PresentedRow | null;\n}\n\n/** Runtime type-guard for {@link RowPresenter}. */\nexport function isRowPresenter<T extends object>(\n controller: unknown\n): controller is RowPresenter<T> {\n return (\n typeof controller === 'object' &&\n controller !== null &&\n typeof (controller as RowPresenter<T>).presentRow === 'function'\n );\n}\n\n/** ARIA tree/expansion semantics a module can attach to a normal (cell-rendered) row. */\nexport interface RowAria {\n /** 1-based `aria-level` for the row element. */\n readonly level?: number;\n /** `aria-expanded` for the row element; omit for non-expandable rows. */\n readonly expanded?: boolean;\n}\n\n/**\n * Optional capability a module's controller may implement to attach ARIA\n * tree/expansion semantics (`aria-level` / `aria-expanded`) to rows that still\n * render as a normal cell grid — unlike {@link RowPresenter}, which replaces the\n * cells with full-width content. Used by pivoting's expandable row groups, whose\n * parent rows must keep showing per-column aggregate values. Returns `null` for\n * rows the module does not own.\n *\n * @remarks Unstable; part of `apex-grid/internal`.\n */\nexport interface RowAriaProvider<T extends object = any> {\n describeRow(row: T, ctx: RowPresenterContext<T>): RowAria | null;\n}\n\n/** Runtime type-guard for {@link RowAriaProvider}. */\nexport function isRowAriaProvider<T extends object>(\n controller: unknown\n): controller is RowAriaProvider<T> {\n return (\n typeof controller === 'object' &&\n controller !== null &&\n typeof (controller as RowAriaProvider<T>).describeRow === 'function'\n );\n}\n\n/**\n * Optional capability a module's controller may implement to label the\n * spreadsheet coordinate hints (`coordinateHints`): the row-number gutter and\n * the column letters. A module whose references address the data rather than\n * the view (the enterprise formula engine) answers with the coordinates a\n * reference reads, so the hints stay true through sort, filter, paging, hidden\n * columns and column moves. Return `null` for no label, or `undefined` to leave\n * the default (view position) in place.\n *\n * @remarks Unstable; part of `apex-grid/internal`.\n */\nexport interface CoordinateLabeler<T extends object = any> {\n /** The gutter label for a row. */\n rowCoordinate(row: T): string | null | undefined;\n /** The letter label for a column key. */\n columnCoordinate(key: string): string | null | undefined;\n}\n\n/** Runtime type-guard for {@link CoordinateLabeler}. */\nexport function isCoordinateLabeler<T extends object>(\n controller: unknown\n): controller is CoordinateLabeler<T> {\n return (\n typeof controller === 'object' &&\n controller !== null &&\n typeof (controller as CoordinateLabeler<T>).rowCoordinate === 'function' &&\n typeof (controller as CoordinateLabeler<T>).columnCoordinate === 'function'\n );\n}\n\n/** Per-cell context handed to a {@link CellDecorator}. */\nexport interface CellDecoratorContext<T extends object = any> {\n /** The row's backing data (or a synthesized row object). */\n readonly row: T;\n /**\n * Index of the row within the current page/view (matches `host.pageItems`),\n * or `-1` for a row rendered in a pinned band, which is outside that view.\n * Key pinned-row state by {@link row} instead.\n */\n readonly rowIndex: number;\n /** The cell's column configuration. */\n readonly column: ColumnConfiguration<T>;\n /** The cell's resolved value (`row[column.key]`). */\n readonly value: unknown;\n}\n\n/**\n * A set of host attributes a module asks the cell to reflect on itself. This is\n * the only thing a {@link CellDecorator} returns — styling is expressed by\n * keying CSS off these attributes (the cell ships an inert, variable-driven\n * `:host([data-range])` style; enterprise themes it via inheriting custom\n * properties).\n */\nexport interface CellDecoration {\n /**\n * Attributes to set on the decorated cell host. Use a `data-*` namespace\n * (e.g. `data-range`) so decoration never collides with the cell's built-in\n * reflected attributes (`active`, `editing`, `data-pinned`, …). A `null`/\n * `undefined` value removes the attribute. Attributes set on a previous\n * update that are absent here are removed automatically.\n */\n readonly attributes: Readonly<Record<string, string | null | undefined>>;\n}\n\n/**\n * Optional capability a feature module's controller may implement to decorate\n * individual cells with extra host attributes during the cell's own render\n * lifecycle — e.g. flagging the cells inside a selected range. Consulted per\n * cell via {@link StateController.decorateCell}; controllers that don't\n * implement it are skipped, so the community grid is unaffected.\n *\n * @remarks Unstable; part of `apex-grid/internal`.\n */\nexport interface CellDecorator<T extends object = any> {\n /** Return decoration for this cell, or `null` to leave it undecorated. */\n decorateCell(ctx: CellDecoratorContext<T>): CellDecoration | null;\n}\n\n/** Runtime type-guard for {@link CellDecorator}. */\nexport function isCellDecorator<T extends object>(\n controller: unknown\n): controller is CellDecorator<T> {\n return (\n typeof controller === 'object' &&\n controller !== null &&\n typeof (controller as CellDecorator<T>).decorateCell === 'function'\n );\n}\n\n/**\n * Optional capability a feature module's controller may implement to signal that\n * it provides a **column header menu** (opened from the header's kebab button).\n * The core header shows the kebab whenever a column is sortable or resizable, or\n * when a module reports `true` here, so a feature (e.g. the enterprise context\n * menu) can surface pin / hide / group / chart actions on any column. The module\n * owns the menu content itself (it opens on the `apex-grid-column-menu` event the\n * header dispatches). Inert for the community grid, which registers no modules.\n *\n * @remarks Unstable; part of `apex-grid/internal`.\n */\nexport interface ColumnMenuProvider {\n /** Whether the column-menu (kebab) button should be offered for columns. */\n providesColumnMenu(): boolean;\n}\n\n/** Runtime type-guard for {@link ColumnMenuProvider}. */\nexport function isColumnMenuProvider(controller: unknown): controller is ColumnMenuProvider {\n return (\n typeof controller === 'object' &&\n controller !== null &&\n typeof (controller as ColumnMenuProvider).providesColumnMenu === 'function'\n );\n}\n\n/** The kind of pointer interaction forwarded from a body cell. */\nexport type CellInteractionKind = 'down' | 'over' | 'up';\n\n/** A pointer interaction on a body cell, forwarded to feature modules. */\nexport interface CellInteraction<T extends object = any> {\n /** `down` on press, `over` while the pointer moves across cells, `up` on release. */\n readonly kind: CellInteractionKind;\n /** The cell's row data. */\n readonly row: T;\n /** Index of the row within the current page/view (matches `host.pageItems`). */\n readonly rowIndex: number;\n /** The cell's column configuration. */\n readonly column: ColumnConfiguration<T>;\n readonly shiftKey: boolean;\n readonly ctrlKey: boolean;\n readonly metaKey: boolean;\n /** The originating pointer event (e.g. to call `preventDefault`). */\n readonly originalEvent: PointerEvent;\n}\n\n/**\n * Optional capability a feature module's controller may implement to receive\n * pointer interactions on body cells (press / drag-over / release) — the seam a\n * range-selection feature uses to track a drag. The grid forwards interactions\n * only when at least one feature module is registered, so the community grid\n * pays nothing.\n *\n * @remarks Unstable; part of `apex-grid/internal`.\n */\nexport interface CellInteractionHandler<T extends object = any> {\n handleCellInteraction(interaction: CellInteraction<T>): void;\n}\n\n/** Runtime type-guard for {@link CellInteractionHandler}. */\nexport function isCellInteractionHandler<T extends object>(\n controller: unknown\n): controller is CellInteractionHandler<T> {\n return (\n typeof controller === 'object' &&\n controller !== null &&\n typeof (controller as CellInteractionHandler<T>).handleCellInteraction === 'function'\n );\n}\n\n/**\n * Optional capability a feature module's controller may implement so its state\n * participates in grid-state snapshots ({@link StateController.serializeModuleState}\n * / {@link StateController.restoreModuleState}, driven by `ApexGrid.getState` /\n * `ApexGrid.setState`). The grid collects each implementer's\n * {@link serializeState} output under the module's id and dispatches the\n * matching value back via {@link restoreState} on restore. Ids with no\n * implementer are reported back by {@link StateController.restoreModuleState}.\n *\n * Controllers that don't implement this are skipped, so the community grid\n * (which registers no modules) contributes nothing to a snapshot.\n *\n * @remarks Unstable; part of `apex-grid/internal`.\n */\nexport interface SerializableModule {\n /**\n * Return a JSON-serializable snapshot of this module's restorable state\n * (e.g. the options the feature was configured with). Exclude transient/derived\n * state and anything not JSON-safe (functions, DOM nodes).\n */\n serializeState(): unknown;\n /**\n * Restore module state from a value previously produced by\n * {@link serializeState}. Implementations should tolerate partial or\n * unexpected shapes (validate before applying) since the blob is opaque.\n */\n restoreState(data: unknown): void;\n}\n\n/** Runtime type-guard for {@link SerializableModule}. */\nexport function isSerializableModule(controller: unknown): controller is SerializableModule {\n return (\n typeof controller === 'object' &&\n controller !== null &&\n typeof (controller as SerializableModule).serializeState === 'function' &&\n typeof (controller as SerializableModule).restoreState === 'function'\n );\n}\n"]}
@@ -11,14 +11,16 @@
11
11
  * minor releases. Do not depend on them from third-party code.
12
12
  */
13
13
  export { ApexGrid } from '../components/grid.js';
14
+ export { viewIndexer } from '../controllers/history.js';
14
15
  export { gridStateContext, StateController } from '../controllers/state.js';
16
+ export { clampRating, commitDateInSameShape, getColumnTypeRenderer, getRatingMax, getSelectOptions, parseDate, } from './column-types.js';
15
17
  export { PIPELINE } from './constants.js';
16
18
  export type { ExportCellValue, ExportFormat, ExportOptions } from './export.js';
17
- export { downloadBlob, getColumnLabel, resolveExportColumns, resolveExportRows, resolveExportValue, } from './export.js';
18
- export type { CellDecoration, CellDecorator, CellDecoratorContext, CellInteraction, CellInteractionHandler, CellInteractionKind, ColumnMenuProvider, GridFeatureModule, PresentedRow, RowAria, RowAriaProvider, RowPresenter, RowPresenterContext, RowTransformer, SerializableModule, } from './feature-module.js';
19
- export { isCellDecorator, isCellInteractionHandler, isColumnMenuProvider, isRowAriaProvider, isRowPresenter, isRowTransformer, isSerializableModule, } from './feature-module.js';
19
+ export { downloadBlob, escapeCSVCell, escapeFormula, getColumnLabel, resolveExportColumns, resolveExportRows, resolveExportValue, } from './export.js';
20
+ export type { CellDecoration, CellDecorator, CellDecoratorContext, CellInteraction, CellInteractionHandler, CellInteractionKind, ColumnMenuProvider, CoordinateLabeler, GridFeatureModule, PresentedRow, RowAria, RowAriaProvider, RowPresenter, RowPresenterContext, RowTransformer, SerializableModule, } from './feature-module.js';
21
+ export { isCellDecorator, isCellInteractionHandler, isColumnMenuProvider, isCoordinateLabeler, isRowAriaProvider, isRowPresenter, isRowTransformer, isSerializableModule, } from './feature-module.js';
20
22
  export { registerComponent } from './register.js';
21
23
  export type { ColumnLayoutState, FilterStateSnapshot, GetStateOptions, GridState, RowRef, SetStateOptions, SortStateSnapshot, } from './state-snapshot.js';
22
- export { applyColumnLayout, deserializeFilter, resolveRowRefs, serializeColumnLayout, serializeFilter, serializeRowRefs, serializeSort, } from './state-snapshot.js';
24
+ export { applyColumnLayout, deserializeFilter, mergeColumnLayout, registerFilterOperand, resolveFilterOperand, resolveRowRefs, serializeColumnLayout, serializeFilter, serializeRowRefs, serializeSort, } from './state-snapshot.js';
23
25
  export type { ActiveNode, ColumnConfiguration, GridHost, Keys, ToolbarAction } from './types.js';
24
26
  export { getDisplayColumns } from './utils.js';
package/internal/index.js CHANGED
@@ -1,9 +1,11 @@
1
1
  export { ApexGrid } from '../components/grid.js';
2
+ export { viewIndexer } from '../controllers/history.js';
2
3
  export { gridStateContext, StateController } from '../controllers/state.js';
4
+ export { clampRating, commitDateInSameShape, getColumnTypeRenderer, getRatingMax, getSelectOptions, parseDate, } from './column-types.js';
3
5
  export { PIPELINE } from './constants.js';
4
- export { downloadBlob, getColumnLabel, resolveExportColumns, resolveExportRows, resolveExportValue, } from './export.js';
5
- export { isCellDecorator, isCellInteractionHandler, isColumnMenuProvider, isRowAriaProvider, isRowPresenter, isRowTransformer, isSerializableModule, } from './feature-module.js';
6
+ export { downloadBlob, escapeCSVCell, escapeFormula, getColumnLabel, resolveExportColumns, resolveExportRows, resolveExportValue, } from './export.js';
7
+ export { isCellDecorator, isCellInteractionHandler, isColumnMenuProvider, isCoordinateLabeler, isRowAriaProvider, isRowPresenter, isRowTransformer, isSerializableModule, } from './feature-module.js';
6
8
  export { registerComponent } from './register.js';
7
- export { applyColumnLayout, deserializeFilter, resolveRowRefs, serializeColumnLayout, serializeFilter, serializeRowRefs, serializeSort, } from './state-snapshot.js';
9
+ export { applyColumnLayout, deserializeFilter, mergeColumnLayout, registerFilterOperand, resolveFilterOperand, resolveRowRefs, serializeColumnLayout, serializeFilter, serializeRowRefs, serializeSort, } from './state-snapshot.js';
8
10
  export { getDisplayColumns } from './utils.js';
9
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/internal/index.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,OAAO,EACL,YAAY,EACZ,cAAc,EACd,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAkBrB,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,oBAAoB,EACpB,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAUlD,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,aAAa,GACd,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC","sourcesContent":["/**\n * Unstable, first-party extension surface for `apex-grid`.\n *\n * This entry point (`apex-grid/internal`) exposes the internals that the\n * first-party `@apexcharts/grid-enterprise` package builds on: the grid class,\n * its state controller, the component registration helper, and the feature\n * module contract. It is intentionally separate from the public `.` barrel.\n *\n * @remarks\n * No semver guarantees are made for these exports. They may change between\n * minor releases. Do not depend on them from third-party code.\n */\nexport { ApexGrid } from '../components/grid.js';\nexport { gridStateContext, StateController } from '../controllers/state.js';\nexport { PIPELINE } from './constants.js';\nexport type { ExportCellValue, ExportFormat, ExportOptions } from './export.js';\nexport {\n downloadBlob,\n getColumnLabel,\n resolveExportColumns,\n resolveExportRows,\n resolveExportValue,\n} from './export.js';\nexport type {\n CellDecoration,\n CellDecorator,\n CellDecoratorContext,\n CellInteraction,\n CellInteractionHandler,\n CellInteractionKind,\n ColumnMenuProvider,\n GridFeatureModule,\n PresentedRow,\n RowAria,\n RowAriaProvider,\n RowPresenter,\n RowPresenterContext,\n RowTransformer,\n SerializableModule,\n} from './feature-module.js';\nexport {\n isCellDecorator,\n isCellInteractionHandler,\n isColumnMenuProvider,\n isRowAriaProvider,\n isRowPresenter,\n isRowTransformer,\n isSerializableModule,\n} from './feature-module.js';\nexport { registerComponent } from './register.js';\nexport type {\n ColumnLayoutState,\n FilterStateSnapshot,\n GetStateOptions,\n GridState,\n RowRef,\n SetStateOptions,\n SortStateSnapshot,\n} from './state-snapshot.js';\nexport {\n applyColumnLayout,\n deserializeFilter,\n resolveRowRefs,\n serializeColumnLayout,\n serializeFilter,\n serializeRowRefs,\n serializeSort,\n} from './state-snapshot.js';\nexport type { ActiveNode, ColumnConfiguration, GridHost, Keys, ToolbarAction } from './types.js';\nexport { getDisplayColumns } from './utils.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/internal/index.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,qBAAqB,EACrB,YAAY,EACZ,gBAAgB,EAChB,SAAS,GACV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,OAAO,EACL,YAAY,EACZ,aAAa,EACb,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAmBrB,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAUlD,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,aAAa,GACd,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC","sourcesContent":["/**\n * Unstable, first-party extension surface for `apex-grid`.\n *\n * This entry point (`apex-grid/internal`) exposes the internals that the\n * first-party `@apexcharts/grid-enterprise` package builds on: the grid class,\n * its state controller, the component registration helper, and the feature\n * module contract. It is intentionally separate from the public `.` barrel.\n *\n * @remarks\n * No semver guarantees are made for these exports. They may change between\n * minor releases. Do not depend on them from third-party code.\n */\nexport { ApexGrid } from '../components/grid.js';\nexport { viewIndexer } from '../controllers/history.js';\nexport { gridStateContext, StateController } from '../controllers/state.js';\nexport {\n clampRating,\n commitDateInSameShape,\n getColumnTypeRenderer,\n getRatingMax,\n getSelectOptions,\n parseDate,\n} from './column-types.js';\nexport { PIPELINE } from './constants.js';\nexport type { ExportCellValue, ExportFormat, ExportOptions } from './export.js';\nexport {\n downloadBlob,\n escapeCSVCell,\n escapeFormula,\n getColumnLabel,\n resolveExportColumns,\n resolveExportRows,\n resolveExportValue,\n} from './export.js';\nexport type {\n CellDecoration,\n CellDecorator,\n CellDecoratorContext,\n CellInteraction,\n CellInteractionHandler,\n CellInteractionKind,\n ColumnMenuProvider,\n CoordinateLabeler,\n GridFeatureModule,\n PresentedRow,\n RowAria,\n RowAriaProvider,\n RowPresenter,\n RowPresenterContext,\n RowTransformer,\n SerializableModule,\n} from './feature-module.js';\nexport {\n isCellDecorator,\n isCellInteractionHandler,\n isColumnMenuProvider,\n isCoordinateLabeler,\n isRowAriaProvider,\n isRowPresenter,\n isRowTransformer,\n isSerializableModule,\n} from './feature-module.js';\nexport { registerComponent } from './register.js';\nexport type {\n ColumnLayoutState,\n FilterStateSnapshot,\n GetStateOptions,\n GridState,\n RowRef,\n SetStateOptions,\n SortStateSnapshot,\n} from './state-snapshot.js';\nexport {\n applyColumnLayout,\n deserializeFilter,\n mergeColumnLayout,\n registerFilterOperand,\n resolveFilterOperand,\n resolveRowRefs,\n serializeColumnLayout,\n serializeFilter,\n serializeRowRefs,\n serializeSort,\n} from './state-snapshot.js';\nexport type { ActiveNode, ColumnConfiguration, GridHost, Keys, ToolbarAction } from './types.js';\nexport { getDisplayColumns } from './utils.js';\n"]}
@@ -1 +1,8 @@
1
- export declare function normalizeCase(string: string, caseSensitive?: boolean): string;
1
+ /**
2
+ * The text a string filter operand compares: strings as they are, `null` and
3
+ * `undefined` as `''`, any other value (number, Date, array) through `String`,
4
+ * case-folded unless `caseSensitive`. Total, so a cell of any type can be
5
+ * matched by a string condition without throwing. A string cell allocates
6
+ * nothing beyond the case fold.
7
+ */
8
+ export declare function normalizeCase(value: unknown, caseSensitive?: boolean): string;
@@ -1,4 +1,5 @@
1
- export function normalizeCase(string, caseSensitive) {
2
- return caseSensitive ? string : string.toLowerCase();
1
+ export function normalizeCase(value, caseSensitive) {
2
+ const text = typeof value === 'string' ? value : value == null ? '' : String(value);
3
+ return caseSensitive ? text : text.toLowerCase();
3
4
  }
4
5
  //# sourceMappingURL=normalize-case.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"normalize-case.js","sourceRoot":"","sources":["../../src/internal/normalize-case.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,aAAa,CAAC,MAAc,EAAE,aAAuB;IACnE,OAAO,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;AACvD,CAAC","sourcesContent":["export function normalizeCase(string: string, caseSensitive?: boolean) {\n return caseSensitive ? string : string.toLowerCase();\n}\n"]}
1
+ {"version":3,"file":"normalize-case.js","sourceRoot":"","sources":["../../src/internal/normalize-case.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,aAAa,CAAC,KAAc,EAAE,aAAuB;IACnE,MAAM,IAAI,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACpF,OAAO,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;AACnD,CAAC","sourcesContent":["/**\n * The text a string filter operand compares: strings as they are, `null` and\n * `undefined` as `''`, any other value (number, Date, array) through `String`,\n * case-folded unless `caseSensitive`. Total, so a cell of any type can be\n * matched by a string condition without throwing. A string cell allocates\n * nothing beyond the case fold.\n */\nexport function normalizeCase(value: unknown, caseSensitive?: boolean): string {\n const text = typeof value === 'string' ? value : value == null ? '' : String(value);\n return caseSensitive ? text : text.toLowerCase();\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { FilterCriteria, FilterExpression } from '../operations/filter/types.js';
1
+ import type { FilterCriteria, FilterExpression, FilterOperation } from '../operations/filter/types.js';
2
2
  import type { SortExpression, SortingDirection } from '../operations/sort/types.js';
3
3
  import type { ColumnConfiguration, PinPosition } from './types.js';
4
4
  /**
@@ -105,6 +105,27 @@ export interface SetStateOptions<T extends object> {
105
105
  */
106
106
  strict?: boolean;
107
107
  }
108
+ /**
109
+ * The `setState` slices in the order `setState` applies them, each mapped to
110
+ * the {@link GridState} keys it restores (`tree` restores both tree keys).
111
+ */
112
+ export declare const SET_STATE_SLICE_KEYS: {
113
+ readonly columns: readonly ["columns"];
114
+ readonly sort: readonly ["sort"];
115
+ readonly filter: readonly ["filter"];
116
+ readonly quickFilter: readonly ["quickFilter"];
117
+ readonly modules: readonly ["modules"];
118
+ readonly rowOrder: readonly ["rowOrder"];
119
+ readonly tree: readonly ["treeExpanded", "treeExpandedKeys"];
120
+ readonly expansion: readonly ["expansion"];
121
+ readonly rowPinning: readonly ["rowPinning"];
122
+ readonly selection: readonly ["selection"];
123
+ readonly pagination: readonly ["pagination"];
124
+ };
125
+ /** A `setState` slice name, as reported in {@link SetStateResult}. */
126
+ export type SetStateSlice = keyof typeof SET_STATE_SLICE_KEYS;
127
+ /** Every slice name, in apply order. */
128
+ export declare const SET_STATE_SLICES: SetStateSlice[];
108
129
  /**
109
130
  * The outcome of an {@link SetStateOptions} apply. `setState` never throws on
110
131
  * malformed input (unless `strict`): it applies recognized slices, skips the
@@ -112,9 +133,12 @@ export interface SetStateOptions<T extends object> {
112
133
  * clamped, or unresolved.
113
134
  */
114
135
  export interface SetStateResult {
115
- /** Slice names that were present and applied (e.g. `'sort'`, `'columns'`). */
136
+ /**
137
+ * Slice names that were present and applied (e.g. `'sort'`, `'columns'`); see
138
+ * {@link SET_STATE_SLICE_KEYS} for the {@link GridState} keys of each.
139
+ */
116
140
  applied: string[];
117
- /** Known slice names that were absent (left untouched). */
141
+ /** Known slice names ({@link SetStateSlice}) that were absent or not applied. */
118
142
  skipped: string[];
119
143
  /** What was dropped / clamped / unresolved, one message each. */
120
144
  warnings: string[];
@@ -125,28 +149,56 @@ export declare function serializeColumnLayout<T extends object>(columns: Readonl
125
149
  * Return a new columns array reordered to match `layout`, with width / pinning /
126
150
  * visibility applied. Column objects are shallow-cloned so non-layout
127
151
  * properties (templates, comparers, type config) are preserved. Columns absent
128
- * from `layout` are appended unchanged; layout entries with no matching column
129
- * are ignored.
152
+ * from `layout` are appended unchanged; layout entries with no matching column,
153
+ * a repeated key, or no string `key` are ignored. This is the full-layout
154
+ * restorer: to change a few fields in place, overlay them with
155
+ * {@link mergeColumnLayout} first.
130
156
  */
131
157
  export declare function applyColumnLayout<T extends object>(columns: ReadonlyArray<ColumnConfiguration<T>>, layout: ReadonlyArray<ColumnLayoutState>): ColumnConfiguration<T>[];
158
+ /**
159
+ * Overlay a partial layout onto `current` (a full {@link serializeColumnLayout}
160
+ * result): each `partial` entry changes only the fields it defines on the
161
+ * column with the same key, and the order of `current` is kept. `null` clears
162
+ * a field (e.g. `pinned: null` unpins). Entries for unknown keys are ignored.
163
+ * Returns a new full layout to hand to `setState({ columns })`.
164
+ */
165
+ export declare function mergeColumnLayout(current: ReadonlyArray<ColumnLayoutState>, partial: ReadonlyArray<Partial<Record<keyof ColumnLayoutState, unknown>>>): ColumnLayoutState[];
132
166
  export declare function serializeSort<T extends object>(expressions: ReadonlyArray<SortExpression<T>>): SortStateSnapshot[];
133
167
  export declare function serializeFilter<T extends object>(expressions: ReadonlyArray<FilterExpression<T>>): FilterStateSnapshot[];
168
+ /**
169
+ * Make `operation` resolvable by its `name` on every column, after the
170
+ * column type's own operands. For first-party filter UIs (the enterprise set
171
+ * filter) whose expressions must survive `setState(getState())`. The
172
+ * operation must be stateless: everything it needs travels in `searchTerm`.
173
+ */
174
+ export declare function registerFilterOperand(operation: FilterOperation<unknown>): void;
175
+ /**
176
+ * Resolve a filter operand name (e.g. `'contains'`) to the live
177
+ * {@link FilterOperation} of `column`'s type, else to one registered with
178
+ * {@link registerFilterOperand}. Only the tables' own names resolve, so
179
+ * `'toString'` or `'constructor'` never pass as operands.
180
+ */
181
+ export declare function resolveFilterOperand<T extends object>(column: ColumnConfiguration<T>, name: unknown): FilterOperation<unknown> | undefined;
134
182
  /**
135
183
  * Rebuild filter expressions from a snapshot, resolving each operand name back
136
184
  * to a live {@link FilterOperation} via the target column's operand set.
137
- * Entries whose column or operand can't be resolved are dropped; `onDrop` (when
138
- * given) is called with the dropped entry and a reason.
185
+ * Entries that are not keyed objects, whose column or operand can't be
186
+ * resolved, or whose binary operand has no `searchTerm` are dropped; `onDrop`
187
+ * (when given) is called with the dropped entry and a reason.
139
188
  */
140
189
  export declare function deserializeFilter<T extends object>(snapshots: ReadonlyArray<FilterStateSnapshot>, getColumn: (key: string) => ColumnConfiguration<T> | undefined, onDrop?: (snapshot: FilterStateSnapshot, reason: string) => void): FilterExpression<T>[];
141
190
  /**
142
191
  * Serialize a set of rows to {@link RowRef}s. With a `rowId` resolver each row
143
192
  * is captured by id; otherwise by its positional index in `universe`
144
193
  * (`ApexGrid.data`). Rows not found in `universe` (index mode) are skipped.
194
+ * Index mode builds one position map per call, so it is linear in the sizes
195
+ * of `rows` and `universe` rather than their product.
145
196
  */
146
197
  export declare function serializeRowRefs<T extends object>(rows: ReadonlyArray<T>, universe: ReadonlyArray<T>, rowId?: (row: T) => string | number): RowRef[];
147
198
  /**
148
199
  * Resolve {@link RowRef}s back to rows in `universe`. `id` refs require a
149
200
  * `rowId` resolver (otherwise they're skipped); `index` refs resolve
150
- * positionally. Unresolvable refs are dropped.
201
+ * positionally and must be non-negative integers. Refs that are not objects,
202
+ * and unresolvable refs, are dropped.
151
203
  */
152
204
  export declare function resolveRowRefs<T extends object>(refs: ReadonlyArray<RowRef>, universe: ReadonlyArray<T>, rowId?: (row: T) => string | number): T[];
@@ -1,4 +1,23 @@
1
1
  import { getFilterOperandsFor } from './utils.js';
2
+ export const SET_STATE_SLICE_KEYS = {
3
+ columns: ['columns'],
4
+ sort: ['sort'],
5
+ filter: ['filter'],
6
+ quickFilter: ['quickFilter'],
7
+ modules: ['modules'],
8
+ rowOrder: ['rowOrder'],
9
+ tree: ['treeExpanded', 'treeExpandedKeys'],
10
+ expansion: ['expansion'],
11
+ rowPinning: ['rowPinning'],
12
+ selection: ['selection'],
13
+ pagination: ['pagination'],
14
+ };
15
+ export const SET_STATE_SLICES = Object.keys(SET_STATE_SLICE_KEYS);
16
+ const _everyKeyHasASlice = true;
17
+ void _everyKeyHasASlice;
18
+ function isKeyed(value) {
19
+ return typeof value === 'object' && value !== null && typeof value.key === 'string';
20
+ }
2
21
  export function serializeColumnLayout(columns) {
3
22
  return columns.map((column) => ({
4
23
  key: String(column.key),
@@ -14,6 +33,8 @@ export function applyColumnLayout(columns, layout) {
14
33
  const used = new Set();
15
34
  const ordered = [];
16
35
  for (const entry of layout) {
36
+ if (!isKeyed(entry) || used.has(entry.key))
37
+ continue;
17
38
  const column = byKey.get(entry.key);
18
39
  if (!column)
19
40
  continue;
@@ -31,6 +52,24 @@ export function applyColumnLayout(columns, layout) {
31
52
  }
32
53
  return ordered;
33
54
  }
55
+ export function mergeColumnLayout(current, partial) {
56
+ const patches = new Map();
57
+ for (const entry of partial) {
58
+ if (isKeyed(entry))
59
+ patches.set(entry.key, entry);
60
+ }
61
+ return current.map((column) => {
62
+ const next = { ...column };
63
+ const patch = patches.get(column.key);
64
+ if (patch) {
65
+ for (const field of ['width', 'pinned', 'hidden']) {
66
+ if (patch[field] !== undefined)
67
+ next[field] = patch[field] ?? undefined;
68
+ }
69
+ }
70
+ return next;
71
+ });
72
+ }
34
73
  export function serializeSort(expressions) {
35
74
  return expressions.map((expression) => ({
36
75
  key: String(expression.key),
@@ -38,53 +77,80 @@ export function serializeSort(expressions) {
38
77
  caseSensitive: expression.caseSensitive,
39
78
  }));
40
79
  }
80
+ function copyTerm(term) {
81
+ return Array.isArray(term) ? [...term] : term;
82
+ }
41
83
  export function serializeFilter(expressions) {
42
84
  return expressions.map((expression) => {
43
85
  const condition = expression.condition;
44
86
  return {
45
87
  key: String(expression.key),
46
88
  operand: typeof condition === 'string' ? condition : condition.name,
47
- searchTerm: expression.searchTerm,
89
+ searchTerm: copyTerm(expression.searchTerm),
48
90
  criteria: expression.criteria,
49
91
  caseSensitive: expression.caseSensitive,
50
92
  };
51
93
  });
52
94
  }
95
+ const registeredOperands = new Map();
96
+ export function registerFilterOperand(operation) {
97
+ registeredOperands.set(operation.name, operation);
98
+ }
99
+ export function resolveFilterOperand(column, name) {
100
+ if (typeof name !== 'string')
101
+ return undefined;
102
+ const operands = getFilterOperandsFor(column);
103
+ return Object.hasOwn(operands, name) ? operands[name] : registeredOperands.get(name);
104
+ }
53
105
  export function deserializeFilter(snapshots, getColumn, onDrop) {
54
106
  const expressions = [];
55
107
  for (const snapshot of snapshots) {
108
+ if (!isKeyed(snapshot)) {
109
+ onDrop?.({ key: String(snapshot) }, 'not a filter entry');
110
+ continue;
111
+ }
56
112
  const column = getColumn(snapshot.key);
57
113
  if (!column) {
58
114
  onDrop?.(snapshot, 'unknown column');
59
115
  continue;
60
116
  }
61
- const operands = getFilterOperandsFor(column);
62
- const condition = operands[snapshot.operand];
117
+ const condition = resolveFilterOperand(column, snapshot.operand);
63
118
  if (!condition) {
64
119
  onDrop?.(snapshot, `unknown operand "${snapshot.operand}"`);
65
120
  continue;
66
121
  }
67
- expressions.push({
68
- key: snapshot.key,
69
- condition,
70
- searchTerm: snapshot.searchTerm,
71
- criteria: snapshot.criteria,
72
- caseSensitive: snapshot.caseSensitive,
73
- });
122
+ if (!condition.unary && (snapshot.searchTerm === undefined || snapshot.searchTerm === null)) {
123
+ onDrop?.(snapshot, `operand "${snapshot.operand}" needs a searchTerm`);
124
+ continue;
125
+ }
126
+ const expression = { key: snapshot.key, condition };
127
+ if (snapshot.searchTerm !== undefined)
128
+ expression.searchTerm = copyTerm(snapshot.searchTerm);
129
+ if (snapshot.criteria !== undefined)
130
+ expression.criteria = snapshot.criteria;
131
+ if (snapshot.caseSensitive !== undefined)
132
+ expression.caseSensitive = snapshot.caseSensitive;
133
+ expressions.push(expression);
74
134
  }
75
135
  return expressions;
76
136
  }
77
137
  export function serializeRowRefs(rows, universe, rowId) {
138
+ if (rowId)
139
+ return rows.map((row) => ({ id: rowId(row) }));
140
+ if (rows.length < 2) {
141
+ const index = rows.length ? universe.indexOf(rows[0]) : -1;
142
+ return index === -1 ? [] : [{ index }];
143
+ }
144
+ const positions = new Map();
145
+ universe.forEach((row, index) => {
146
+ if (!positions.has(row))
147
+ positions.set(row, index);
148
+ });
78
149
  const refs = [];
79
150
  for (const row of rows) {
80
- if (rowId) {
81
- refs.push({ id: rowId(row) });
82
- }
83
- else {
84
- const index = universe.indexOf(row);
85
- if (index !== -1)
86
- refs.push({ index });
87
- }
151
+ const index = positions.get(row);
152
+ if (index !== undefined)
153
+ refs.push({ index });
88
154
  }
89
155
  return refs;
90
156
  }
@@ -92,6 +158,8 @@ export function resolveRowRefs(refs, universe, rowId) {
92
158
  const out = [];
93
159
  let byId = null;
94
160
  for (const ref of refs) {
161
+ if (typeof ref !== 'object' || ref === null)
162
+ continue;
95
163
  if ('id' in ref) {
96
164
  if (!rowId)
97
165
  continue;
@@ -104,7 +172,7 @@ export function resolveRowRefs(refs, universe, rowId) {
104
172
  if (row)
105
173
  out.push(row);
106
174
  }
107
- else {
175
+ else if (Number.isInteger(ref.index) && ref.index >= 0) {
108
176
  const row = universe[ref.index];
109
177
  if (row)
110
178
  out.push(row);