apex-grid 3.6.0 → 4.0.0-beta.2

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 (151) hide show
  1. package/CHANGELOG.md +301 -0
  2. package/LICENSE.md +2 -1
  3. package/README.md +117 -18
  4. package/components/cell.d.ts +13 -1
  5. package/components/cell.js +28 -13
  6. package/components/cell.js.map +1 -1
  7. package/components/grid.d.ts +175 -28
  8. package/components/grid.js +183 -63
  9. package/components/grid.js.map +1 -1
  10. package/components/header-row.d.ts +0 -6
  11. package/components/header-row.js +1 -19
  12. package/components/header-row.js.map +1 -1
  13. package/components/header.js +39 -2
  14. package/components/header.js.map +1 -1
  15. package/components/paginator.d.ts +20 -5
  16. package/components/paginator.js +54 -17
  17. package/components/paginator.js.map +1 -1
  18. package/components/row.js +149 -40
  19. package/components/row.js.map +1 -1
  20. package/components/toolbar.d.ts +5 -1
  21. package/components/toolbar.js +4 -1
  22. package/components/toolbar.js.map +1 -1
  23. package/components/virtualize-rows.d.ts +38 -0
  24. package/components/virtualize-rows.js +286 -0
  25. package/components/virtualize-rows.js.map +1 -0
  26. package/components/virtualizer.d.ts +71 -1
  27. package/components/virtualizer.js +105 -29
  28. package/components/virtualizer.js.map +1 -1
  29. package/controllers/dom.d.ts +10 -3
  30. package/controllers/dom.js +5 -5
  31. package/controllers/dom.js.map +1 -1
  32. package/controllers/navigation.d.ts +63 -0
  33. package/controllers/navigation.js +295 -21
  34. package/controllers/navigation.js.map +1 -1
  35. package/controllers/pagination.d.ts +7 -6
  36. package/controllers/pagination.js +27 -28
  37. package/controllers/pagination.js.map +1 -1
  38. package/controllers/row-pin.d.ts +6 -0
  39. package/controllers/row-pin.js +8 -2
  40. package/controllers/row-pin.js.map +1 -1
  41. package/controllers/row-reorder.d.ts +12 -4
  42. package/controllers/row-reorder.js +20 -6
  43. package/controllers/row-reorder.js.map +1 -1
  44. package/controllers/selection.d.ts +32 -10
  45. package/controllers/selection.js +33 -10
  46. package/controllers/selection.js.map +1 -1
  47. package/controllers/sort.js +2 -3
  48. package/controllers/sort.js.map +1 -1
  49. package/controllers/state.d.ts +18 -5
  50. package/controllers/state.js +53 -46
  51. package/controllers/state.js.map +1 -1
  52. package/custom-elements.json +1762 -339
  53. package/docs/assets/hierarchy.js +1 -1
  54. package/docs/assets/search.js +1 -1
  55. package/docs/classes/ApexGrid.html +166 -66
  56. package/docs/classes/ApexGridPaginator.html +39 -8
  57. package/docs/classes/ApexGridToolbar.html +9 -5
  58. package/docs/functions/localize.html +2 -2
  59. package/docs/hierarchy.html +1 -1
  60. package/docs/index.html +123 -20
  61. package/docs/interfaces/ApexPageChangedEvent.html +3 -1
  62. package/docs/interfaces/BaseApexCellContext.html +18 -5
  63. package/docs/interfaces/BaseApexEditorContext.html +20 -5
  64. package/docs/interfaces/BaseFilterExpression.html +1 -1
  65. package/docs/interfaces/CSVExportOptions.html +1 -1
  66. package/docs/interfaces/DataPipelineConfiguration.html +4 -9
  67. package/docs/interfaces/ExportOptions.html +1 -1
  68. package/docs/interfaces/GridSelectionConfiguration.html +19 -4
  69. package/docs/interfaces/PaginationConfiguration.html +11 -4
  70. package/docs/interfaces/PaginationState.html +4 -2
  71. package/docs/media/_token-values.scss +42 -16
  72. package/docs/media/package.json +12 -4
  73. package/docs/types/DataPipelineParams.html +2 -2
  74. package/docs/types/PaginationMode.html +5 -4
  75. package/docs/variables/EN_LOCALE.html +2 -2
  76. package/i18n/en.d.ts +2 -0
  77. package/i18n/en.js +2 -0
  78. package/i18n/en.js.map +1 -1
  79. package/i18n/es.js +2 -0
  80. package/i18n/es.js.map +1 -1
  81. package/ignite-ui.d.ts +1 -0
  82. package/ignite-ui.js +4 -0
  83. package/ignite-ui.js.map +1 -0
  84. package/internal/column-types.d.ts +11 -1
  85. package/internal/column-types.js +8 -6
  86. package/internal/column-types.js.map +1 -1
  87. package/internal/design-system.d.ts +32 -0
  88. package/internal/design-system.js +66 -0
  89. package/internal/design-system.js.map +1 -0
  90. package/internal/export.d.ts +6 -0
  91. package/internal/export.js +4 -0
  92. package/internal/export.js.map +1 -1
  93. package/internal/feature-module.d.ts +7 -0
  94. package/internal/feature-module.js.map +1 -1
  95. package/internal/flip.d.ts +28 -16
  96. package/internal/flip.js +42 -10
  97. package/internal/flip.js.map +1 -1
  98. package/internal/icons.d.ts +2 -16
  99. package/internal/icons.js +0 -8
  100. package/internal/icons.js.map +1 -1
  101. package/internal/ignite-ui.d.ts +10 -0
  102. package/internal/ignite-ui.js +118 -0
  103. package/internal/ignite-ui.js.map +1 -0
  104. package/internal/index.d.ts +4 -2
  105. package/internal/index.js +3 -2
  106. package/internal/index.js.map +1 -1
  107. package/internal/types.d.ts +75 -19
  108. package/internal/types.js.map +1 -1
  109. package/operations/filter/types.d.ts +1 -1
  110. package/operations/filter/types.js.map +1 -1
  111. package/package.json +7 -2
  112. package/styles/_token-blocks.css.d.ts +1 -0
  113. package/styles/_token-blocks.css.js +3 -0
  114. package/styles/_token-blocks.css.js.map +1 -0
  115. package/styles/_tokens.css.js +1 -1
  116. package/styles/_tokens.css.js.map +1 -1
  117. package/styles/body-cell/body-cell.css.js +1 -1
  118. package/styles/body-cell/body-cell.css.js.map +1 -1
  119. package/styles/body-cell/cell-renderers.css.js +1 -1
  120. package/styles/body-cell/cell-renderers.css.js.map +1 -1
  121. package/styles/body-row/body-row.css.js +1 -1
  122. package/styles/body-row/body-row.css.js.map +1 -1
  123. package/styles/document-tokens.css.js +1 -1
  124. package/styles/document-tokens.css.js.map +1 -1
  125. package/styles/grid/_grid.base.css.js +1 -1
  126. package/styles/grid/_grid.base.css.js.map +1 -1
  127. package/styles/grid/grid.css.js +1 -1
  128. package/styles/grid/grid.css.js.map +1 -1
  129. package/styles/group-header-row/group-header-row.css.js +1 -1
  130. package/styles/group-header-row/group-header-row.css.js.map +1 -1
  131. package/styles/header-cell/header-cell.css.js +1 -1
  132. package/styles/header-cell/header-cell.css.js.map +1 -1
  133. package/styles/header-row/header-row.base.css.js +1 -1
  134. package/styles/header-row/header-row.base.css.js.map +1 -1
  135. package/styles/ignite-ui/ignite-ui-dark.css.d.ts +1 -0
  136. package/styles/ignite-ui/ignite-ui-dark.css.js +3 -0
  137. package/styles/ignite-ui/ignite-ui-dark.css.js.map +1 -0
  138. package/styles/ignite-ui/ignite-ui-document.css.d.ts +1 -0
  139. package/styles/ignite-ui/ignite-ui-document.css.js +3 -0
  140. package/styles/ignite-ui/ignite-ui-document.css.js.map +1 -0
  141. package/styles/ignite-ui/ignite-ui.css.d.ts +1 -0
  142. package/styles/ignite-ui/ignite-ui.css.js +3 -0
  143. package/styles/ignite-ui/ignite-ui.css.js.map +1 -0
  144. package/styles/paginator/paginator.css.js +1 -1
  145. package/styles/paginator/paginator.css.js.map +1 -1
  146. package/styles/toolbar/toolbar.css.js +1 -1
  147. package/styles/toolbar/toolbar.css.js.map +1 -1
  148. package/tokens.css +14 -15
  149. package/operations/pagination.d.ts +0 -19
  150. package/operations/pagination.js +0 -12
  151. package/operations/pagination.js.map +0 -1
@@ -19,9 +19,11 @@ export declare const DEFAULT_SELECTION_CONFIG: Readonly<{
19
19
  * any selected rows that are no longer present in the new array; consumers
20
20
  * should call {@link clear} or re-apply selection after such a change.
21
21
  *
22
- * Only data records are selectable: rows a feature module synthesizes into the
23
- * view (group headers, a grand-total row; see {@link StateController.isDataRow})
24
- * are skipped by every mutation and ignored by {@link allSelected}.
22
+ * Only selectable data records are selected (see {@link isSelectable}): rows
23
+ * a feature module synthesizes into the view (group headers, a grand-total
24
+ * row; see {@link StateController.isDataRow}) and rows the
25
+ * `selection.isRowSelectable` predicate rejects are skipped by every
26
+ * mutation and ignored by {@link allSelected}.
25
27
  *
26
28
  * Mutations go through the cancellable `rowSelecting` event and emit a
27
29
  * follow-up `rowSelected` event, mirroring the pattern used by editing.
@@ -38,6 +40,14 @@ export declare class SelectionController<T extends object> implements ReactiveCo
38
40
  anchor: T | null;
39
41
  constructor(host: GridHost<T>);
40
42
  hostConnected(): void;
43
+ /**
44
+ * Drops selected rows the `isRowSelectable` predicate rejects once
45
+ * `selection` or `data` is reassigned. Runs after the render, so the
46
+ * `rowSelecting`/`rowSelected` listeners (and any grid property they
47
+ * write) start a normal update. Without a predicate nothing is checked,
48
+ * and a mode switch never trims the selection here.
49
+ */
50
+ hostUpdated(): void;
41
51
  /**
42
52
  * Whether selection is enabled at the grid level.
43
53
  */
@@ -54,15 +64,27 @@ export declare class SelectionController<T extends object> implements ReactiveCo
54
64
  * Whether the given row is currently selected.
55
65
  */
56
66
  isSelected(row: T): boolean;
67
+ /**
68
+ * Whether `row` can be selected: a data record (see
69
+ * {@link StateController.isDataRow}) that `selection.isRowSelectable`, when
70
+ * set, accepts.
71
+ */
72
+ isSelectable(row: T): boolean;
73
+ /**
74
+ * The selectable rows of `rows` (see {@link isSelectable}), in order.
75
+ * Returns `rows` itself when nothing filters them (the community grid
76
+ * without a predicate).
77
+ */
78
+ selectable(rows: ReadonlyArray<T>): ReadonlyArray<T>;
57
79
  /**
58
80
  * Returns the currently selected rows in insertion order.
59
81
  */
60
82
  selectedRows(): T[];
61
83
  /**
62
- * Whether every data record the current view covers is selected, including
63
- * the leaves of collapsed groups (see {@link StateController.viewRecords}).
64
- * `false` when the view covers none. Used by the header checkbox to render
65
- * its "all" state.
84
+ * Whether every selectable record the current view covers is selected,
85
+ * including the leaves of collapsed groups (see
86
+ * {@link StateController.viewRecords}). `false` when the view covers none.
87
+ * Used by the header checkbox to render its "all" state.
66
88
  */
67
89
  allSelected(): boolean;
68
90
  /**
@@ -98,9 +120,9 @@ export declare class SelectionController<T extends object> implements ReactiveCo
98
120
  */
99
121
  rangeToggle(row: T): Promise<boolean>;
100
122
  /**
101
- * Selects every data record the current view ({@link ApexGrid.dataView})
102
- * covers, including the leaves of collapsed groups. No-op in `'single'`
103
- * mode.
123
+ * Selects every selectable record the current view
124
+ * ({@link ApexGrid.dataView}) covers, including the leaves of collapsed
125
+ * groups. No-op in `'single'` mode.
104
126
  */
105
127
  selectAll(): Promise<boolean>;
106
128
  /**
@@ -4,13 +4,26 @@ export const DEFAULT_SELECTION_CONFIG = Object.freeze({
4
4
  showCheckboxColumn: false,
5
5
  });
6
6
  export class SelectionController {
7
+ #seen;
7
8
  constructor(host) {
8
9
  this.host = host;
9
10
  this.selected = new Set();
10
11
  this.anchor = null;
12
+ this.#seen = [undefined, undefined];
11
13
  this.host.addController(this);
12
14
  }
13
15
  hostConnected() { }
16
+ hostUpdated() {
17
+ const { selection, data } = this.host;
18
+ if (selection === this.#seen[0] && data === this.#seen[1])
19
+ return;
20
+ this.#seen = [selection, data];
21
+ if (!this.enabled || !selection?.isRowSelectable)
22
+ return;
23
+ const keep = this.selectable(this.selectedRows());
24
+ if (keep.length < this.selected.size)
25
+ void this.#commit(new Set(keep));
26
+ }
14
27
  get enabled() {
15
28
  return Boolean(this.host.selection?.enabled);
16
29
  }
@@ -24,6 +37,16 @@ export class SelectionController {
24
37
  isSelected(row) {
25
38
  return this.selected.has(row);
26
39
  }
40
+ isSelectable(row) {
41
+ return ((this.#state?.isDataRow(row) ?? true) && (this.host.selection?.isRowSelectable?.(row) ?? true));
42
+ }
43
+ selectable(rows) {
44
+ return this.#accepted(this.#state?.dataRows(rows) ?? rows);
45
+ }
46
+ #accepted(rows) {
47
+ const accept = this.host.selection?.isRowSelectable;
48
+ return accept ? rows.filter((row) => accept(row)) : rows;
49
+ }
27
50
  selectedRows() {
28
51
  return Array.from(this.selected);
29
52
  }
@@ -45,7 +68,7 @@ export class SelectionController {
45
68
  return true;
46
69
  }
47
70
  async toggleRow(row) {
48
- if (!this.enabled || !this.#isDataRow(row))
71
+ if (!this.#togglable(row))
49
72
  return false;
50
73
  const next = new Set(this.selected);
51
74
  if (next.has(row)) {
@@ -60,7 +83,7 @@ export class SelectionController {
60
83
  return this.#commit(next);
61
84
  }
62
85
  async selectRow(row) {
63
- if (!this.enabled || !this.#isDataRow(row))
86
+ if (!this.enabled || !this.isSelectable(row))
64
87
  return false;
65
88
  const next = new Set(this.mode === 'single' ? [] : this.selected);
66
89
  next.add(row);
@@ -77,7 +100,7 @@ export class SelectionController {
77
100
  return this.#commit(next);
78
101
  }
79
102
  async additiveToggle(row) {
80
- if (!this.enabled || !this.#isDataRow(row))
103
+ if (!this.#togglable(row))
81
104
  return false;
82
105
  if (this.mode === 'single')
83
106
  return this.toggleRow(row);
@@ -92,7 +115,7 @@ export class SelectionController {
92
115
  return this.#commit(next);
93
116
  }
94
117
  async rangeToggle(row) {
95
- if (!this.enabled || !this.#isDataRow(row))
118
+ if (!this.enabled || !this.isSelectable(row))
96
119
  return false;
97
120
  if (this.mode === 'single' || !this.anchor) {
98
121
  return this.selectRow(row);
@@ -106,7 +129,7 @@ export class SelectionController {
106
129
  const [lo, hi] = startIdx < endIdx ? [startIdx, endIdx] : [endIdx, startIdx];
107
130
  const next = new Set(this.selected);
108
131
  for (let i = lo; i <= hi; i++) {
109
- if (this.#isDataRow(pageItems[i]))
132
+ if (this.isSelectable(pageItems[i]))
110
133
  next.add(pageItems[i]);
111
134
  }
112
135
  return this.#commit(next);
@@ -124,19 +147,19 @@ export class SelectionController {
124
147
  async replaceSelection(rows) {
125
148
  if (!this.enabled)
126
149
  return false;
127
- const records = this.#state?.dataRows(rows) ?? rows;
150
+ const records = this.selectable(rows);
128
151
  const next = new Set(this.mode === 'single' ? records.slice(0, 1) : records);
129
152
  return this.#commit(next);
130
153
  }
131
154
  get #state() {
132
155
  return this.host.stateController;
133
156
  }
157
+ #togglable(row) {
158
+ return this.enabled && (this.selected.has(row) || this.isSelectable(row));
159
+ }
134
160
  #records() {
135
161
  const view = this.host.dataView;
136
- return this.#state?.viewRecords(view) ?? view;
137
- }
138
- #isDataRow(row) {
139
- return this.#state?.isDataRow(row) ?? true;
162
+ return this.#accepted(this.#state?.viewRecords(view) ?? view);
140
163
  }
141
164
  async #commit(next) {
142
165
  const added = [];
@@ -1 +1 @@
1
- {"version":3,"file":"selection.js","sourceRoot":"","sources":["../../src/controllers/selection.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController } from 'lit';\nimport type { GridHost, SelectionMode } from '../internal/types.js';\nimport type { StateController } from './state.js';\n\n/**\n * The default selection configuration used when the grid has none set.\n */\nexport const DEFAULT_SELECTION_CONFIG = Object.freeze({\n enabled: false,\n mode: 'multiple' as SelectionMode,\n showCheckboxColumn: false,\n});\n\n/**\n * Reactive controller backing row selection.\n *\n * @remarks\n * Selection state is reference-based — the controller holds a `Set` of row\n * data references so the selection survives sort / filter / pagination as\n * long as those operations preserve identities (the default in-place\n * pipeline does). Replacing {@link ApexGrid.data} wholesale will invalidate\n * any selected rows that are no longer present in the new array; consumers\n * should call {@link clear} or re-apply selection after such a change.\n *\n * Only data records are selectable: rows a feature module synthesizes into the\n * view (group headers, a grand-total row; see {@link StateController.isDataRow})\n * are skipped by every mutation and ignored by {@link allSelected}.\n *\n * Mutations go through the cancellable `rowSelecting` event and emit a\n * follow-up `rowSelected` event, mirroring the pattern used by editing.\n */\nexport class SelectionController<T extends object> implements ReactiveController {\n /** Set of currently selected row data references. */\n public selected: Set<T> = new Set();\n\n /**\n * Anchor row for Shift+click range selection. Set whenever a single row\n * is toggled / picked so a follow-up Shift+click knows where to start.\n */\n public anchor: T | null = null;\n\n constructor(protected host: GridHost<T>) {\n this.host.addController(this);\n }\n\n public hostConnected() {}\n\n /**\n * Whether selection is enabled at the grid level.\n */\n public get enabled(): boolean {\n return Boolean(this.host.selection?.enabled);\n }\n\n /**\n * The resolved selection mode (`'multiple'` if unset).\n */\n public get mode(): SelectionMode {\n return this.host.selection?.mode ?? DEFAULT_SELECTION_CONFIG.mode;\n }\n\n /**\n * Whether to render the built-in checkbox column.\n */\n public get showCheckboxColumn(): boolean {\n return (\n this.enabled &&\n (this.host.selection?.showCheckboxColumn ?? DEFAULT_SELECTION_CONFIG.showCheckboxColumn)\n );\n }\n\n /**\n * Whether the given row is currently selected.\n */\n public isSelected(row: T): boolean {\n return this.selected.has(row);\n }\n\n /**\n * Returns the currently selected rows in insertion order.\n */\n public selectedRows(): T[] {\n return Array.from(this.selected);\n }\n\n /**\n * Whether every data record the current view covers is selected, including\n * the leaves of collapsed groups (see {@link StateController.viewRecords}).\n * `false` when the view covers none. Used by the header checkbox to render\n * its \"all\" state.\n */\n public allSelected(): boolean {\n const records = this.#records();\n if (records.length === 0) return false;\n for (const row of records) {\n if (!this.selected.has(row)) return false;\n }\n return true;\n }\n\n /**\n * Whether some — but not all — rows in the current view are selected.\n * Used to render the header checkbox in its indeterminate state.\n */\n public someSelected(): boolean {\n if (this.selected.size === 0) return false;\n if (this.allSelected()) return false;\n return true;\n }\n\n /**\n * Toggles selection of `row`. In `'single'` mode this either selects the\n * row (replacing any previous selection) or deselects it if it was the\n * only selected row.\n */\n public async toggleRow(row: T): Promise<boolean> {\n if (!this.enabled || !this.#isDataRow(row)) return false;\n const next = new Set(this.selected);\n if (next.has(row)) {\n next.delete(row);\n } else {\n if (this.mode === 'single') next.clear();\n next.add(row);\n }\n this.anchor = next.has(row) ? row : null;\n return this.#commit(next);\n }\n\n /**\n * Adds `row` to the selection. In `'single'` mode the existing selection\n * is cleared first.\n */\n public async selectRow(row: T): Promise<boolean> {\n if (!this.enabled || !this.#isDataRow(row)) return false;\n const next = new Set(this.mode === 'single' ? [] : this.selected);\n next.add(row);\n this.anchor = row;\n return this.#commit(next);\n }\n\n /**\n * Removes `row` from the selection.\n */\n public async deselectRow(row: T): Promise<boolean> {\n if (!this.enabled) return false;\n if (!this.selected.has(row)) return true;\n const next = new Set(this.selected);\n next.delete(row);\n return this.#commit(next);\n }\n\n /**\n * Adds `row` to the selection without affecting other selected rows\n * (`'multiple'` mode only). In `'single'` mode this behaves like\n * {@link selectRow}.\n */\n public async additiveToggle(row: T): Promise<boolean> {\n if (!this.enabled || !this.#isDataRow(row)) return false;\n if (this.mode === 'single') return this.toggleRow(row);\n const next = new Set(this.selected);\n if (next.has(row)) {\n next.delete(row);\n } else {\n next.add(row);\n }\n this.anchor = next.has(row) ? row : this.anchor;\n return this.#commit(next);\n }\n\n /**\n * Selects every row between {@link anchor} and `row` (inclusive) in the\n * current page view. No-op in `'single'` mode or when no anchor is set —\n * in those cases the call falls back to a plain {@link selectRow}.\n */\n public async rangeToggle(row: T): Promise<boolean> {\n if (!this.enabled || !this.#isDataRow(row)) return false;\n if (this.mode === 'single' || !this.anchor) {\n return this.selectRow(row);\n }\n const pageItems = this.host.pageItems as ReadonlyArray<T>;\n const startIdx = pageItems.indexOf(this.anchor);\n const endIdx = pageItems.indexOf(row);\n if (startIdx === -1 || endIdx === -1) {\n return this.selectRow(row);\n }\n const [lo, hi] = startIdx < endIdx ? [startIdx, endIdx] : [endIdx, startIdx];\n const next = new Set(this.selected);\n for (let i = lo; i <= hi; i++) {\n if (this.#isDataRow(pageItems[i])) next.add(pageItems[i]);\n }\n return this.#commit(next);\n }\n\n /**\n * Selects every data record the current view ({@link ApexGrid.dataView})\n * covers, including the leaves of collapsed groups. No-op in `'single'`\n * mode.\n */\n public async selectAll(): Promise<boolean> {\n if (!this.enabled || this.mode === 'single') return false;\n return this.#commit(new Set(this.#records()));\n }\n\n /**\n * Clears the selection.\n */\n public async clear(): Promise<boolean> {\n if (this.selected.size === 0) return true;\n return this.#commit(new Set());\n }\n\n /**\n * Replaces the current selection with `rows`. Used by the public\n * {@link ApexGrid.selectedRows} setter so callers can drive selection\n * programmatically. Single-mode keeps at most one row.\n */\n public async replaceSelection(rows: ReadonlyArray<T>): Promise<boolean> {\n if (!this.enabled) return false;\n const records = this.#state?.dataRows(rows) ?? rows;\n const next = new Set(this.mode === 'single' ? records.slice(0, 1) : records);\n return this.#commit(next);\n }\n\n /** The owning state controller; absent only while the grid is constructing. */\n get #state(): StateController<T> | undefined {\n return (this.host as unknown as { stateController?: StateController<T> }).stateController;\n }\n\n /** The data records the current view covers, collapsed group leaves included. */\n #records(): ReadonlyArray<T> {\n const view = this.host.dataView as ReadonlyArray<T>;\n return this.#state?.viewRecords(view) ?? view;\n }\n\n #isDataRow(row: T): boolean {\n return this.#state?.isDataRow(row) ?? true;\n }\n\n async #commit(next: Set<T>): Promise<boolean> {\n const added: T[] = [];\n const removed: T[] = [];\n for (const row of next) {\n if (!this.selected.has(row)) added.push(row);\n }\n for (const row of this.selected) {\n if (!next.has(row)) removed.push(row);\n }\n if (added.length === 0 && removed.length === 0) return true;\n\n const proceed = this.host.emitEvent('rowSelecting', {\n detail: {\n added,\n removed,\n current: Array.from(this.selected),\n next: Array.from(next),\n },\n cancelable: true,\n });\n if (!proceed) return false;\n\n this.selected = next;\n this.host.requestUpdate();\n\n const total = this.selected.size;\n if (total === 0) {\n this.host.announce(this.host.localize('announce.selectionCleared'));\n } else if (added.length === 1 && removed.length === 0 && total === 1) {\n this.host.announce(this.host.localize('announce.rowSelected'));\n } else {\n this.host.announce(this.host.localize('announce.rowsSelected', { count: total }));\n }\n\n this.host.emitEvent('rowSelected', {\n detail: { added, removed, selected: Array.from(this.selected) },\n });\n return true;\n }\n}\n"]}
1
+ {"version":3,"file":"selection.js","sourceRoot":"","sources":["../../src/controllers/selection.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController } from 'lit';\nimport type { GridHost, SelectionMode } from '../internal/types.js';\nimport type { StateController } from './state.js';\n\n/**\n * The default selection configuration used when the grid has none set.\n */\nexport const DEFAULT_SELECTION_CONFIG = Object.freeze({\n enabled: false,\n mode: 'multiple' as SelectionMode,\n showCheckboxColumn: false,\n});\n\n/**\n * Reactive controller backing row selection.\n *\n * @remarks\n * Selection state is reference-based — the controller holds a `Set` of row\n * data references so the selection survives sort / filter / pagination as\n * long as those operations preserve identities (the default in-place\n * pipeline does). Replacing {@link ApexGrid.data} wholesale will invalidate\n * any selected rows that are no longer present in the new array; consumers\n * should call {@link clear} or re-apply selection after such a change.\n *\n * Only selectable data records are selected (see {@link isSelectable}): rows\n * a feature module synthesizes into the view (group headers, a grand-total\n * row; see {@link StateController.isDataRow}) and rows the\n * `selection.isRowSelectable` predicate rejects are skipped by every\n * mutation and ignored by {@link allSelected}.\n *\n * Mutations go through the cancellable `rowSelecting` event and emit a\n * follow-up `rowSelected` event, mirroring the pattern used by editing.\n */\nexport class SelectionController<T extends object> implements ReactiveController {\n /** Set of currently selected row data references. */\n public selected: Set<T> = new Set();\n\n /**\n * Anchor row for Shift+click range selection. Set whenever a single row\n * is toggled / picked so a follow-up Shift+click knows where to start.\n */\n public anchor: T | null = null;\n\n /** The selection config and data the selection was last checked against. */\n #seen: [unknown, unknown] = [undefined, undefined];\n\n constructor(protected host: GridHost<T>) {\n this.host.addController(this);\n }\n\n public hostConnected() {}\n\n /**\n * Drops selected rows the `isRowSelectable` predicate rejects once\n * `selection` or `data` is reassigned. Runs after the render, so the\n * `rowSelecting`/`rowSelected` listeners (and any grid property they\n * write) start a normal update. Without a predicate nothing is checked,\n * and a mode switch never trims the selection here.\n */\n public hostUpdated() {\n const { selection, data } = this.host;\n if (selection === this.#seen[0] && data === this.#seen[1]) return;\n this.#seen = [selection, data];\n if (!this.enabled || !selection?.isRowSelectable) return;\n const keep = this.selectable(this.selectedRows());\n if (keep.length < this.selected.size) void this.#commit(new Set(keep));\n }\n\n /**\n * Whether selection is enabled at the grid level.\n */\n public get enabled(): boolean {\n return Boolean(this.host.selection?.enabled);\n }\n\n /**\n * The resolved selection mode (`'multiple'` if unset).\n */\n public get mode(): SelectionMode {\n return this.host.selection?.mode ?? DEFAULT_SELECTION_CONFIG.mode;\n }\n\n /**\n * Whether to render the built-in checkbox column.\n */\n public get showCheckboxColumn(): boolean {\n return (\n this.enabled &&\n (this.host.selection?.showCheckboxColumn ?? DEFAULT_SELECTION_CONFIG.showCheckboxColumn)\n );\n }\n\n /**\n * Whether the given row is currently selected.\n */\n public isSelected(row: T): boolean {\n return this.selected.has(row);\n }\n\n /**\n * Whether `row` can be selected: a data record (see\n * {@link StateController.isDataRow}) that `selection.isRowSelectable`, when\n * set, accepts.\n */\n public isSelectable(row: T): boolean {\n return (\n (this.#state?.isDataRow(row) ?? true) && (this.host.selection?.isRowSelectable?.(row) ?? true)\n );\n }\n\n /**\n * The selectable rows of `rows` (see {@link isSelectable}), in order.\n * Returns `rows` itself when nothing filters them (the community grid\n * without a predicate).\n */\n public selectable(rows: ReadonlyArray<T>): ReadonlyArray<T> {\n return this.#accepted(this.#state?.dataRows(rows) ?? rows);\n }\n\n /** The rows `selection.isRowSelectable` accepts; `rows` itself when unset. */\n #accepted(rows: ReadonlyArray<T>): ReadonlyArray<T> {\n const accept = this.host.selection?.isRowSelectable;\n return accept ? rows.filter((row) => accept(row)) : rows;\n }\n\n /**\n * Returns the currently selected rows in insertion order.\n */\n public selectedRows(): T[] {\n return Array.from(this.selected);\n }\n\n /**\n * Whether every selectable record the current view covers is selected,\n * including the leaves of collapsed groups (see\n * {@link StateController.viewRecords}). `false` when the view covers none.\n * Used by the header checkbox to render its \"all\" state.\n */\n public allSelected(): boolean {\n const records = this.#records();\n if (records.length === 0) return false;\n for (const row of records) {\n if (!this.selected.has(row)) return false;\n }\n return true;\n }\n\n /**\n * Whether some — but not all — rows in the current view are selected.\n * Used to render the header checkbox in its indeterminate state.\n */\n public someSelected(): boolean {\n if (this.selected.size === 0) return false;\n if (this.allSelected()) return false;\n return true;\n }\n\n /**\n * Toggles selection of `row`. In `'single'` mode this either selects the\n * row (replacing any previous selection) or deselects it if it was the\n * only selected row.\n */\n public async toggleRow(row: T): Promise<boolean> {\n if (!this.#togglable(row)) return false;\n const next = new Set(this.selected);\n if (next.has(row)) {\n next.delete(row);\n } else {\n if (this.mode === 'single') next.clear();\n next.add(row);\n }\n this.anchor = next.has(row) ? row : null;\n return this.#commit(next);\n }\n\n /**\n * Adds `row` to the selection. In `'single'` mode the existing selection\n * is cleared first.\n */\n public async selectRow(row: T): Promise<boolean> {\n if (!this.enabled || !this.isSelectable(row)) return false;\n const next = new Set(this.mode === 'single' ? [] : this.selected);\n next.add(row);\n this.anchor = row;\n return this.#commit(next);\n }\n\n /**\n * Removes `row` from the selection.\n */\n public async deselectRow(row: T): Promise<boolean> {\n if (!this.enabled) return false;\n if (!this.selected.has(row)) return true;\n const next = new Set(this.selected);\n next.delete(row);\n return this.#commit(next);\n }\n\n /**\n * Adds `row` to the selection without affecting other selected rows\n * (`'multiple'` mode only). In `'single'` mode this behaves like\n * {@link selectRow}.\n */\n public async additiveToggle(row: T): Promise<boolean> {\n if (!this.#togglable(row)) return false;\n if (this.mode === 'single') return this.toggleRow(row);\n const next = new Set(this.selected);\n if (next.has(row)) {\n next.delete(row);\n } else {\n next.add(row);\n }\n this.anchor = next.has(row) ? row : this.anchor;\n return this.#commit(next);\n }\n\n /**\n * Selects every row between {@link anchor} and `row` (inclusive) in the\n * current page view. No-op in `'single'` mode or when no anchor is set —\n * in those cases the call falls back to a plain {@link selectRow}.\n */\n public async rangeToggle(row: T): Promise<boolean> {\n if (!this.enabled || !this.isSelectable(row)) return false;\n if (this.mode === 'single' || !this.anchor) {\n return this.selectRow(row);\n }\n const pageItems = this.host.pageItems as ReadonlyArray<T>;\n const startIdx = pageItems.indexOf(this.anchor);\n const endIdx = pageItems.indexOf(row);\n if (startIdx === -1 || endIdx === -1) {\n return this.selectRow(row);\n }\n const [lo, hi] = startIdx < endIdx ? [startIdx, endIdx] : [endIdx, startIdx];\n const next = new Set(this.selected);\n for (let i = lo; i <= hi; i++) {\n if (this.isSelectable(pageItems[i])) next.add(pageItems[i]);\n }\n return this.#commit(next);\n }\n\n /**\n * Selects every selectable record the current view\n * ({@link ApexGrid.dataView}) covers, including the leaves of collapsed\n * groups. No-op in `'single'` mode.\n */\n public async selectAll(): Promise<boolean> {\n if (!this.enabled || this.mode === 'single') return false;\n return this.#commit(new Set(this.#records()));\n }\n\n /**\n * Clears the selection.\n */\n public async clear(): Promise<boolean> {\n if (this.selected.size === 0) return true;\n return this.#commit(new Set());\n }\n\n /**\n * Replaces the current selection with `rows`. Used by the public\n * {@link ApexGrid.selectedRows} setter so callers can drive selection\n * programmatically. Single-mode keeps at most one row.\n */\n public async replaceSelection(rows: ReadonlyArray<T>): Promise<boolean> {\n if (!this.enabled) return false;\n const records = this.selectable(rows);\n const next = new Set(this.mode === 'single' ? records.slice(0, 1) : records);\n return this.#commit(next);\n }\n\n /** The owning state controller; absent only while the grid is constructing. */\n get #state(): StateController<T> | undefined {\n return (this.host as unknown as { stateController?: StateController<T> }).stateController;\n }\n\n /**\n * Whether a toggle may act on `row`: a selected row can always be\n * deselected, even one the predicate has since come to reject (a row\n * pinned or edited in place), so it never gets stuck selected.\n */\n #togglable(row: T): boolean {\n return this.enabled && (this.selected.has(row) || this.isSelectable(row));\n }\n\n /** The selectable records the current view covers, collapsed group leaves included. */\n #records(): ReadonlyArray<T> {\n const view = this.host.dataView as ReadonlyArray<T>;\n return this.#accepted(this.#state?.viewRecords(view) ?? view);\n }\n\n async #commit(next: Set<T>): Promise<boolean> {\n const added: T[] = [];\n const removed: T[] = [];\n for (const row of next) {\n if (!this.selected.has(row)) added.push(row);\n }\n for (const row of this.selected) {\n if (!next.has(row)) removed.push(row);\n }\n if (added.length === 0 && removed.length === 0) return true;\n\n const proceed = this.host.emitEvent('rowSelecting', {\n detail: {\n added,\n removed,\n current: Array.from(this.selected),\n next: Array.from(next),\n },\n cancelable: true,\n });\n if (!proceed) return false;\n\n this.selected = next;\n this.host.requestUpdate();\n\n const total = this.selected.size;\n if (total === 0) {\n this.host.announce(this.host.localize('announce.selectionCleared'));\n } else if (added.length === 1 && removed.length === 0 && total === 1) {\n this.host.announce(this.host.localize('announce.rowSelected'));\n } else {\n this.host.announce(this.host.localize('announce.rowsSelected', { count: total }));\n }\n\n this.host.emitEvent('rowSelected', {\n detail: { added, removed, selected: Array.from(this.selected) },\n });\n return true;\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { PIPELINE } from '../internal/constants.js';
2
- import { awaitChildUpdates, playKeyedFlip } from '../internal/flip.js';
2
+ import { playKeyedFlip, settleRowsForFlip } from '../internal/flip.js';
3
3
  import { asArray } from '../internal/utils.js';
4
4
  export class SortController {
5
5
  constructor(host) {
@@ -68,8 +68,7 @@ export class SortController {
68
68
  }
69
69
  const beforeRects = this.#captureRowRects();
70
70
  this._sort(expression);
71
- await this.host.pipelineSettled();
72
- await awaitChildUpdates(this.host.rows);
71
+ await settleRowsForFlip(this.host);
73
72
  this.#playRowFlip(beforeRects);
74
73
  this.#announce(expression, column);
75
74
  this.#emitSortedEvent(expression);
@@ -1 +1 @@
1
- {"version":3,"file":"sort.js","sourceRoot":"","sources":["../../src/controllers/sort.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAuB,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAO5F,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAG/C,MAAM,OAAO,cAAc;IACzB,YAAsB,IAAiB;QAAjB,SAAI,GAAJ,IAAI,CAAa;QAIhC,UAAK,GAAiB,IAAI,GAAG,EAAE,CAAC;QAHrC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAID,IAAI,eAAe;QACjB,OAAO,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC;IAC9C,CAAC;IAED,IAAI,eAAe;QACjB,OAAO,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC;IAC9C,CAAC;IASD,mBAAmB,CAAC,OAA8C;QAChE,MAAM,IAAI,GAA+B,EAAE,CAAC;QAC5C,IAAI,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC3C,IAAI,OAAO,CAAC,aAAa,KAAK,SAAS;gBAAE,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;YACpF,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS;gBAAE,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAe,CAAC;QAC9E,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,wBAAwB,CAAC,GAAY;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC;QAE/C,OAAO;YACL,GAAG;YACH,SAAS,EAAE,WAAW;YACtB,aAAa,EAAE,KAAK;YACpB,QAAQ,EAAE,SAAS;YACnB,GAAG,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC;SAChB,CAAC;IACzB,CAAC;IAED,QAAQ,CAAC,GAAsB;QAC7B,OAAO,IAAI,CAAC,eAAe;YACzB,CAAC,CAAC,GAAG,KAAK,WAAW;gBACnB,CAAC,CAAC,YAAY;gBACd,CAAC,CAAC,GAAG,KAAK,YAAY;oBACpB,CAAC,CAAC,MAAM;oBACR,CAAC,CAAC,WAAW;YACjB,CAAC,CAAC,GAAG,KAAK,WAAW;gBACnB,CAAC,CAAC,YAAY;gBACd,CAAC,CAAC,WAAW,CAAC;IACpB,CAAC;IAED,iBAAiB,CAAC,MAAyB;QACzC,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;IACtE,CAAC;IAED,gBAAgB,CAAC,MAAyB;QACxC,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,cAAc,CAAC,UAA6B;QAC1C,UAAU,CAAC,SAAS,KAAK,MAAM;YAC7B,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;YAC5B,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE,GAAG,UAAU,EAAE,CAAC,CAAC;IACxD,CAAC;IAEM,mBAAmB,CAAC,MAA8B,EAAE,QAAQ,GAAG,KAAK;QACzE,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC/C,CAAC;IAQM,KAAK,CAAC,UAAU,CACrB,MAA8B,EAC9B,EAAE,SAAS,EAAE,QAAQ,GAAG,KAAK,KAA2D,EAAE;QAE1F,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAE7D,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,EAAE,CAAC;YACxC,OAAO;QACT,CAAC;QAQD,IAAI,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,QAAQ,EAAE,CAAC;YACvC,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC;QAMD,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAE5C,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAGvB,MAAM,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;QAIlC,MAAM,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;QAC/B,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACnC,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;IACpC,CAAC;IAED,SAAS,CAAC,UAA6B,EAAE,MAA8B;QACrE,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACtD,MAAM,OAAO,GACX,UAAU,CAAC,SAAS,KAAK,MAAM;YAC7B,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,sBAAsB,EAAE,EAAE,KAAK,EAAE,CAAC;YACvD,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAChB,UAAU,CAAC,SAAS,KAAK,WAAW;gBAClC,CAAC,CAAC,0BAA0B;gBAC5B,CAAC,CAAC,2BAA2B,EAC/B,EAAE,KAAK,EAAE,CACV,CAAC;QACR,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAED,gBAAgB;QACd,MAAM,OAAO,GAAwB,EAAE,CAAC;QACxC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACjC,MAAM,EAAE,GAAG,GAA6B,CAAC;YACzC,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;QACpE,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,YAAY,CAAC,MAAwC;QAGnD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;QAC5B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;QACzC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,GAA6B,CAAC,CAAC;QACtD,CAAC;QACD,aAAa,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,GAAG,CAAC,CAAC;IACjE,CAAC;IAMM,iBAAiB,CACtB,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAA0B,EAC9C,SAA4B;QAE5B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEjC,OAAO;YACL,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,wBAAwB,CAAC,GAAG,CAAC,CAAC;YAC/C,SAAS,EAAE,SAAS,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;YACtD,GAAG,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC;SAChB,CAAC;IACzB,CAAC;IAOM,UAAU,CAAC,MAA8B;QAC9C,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC;IACnE,CAAC;IAEM,KAAK,CAAC,GAAa;QACxB,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACpD,CAAC;IAED,IAAI,WAAW;QAEb,OAAO,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,UAEpB,CAAC;IAChB,CAAC;IAES,KAAK,CAAC,WAAoD;QAClE,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;QAGnC,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC;QACjC,IAAI,OAAO,EAAE,cAAc,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,KAAK,MAAM,CAAC,EAAE,CAAC;YAC/E,OAAO,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACjC,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAEM,IAAI,CAAC,WAAoD;QAC9D,IAAI,CAAC,KAAK,CACR,OAAO,CAAC,WAAW,CAAC,CAAC,GAAG,CACtB,CAAC,IAAI,EAAE,EAAE,CACP,CAAC;YACC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACxE,GAAG,IAAI;SACR,CAAsB,CAC1B,CACF,CAAC;IACJ,CAAC;IAEM,aAAa,KAAI,CAAC;CAC1B","sourcesContent":["import type { ReactiveController } from 'lit';\nimport { PIPELINE } from '../internal/constants.js';\nimport { awaitChildUpdates, type KeyedFlipEntry, playKeyedFlip } from '../internal/flip.js';\nimport type {\n ColumnConfiguration,\n ColumnSortConfiguration,\n GridHost,\n Keys,\n} from '../internal/types.js';\nimport { asArray } from '../internal/utils.js';\nimport type { SortExpression, SortingDirection, SortState } from '../operations/sort/types.js';\n\nexport class SortController<T extends object> implements ReactiveController {\n constructor(protected host: GridHost<T>) {\n this.host.addController(this);\n }\n\n public state: SortState<T> = new Map();\n\n get #isMultipleSort() {\n return this.host.sortConfiguration.multiple;\n }\n\n get #isTriStateSort() {\n return this.host.sortConfiguration.triState;\n }\n\n /**\n * Only the options the column config actually sets, so a header click keeps a\n * comparer or caseSensitive that came in through `grid.sort()` when the column\n * itself does not define one. The trade-off: a comparer later removed from\n * the column config stays on the stored expression until that column's sort\n * is cleared, because its origin is not tracked.\n */\n #resolveSortOptions(options?: boolean | ColumnSortConfiguration<T>) {\n const expr: Partial<SortExpression<T>> = {};\n if (options && typeof options === 'object') {\n if (options.caseSensitive !== undefined) expr.caseSensitive = options.caseSensitive;\n if (options.comparer !== undefined) expr.comparer = options.comparer as any;\n }\n return expr;\n }\n\n #createDefaultExpression(key: Keys<T>) {\n const options = this.host.getColumn(key)?.sort;\n\n return {\n key,\n direction: 'ascending',\n caseSensitive: false,\n comparer: undefined,\n ...this.#resolveSortOptions(options),\n } as SortExpression<T>;\n }\n\n #orderBy(dir?: SortingDirection): SortingDirection {\n return this.#isTriStateSort\n ? dir === 'ascending'\n ? 'descending'\n : dir === 'descending'\n ? 'none'\n : 'ascending'\n : dir === 'ascending'\n ? 'descending'\n : 'ascending';\n }\n\n #emitSortingEvent(detail: SortExpression<T>) {\n return this.host.emitEvent('sorting', { detail, cancelable: true });\n }\n\n #emitSortedEvent(detail: SortExpression<T>) {\n return this.host.emitEvent('sorted', { detail });\n }\n\n #setExpression(expression: SortExpression<T>) {\n expression.direction === 'none'\n ? this.reset(expression.key)\n : this.state.set(expression.key, { ...expression });\n }\n\n public sortFromHeaderClick(column: ColumnConfiguration<T>, additive = false) {\n return this.sortFromUI(column, { additive });\n }\n\n /**\n * The one path for a sort the user starts from the UI (header click, column\n * menu): fires the cancelable `sorting` event, replaces the sort unless the\n * gesture is additive in multi-sort mode, announces, then fires `sorted`.\n * Without a `direction` the column steps to its next direction.\n */\n public async sortFromUI(\n column: ColumnConfiguration<T>,\n { direction, additive = false }: { direction?: SortingDirection; additive?: boolean } = {}\n ) {\n const expression = this.prepareExpression(column, direction);\n\n if (!this.#emitSortingEvent(expression)) {\n return;\n }\n\n // A plain click sorts by this column alone, clearing any other sorted\n // columns. Only a Ctrl/Cmd+click while multi-sort is enabled keeps the\n // existing sort keys and appends this column as an additional, lower-\n // priority sort. Without this gate every plain click would accumulate a\n // key, so a second column added behind a unique primary column would not\n // change the visible order at all.\n if (!this.#isMultipleSort || !additive) {\n this.reset();\n }\n\n // Capture visible-row rects keyed by data identity BEFORE the pipeline\n // runs. After the new render the virtualizer will have repositioned\n // rows; we use those captured rects as the \"First\" of a FLIP and\n // animate each row from its old viewport position back to identity.\n const beforeRects = this.#captureRowRects();\n\n this._sort(expression);\n\n // @ts-expect-error - protected member access\n await this.host.pipelineSettled();\n // Wait for body row updates so the new data is committed to each row\n // element before measuring the \"Last\" rect — otherwise the captured-\n // before and measured-after rects match and the animation no-ops.\n await awaitChildUpdates(this.host.rows);\n this.#playRowFlip(beforeRects);\n this.#announce(expression, column);\n this.#emitSortedEvent(expression);\n }\n\n #announce(expression: SortExpression<T>, column: ColumnConfiguration<T>) {\n const label = column.headerText ?? String(column.key);\n const message =\n expression.direction === 'none'\n ? this.host.localize('announce.sortCleared', { label })\n : this.host.localize(\n expression.direction === 'ascending'\n ? 'announce.sortedAscending'\n : 'announce.sortedDescending',\n { label }\n );\n this.host.announce(message);\n }\n\n #captureRowRects(): KeyedFlipEntry<T>[] {\n const entries: KeyedFlipEntry<T>[] = [];\n for (const row of this.host.rows) {\n const el = row as unknown as HTMLElement;\n entries.push({ key: row.data, rect: el.getBoundingClientRect() });\n }\n return entries;\n }\n\n #playRowFlip(before: ReadonlyArray<KeyedFlipEntry<T>>) {\n // After sort, the same data may be rendered by a different `<apex-grid-row>`\n // DOM element (virtualizer recycles rows). Resolve by data identity.\n const rows = this.host.rows;\n const byData = new Map<T, HTMLElement>();\n for (const row of rows) {\n byData.set(row.data, row as unknown as HTMLElement);\n }\n playKeyedFlip(before, (data) => byData.get(data) ?? null, 'y');\n }\n\n /**\n * The expression a UI sort would apply. Always a fresh object: the stored\n * entry is only replaced once the `sorting` event is not cancelled.\n */\n public prepareExpression(\n { key, sort: options }: ColumnConfiguration<T>,\n direction?: SortingDirection\n ): SortExpression<T> {\n const expr = this.state.get(key);\n\n return {\n ...(expr ?? this.#createDefaultExpression(key)),\n direction: direction ?? this.#orderBy(expr?.direction),\n ...this.#resolveSortOptions(options),\n } as SortExpression<T>;\n }\n\n /**\n * Whether the UI offers sorting on `column`: it declares `sort` and no\n * manual row order is active (sorting and a manual order are mutually\n * exclusive). The one rule for every sort affordance, built in or a module's.\n */\n public isSortable(column: ColumnConfiguration<T>): boolean {\n return Boolean(column.sort) && !this.#rowReorder?.hasManualOrder;\n }\n\n public reset(key?: Keys<T>) {\n key ? this.state.delete(key) : this.state.clear();\n }\n\n get #rowReorder() {\n // @ts-expect-error - protected member access\n return this.host.stateController.rowReorder as\n | { hasManualOrder: boolean; clearManualOrder(announce?: boolean): void }\n | undefined;\n }\n\n protected _sort(expressions: SortExpression<T> | SortExpression<T>[]) {\n const exprs = asArray(expressions);\n // Applying a sort is mutually exclusive with a manual row order (F5):\n // a real sort (any non-`none` direction) clears it, with an announcement.\n const reorder = this.#rowReorder;\n if (reorder?.hasManualOrder && exprs.some((expr) => expr.direction !== 'none')) {\n reorder.clearManualOrder(true);\n }\n\n for (const expr of exprs) {\n this.#setExpression(expr);\n }\n\n this.host.requestUpdate(PIPELINE);\n }\n\n public sort(expressions: SortExpression<T> | SortExpression<T>[]) {\n this._sort(\n asArray(expressions).map(\n (expr) =>\n ({\n ...(this.state.get(expr.key) ?? this.#createDefaultExpression(expr.key)),\n ...expr,\n }) as SortExpression<T>\n )\n );\n }\n\n public hostConnected() {}\n}\n"]}
1
+ {"version":3,"file":"sort.js","sourceRoot":"","sources":["../../src/controllers/sort.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAuB,aAAa,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAO5F,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAG/C,MAAM,OAAO,cAAc;IACzB,YAAsB,IAAiB;QAAjB,SAAI,GAAJ,IAAI,CAAa;QAIhC,UAAK,GAAiB,IAAI,GAAG,EAAE,CAAC;QAHrC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAID,IAAI,eAAe;QACjB,OAAO,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC;IAC9C,CAAC;IAED,IAAI,eAAe;QACjB,OAAO,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC;IAC9C,CAAC;IASD,mBAAmB,CAAC,OAA8C;QAChE,MAAM,IAAI,GAA+B,EAAE,CAAC;QAC5C,IAAI,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC3C,IAAI,OAAO,CAAC,aAAa,KAAK,SAAS;gBAAE,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;YACpF,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS;gBAAE,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAe,CAAC;QAC9E,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,wBAAwB,CAAC,GAAY;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC;QAE/C,OAAO;YACL,GAAG;YACH,SAAS,EAAE,WAAW;YACtB,aAAa,EAAE,KAAK;YACpB,QAAQ,EAAE,SAAS;YACnB,GAAG,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC;SAChB,CAAC;IACzB,CAAC;IAED,QAAQ,CAAC,GAAsB;QAC7B,OAAO,IAAI,CAAC,eAAe;YACzB,CAAC,CAAC,GAAG,KAAK,WAAW;gBACnB,CAAC,CAAC,YAAY;gBACd,CAAC,CAAC,GAAG,KAAK,YAAY;oBACpB,CAAC,CAAC,MAAM;oBACR,CAAC,CAAC,WAAW;YACjB,CAAC,CAAC,GAAG,KAAK,WAAW;gBACnB,CAAC,CAAC,YAAY;gBACd,CAAC,CAAC,WAAW,CAAC;IACpB,CAAC;IAED,iBAAiB,CAAC,MAAyB;QACzC,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;IACtE,CAAC;IAED,gBAAgB,CAAC,MAAyB;QACxC,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,cAAc,CAAC,UAA6B;QAC1C,UAAU,CAAC,SAAS,KAAK,MAAM;YAC7B,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;YAC5B,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE,GAAG,UAAU,EAAE,CAAC,CAAC;IACxD,CAAC;IAEM,mBAAmB,CAAC,MAA8B,EAAE,QAAQ,GAAG,KAAK;QACzE,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC/C,CAAC;IAQM,KAAK,CAAC,UAAU,CACrB,MAA8B,EAC9B,EAAE,SAAS,EAAE,QAAQ,GAAG,KAAK,KAA2D,EAAE;QAE1F,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAE7D,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,EAAE,CAAC;YACxC,OAAO;QACT,CAAC;QAQD,IAAI,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,QAAQ,EAAE,CAAC;YACvC,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC;QAMD,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAE5C,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAKvB,MAAM,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;QAC/B,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACnC,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;IACpC,CAAC;IAED,SAAS,CAAC,UAA6B,EAAE,MAA8B;QACrE,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACtD,MAAM,OAAO,GACX,UAAU,CAAC,SAAS,KAAK,MAAM;YAC7B,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,sBAAsB,EAAE,EAAE,KAAK,EAAE,CAAC;YACvD,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAChB,UAAU,CAAC,SAAS,KAAK,WAAW;gBAClC,CAAC,CAAC,0BAA0B;gBAC5B,CAAC,CAAC,2BAA2B,EAC/B,EAAE,KAAK,EAAE,CACV,CAAC;QACR,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAED,gBAAgB;QACd,MAAM,OAAO,GAAwB,EAAE,CAAC;QACxC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACjC,MAAM,EAAE,GAAG,GAA6B,CAAC;YACzC,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;QACpE,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,YAAY,CAAC,MAAwC;QAGnD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;QAC5B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;QACzC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,GAA6B,CAAC,CAAC;QACtD,CAAC;QACD,aAAa,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,GAAG,CAAC,CAAC;IACjE,CAAC;IAMM,iBAAiB,CACtB,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAA0B,EAC9C,SAA4B;QAE5B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEjC,OAAO;YACL,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,wBAAwB,CAAC,GAAG,CAAC,CAAC;YAC/C,SAAS,EAAE,SAAS,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;YACtD,GAAG,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC;SAChB,CAAC;IACzB,CAAC;IAOM,UAAU,CAAC,MAA8B;QAC9C,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC;IACnE,CAAC;IAEM,KAAK,CAAC,GAAa;QACxB,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACpD,CAAC;IAED,IAAI,WAAW;QAEb,OAAO,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,UAEpB,CAAC;IAChB,CAAC;IAES,KAAK,CAAC,WAAoD;QAClE,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;QAGnC,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC;QACjC,IAAI,OAAO,EAAE,cAAc,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,KAAK,MAAM,CAAC,EAAE,CAAC;YAC/E,OAAO,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACjC,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAEM,IAAI,CAAC,WAAoD;QAC9D,IAAI,CAAC,KAAK,CACR,OAAO,CAAC,WAAW,CAAC,CAAC,GAAG,CACtB,CAAC,IAAI,EAAE,EAAE,CACP,CAAC;YACC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACxE,GAAG,IAAI;SACR,CAAsB,CAC1B,CACF,CAAC;IACJ,CAAC;IAEM,aAAa,KAAI,CAAC;CAC1B","sourcesContent":["import type { ReactiveController } from 'lit';\nimport { PIPELINE } from '../internal/constants.js';\nimport { type KeyedFlipEntry, playKeyedFlip, settleRowsForFlip } from '../internal/flip.js';\nimport type {\n ColumnConfiguration,\n ColumnSortConfiguration,\n GridHost,\n Keys,\n} from '../internal/types.js';\nimport { asArray } from '../internal/utils.js';\nimport type { SortExpression, SortingDirection, SortState } from '../operations/sort/types.js';\n\nexport class SortController<T extends object> implements ReactiveController {\n constructor(protected host: GridHost<T>) {\n this.host.addController(this);\n }\n\n public state: SortState<T> = new Map();\n\n get #isMultipleSort() {\n return this.host.sortConfiguration.multiple;\n }\n\n get #isTriStateSort() {\n return this.host.sortConfiguration.triState;\n }\n\n /**\n * Only the options the column config actually sets, so a header click keeps a\n * comparer or caseSensitive that came in through `grid.sort()` when the column\n * itself does not define one. The trade-off: a comparer later removed from\n * the column config stays on the stored expression until that column's sort\n * is cleared, because its origin is not tracked.\n */\n #resolveSortOptions(options?: boolean | ColumnSortConfiguration<T>) {\n const expr: Partial<SortExpression<T>> = {};\n if (options && typeof options === 'object') {\n if (options.caseSensitive !== undefined) expr.caseSensitive = options.caseSensitive;\n if (options.comparer !== undefined) expr.comparer = options.comparer as any;\n }\n return expr;\n }\n\n #createDefaultExpression(key: Keys<T>) {\n const options = this.host.getColumn(key)?.sort;\n\n return {\n key,\n direction: 'ascending',\n caseSensitive: false,\n comparer: undefined,\n ...this.#resolveSortOptions(options),\n } as SortExpression<T>;\n }\n\n #orderBy(dir?: SortingDirection): SortingDirection {\n return this.#isTriStateSort\n ? dir === 'ascending'\n ? 'descending'\n : dir === 'descending'\n ? 'none'\n : 'ascending'\n : dir === 'ascending'\n ? 'descending'\n : 'ascending';\n }\n\n #emitSortingEvent(detail: SortExpression<T>) {\n return this.host.emitEvent('sorting', { detail, cancelable: true });\n }\n\n #emitSortedEvent(detail: SortExpression<T>) {\n return this.host.emitEvent('sorted', { detail });\n }\n\n #setExpression(expression: SortExpression<T>) {\n expression.direction === 'none'\n ? this.reset(expression.key)\n : this.state.set(expression.key, { ...expression });\n }\n\n public sortFromHeaderClick(column: ColumnConfiguration<T>, additive = false) {\n return this.sortFromUI(column, { additive });\n }\n\n /**\n * The one path for a sort the user starts from the UI (header click, column\n * menu): fires the cancelable `sorting` event, replaces the sort unless the\n * gesture is additive in multi-sort mode, announces, then fires `sorted`.\n * Without a `direction` the column steps to its next direction.\n */\n public async sortFromUI(\n column: ColumnConfiguration<T>,\n { direction, additive = false }: { direction?: SortingDirection; additive?: boolean } = {}\n ) {\n const expression = this.prepareExpression(column, direction);\n\n if (!this.#emitSortingEvent(expression)) {\n return;\n }\n\n // A plain click sorts by this column alone, clearing any other sorted\n // columns. Only a Ctrl/Cmd+click while multi-sort is enabled keeps the\n // existing sort keys and appends this column as an additional, lower-\n // priority sort. Without this gate every plain click would accumulate a\n // key, so a second column added behind a unique primary column would not\n // change the visible order at all.\n if (!this.#isMultipleSort || !additive) {\n this.reset();\n }\n\n // Capture visible-row rects keyed by data identity BEFORE the pipeline\n // runs. After the new render the virtualizer will have repositioned\n // rows; we use those captured rects as the \"First\" of a FLIP and\n // animate each row from its old viewport position back to identity.\n const beforeRects = this.#captureRowRects();\n\n this._sort(expression);\n\n // The rows must show the sorted data before the \"Last\" rects are\n // measured, otherwise the captured-before and measured-after rects match\n // and the animation no-ops.\n await settleRowsForFlip(this.host);\n this.#playRowFlip(beforeRects);\n this.#announce(expression, column);\n this.#emitSortedEvent(expression);\n }\n\n #announce(expression: SortExpression<T>, column: ColumnConfiguration<T>) {\n const label = column.headerText ?? String(column.key);\n const message =\n expression.direction === 'none'\n ? this.host.localize('announce.sortCleared', { label })\n : this.host.localize(\n expression.direction === 'ascending'\n ? 'announce.sortedAscending'\n : 'announce.sortedDescending',\n { label }\n );\n this.host.announce(message);\n }\n\n #captureRowRects(): KeyedFlipEntry<T>[] {\n const entries: KeyedFlipEntry<T>[] = [];\n for (const row of this.host.rows) {\n const el = row as unknown as HTMLElement;\n entries.push({ key: row.data, rect: el.getBoundingClientRect() });\n }\n return entries;\n }\n\n #playRowFlip(before: ReadonlyArray<KeyedFlipEntry<T>>) {\n // After sort, the same data may be rendered by a different `<apex-grid-row>`\n // DOM element (virtualizer recycles rows). Resolve by data identity.\n const rows = this.host.rows;\n const byData = new Map<T, HTMLElement>();\n for (const row of rows) {\n byData.set(row.data, row as unknown as HTMLElement);\n }\n playKeyedFlip(before, (data) => byData.get(data) ?? null, 'y');\n }\n\n /**\n * The expression a UI sort would apply. Always a fresh object: the stored\n * entry is only replaced once the `sorting` event is not cancelled.\n */\n public prepareExpression(\n { key, sort: options }: ColumnConfiguration<T>,\n direction?: SortingDirection\n ): SortExpression<T> {\n const expr = this.state.get(key);\n\n return {\n ...(expr ?? this.#createDefaultExpression(key)),\n direction: direction ?? this.#orderBy(expr?.direction),\n ...this.#resolveSortOptions(options),\n } as SortExpression<T>;\n }\n\n /**\n * Whether the UI offers sorting on `column`: it declares `sort` and no\n * manual row order is active (sorting and a manual order are mutually\n * exclusive). The one rule for every sort affordance, built in or a module's.\n */\n public isSortable(column: ColumnConfiguration<T>): boolean {\n return Boolean(column.sort) && !this.#rowReorder?.hasManualOrder;\n }\n\n public reset(key?: Keys<T>) {\n key ? this.state.delete(key) : this.state.clear();\n }\n\n get #rowReorder() {\n // @ts-expect-error - protected member access\n return this.host.stateController.rowReorder as\n | { hasManualOrder: boolean; clearManualOrder(announce?: boolean): void }\n | undefined;\n }\n\n protected _sort(expressions: SortExpression<T> | SortExpression<T>[]) {\n const exprs = asArray(expressions);\n // Applying a sort is mutually exclusive with a manual row order (F5):\n // a real sort (any non-`none` direction) clears it, with an announcement.\n const reorder = this.#rowReorder;\n if (reorder?.hasManualOrder && exprs.some((expr) => expr.direction !== 'none')) {\n reorder.clearManualOrder(true);\n }\n\n for (const expr of exprs) {\n this.#setExpression(expr);\n }\n\n this.host.requestUpdate(PIPELINE);\n }\n\n public sort(expressions: SortExpression<T> | SortExpression<T>[]) {\n this._sort(\n asArray(expressions).map(\n (expr) =>\n ({\n ...(this.state.get(expr.key) ?? this.#createDefaultExpression(expr.key)),\n ...expr,\n }) as SortExpression<T>\n )\n );\n }\n\n public hostConnected() {}\n}\n"]}
@@ -70,11 +70,6 @@ export declare class StateController<T extends object> implements ReactiveContro
70
70
  get scrollContainer(): import("../components/virtualizer.js").default;
71
71
  get paginator(): import("../index.js").ApexGridPaginator<T>;
72
72
  get toolbar(): import("../index.js").ApexGridToolbar<T>;
73
- /**
74
- * Cumulative pin offsets (in px) keyed by column key. Populated by the
75
- * {@link GridDOMController} after each layout.
76
- */
77
- get pinOffsets(): Map<unknown, number>;
78
73
  constructor(host: GridHost<T>, extraModules?: ReadonlyArray<GridFeatureModule<T>>);
79
74
  protected init(): void;
80
75
  /**
@@ -119,6 +114,19 @@ export declare class StateController<T extends object> implements ReactiveContro
119
114
  * the first non-null result, or `null` when no module owns the row.
120
115
  */
121
116
  presentRow(row: T, ctx: RowPresenterContext<T>): PresentedRow | null;
117
+ /**
118
+ * Columns in the `aria-colindex` sequence: the visible columns plus the
119
+ * selection and expansion chrome columns. Shared by `aria-colcount` and the
120
+ * cells that span a whole row.
121
+ */
122
+ get colCount(): number;
123
+ /**
124
+ * Whether rows carry tree semantics (`aria-level` / `aria-expanded`), which
125
+ * only a `treegrid` supports: the core tree, master-detail, or a feature
126
+ * module that reports {@link RowPresenter.hierarchical} (row grouping, a
127
+ * pivot tree, server-side groups). The grid role is read from here.
128
+ */
129
+ get hierarchical(): boolean;
122
130
  /**
123
131
  * Asks feature modules implementing {@link RowAriaProvider} for ARIA
124
132
  * tree/expansion semantics on a normal (cell-rendered) row — e.g. pivoting's
@@ -156,6 +164,11 @@ export declare class StateController<T extends object> implements ReactiveContro
156
164
  * for the community grid (no modules).
157
165
  */
158
166
  get hasColumnMenu(): boolean;
167
+ /**
168
+ * Whether any feature module implements {@link CellDecorator}, so a cell can
169
+ * skip decoration altogether when none does (the community grid).
170
+ */
171
+ get hasCellDecorators(): boolean;
159
172
  /**
160
173
  * Collects per-cell decoration contributed by feature modules implementing
161
174
  * {@link CellDecorator}, merging their attribute maps (later modules win on a
@@ -23,7 +23,7 @@ export class StateController {
23
23
  return;
24
24
  }
25
25
  this.#announceToken = (this.#announceToken + 1) % 2;
26
- this.announcement = this.#announceToken ? message : `${message} `;
26
+ this.announcement = this.#announceToken ? message : `${message}\u200B`;
27
27
  this.host.requestUpdate();
28
28
  }
29
29
  localize(key, params, fallback) {
@@ -50,10 +50,6 @@ export class StateController {
50
50
  get toolbar() {
51
51
  return this.host.toolbar;
52
52
  }
53
- get pinOffsets() {
54
- const dom = this.host.DOM;
55
- return dom?.pinOffsets ?? new Map();
56
- }
57
53
  constructor(host, extraModules = []) {
58
54
  this.host = host;
59
55
  this.extraModules = extraModules;
@@ -62,6 +58,7 @@ export class StateController {
62
58
  this.#announceToken = 0;
63
59
  this.#transformers = [];
64
60
  this.#synthesizers = [];
61
+ this.#hooks = new Map();
65
62
  this.#decorationVersion = 0;
66
63
  this.dataVersion = 0;
67
64
  this.host.addController(this);
@@ -99,6 +96,15 @@ export class StateController {
99
96
  }
100
97
  #transformers;
101
98
  #synthesizers;
99
+ #hooks;
100
+ #withHook(guard) {
101
+ let list = this.#hooks.get(guard);
102
+ if (!list) {
103
+ list = [...this.modules.values()].filter(guard);
104
+ this.#hooks.set(guard, list);
105
+ }
106
+ return list;
107
+ }
102
108
  module(id) {
103
109
  return this.modules.get(id);
104
110
  }
@@ -142,42 +148,49 @@ export class StateController {
142
148
  return out;
143
149
  }
144
150
  presentRow(row, ctx) {
145
- for (const controller of this.modules.values()) {
146
- if (isRowPresenter(controller)) {
147
- const presented = controller.presentRow(row, ctx);
148
- if (presented)
149
- return presented;
150
- }
151
+ for (const controller of this.#withHook((isRowPresenter))) {
152
+ const presented = controller.presentRow(row, ctx);
153
+ if (presented)
154
+ return presented;
151
155
  }
152
156
  return null;
153
157
  }
154
- describeRow(row, ctx) {
158
+ get colCount() {
159
+ return (this.host.columns.filter((column) => !column.hidden).length +
160
+ (this.selection.showCheckboxColumn ? 1 : 0) +
161
+ (this.expansion.showToggleColumn ? 1 : 0));
162
+ }
163
+ get hierarchical() {
164
+ if (this.tree.enabled || (this.expansion.enabled && this.host.expansion?.detailTemplate)) {
165
+ return true;
166
+ }
155
167
  for (const controller of this.modules.values()) {
156
- if (isRowAriaProvider(controller)) {
157
- const aria = controller.describeRow(row, ctx);
158
- if (aria)
159
- return aria;
160
- }
168
+ if (controller.hierarchical)
169
+ return true;
170
+ }
171
+ return false;
172
+ }
173
+ describeRow(row, ctx) {
174
+ for (const controller of this.#withHook((isRowAriaProvider))) {
175
+ const aria = controller.describeRow(row, ctx);
176
+ if (aria)
177
+ return aria;
161
178
  }
162
179
  return null;
163
180
  }
164
181
  rowCoordinate(row) {
165
- for (const controller of this.modules.values()) {
166
- if (isCoordinateLabeler(controller)) {
167
- const label = controller.rowCoordinate(row);
168
- if (label !== undefined)
169
- return label;
170
- }
182
+ for (const controller of this.#withHook((isCoordinateLabeler))) {
183
+ const label = controller.rowCoordinate(row);
184
+ if (label !== undefined)
185
+ return label;
171
186
  }
172
187
  return undefined;
173
188
  }
174
189
  columnCoordinate(key) {
175
- for (const controller of this.modules.values()) {
176
- if (isCoordinateLabeler(controller)) {
177
- const label = controller.columnCoordinate(key);
178
- if (label !== undefined)
179
- return label;
180
- }
190
+ for (const controller of this.#withHook((isCoordinateLabeler))) {
191
+ const label = controller.columnCoordinate(key);
192
+ if (label !== undefined)
193
+ return label;
181
194
  }
182
195
  return undefined;
183
196
  }
@@ -190,31 +203,25 @@ export class StateController {
190
203
  this.host.requestUpdate();
191
204
  }
192
205
  get hasColumnMenu() {
193
- for (const controller of this.modules.values()) {
194
- if (isColumnMenuProvider(controller) && controller.providesColumnMenu()) {
195
- return true;
196
- }
197
- }
198
- return false;
206
+ return this.#withHook(isColumnMenuProvider).some((controller) => controller.providesColumnMenu());
207
+ }
208
+ get hasCellDecorators() {
209
+ return this.#withHook(isCellDecorator).length > 0;
199
210
  }
200
211
  decorateCell(ctx) {
201
212
  let attributes = null;
202
- for (const controller of this.modules.values()) {
203
- if (isCellDecorator(controller)) {
204
- const decoration = controller.decorateCell(ctx);
205
- if (decoration) {
206
- attributes ??= {};
207
- Object.assign(attributes, decoration.attributes);
208
- }
213
+ for (const controller of this.#withHook((isCellDecorator))) {
214
+ const decoration = controller.decorateCell(ctx);
215
+ if (decoration) {
216
+ attributes ??= {};
217
+ Object.assign(attributes, decoration.attributes);
209
218
  }
210
219
  }
211
220
  return attributes ? { attributes } : null;
212
221
  }
213
222
  handleCellInteraction(interaction) {
214
- for (const controller of this.modules.values()) {
215
- if (isCellInteractionHandler(controller)) {
216
- controller.handleCellInteraction(interaction);
217
- }
223
+ for (const controller of this.#withHook((isCellInteractionHandler))) {
224
+ controller.handleCellInteraction(interaction);
218
225
  }
219
226
  }
220
227
  serializeModuleState() {