apex-grid 3.4.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.
- package/CHANGELOG.md +439 -0
- package/LICENSE.md +125 -0
- package/README.md +107 -14
- package/components/cell.d.ts +30 -4
- package/components/cell.js +199 -53
- package/components/cell.js.map +1 -1
- package/components/filter-row.d.ts +2 -0
- package/components/filter-row.js +82 -28
- package/components/filter-row.js.map +1 -1
- package/components/grid.d.ts +257 -20
- package/components/grid.js +463 -99
- package/components/grid.js.map +1 -1
- package/components/group-header-row.js +28 -25
- package/components/group-header-row.js.map +1 -1
- package/components/header-row.d.ts +7 -5
- package/components/header-row.js +19 -9
- package/components/header-row.js.map +1 -1
- package/components/header.js +36 -22
- package/components/header.js.map +1 -1
- package/components/row.d.ts +19 -3
- package/components/row.js +96 -22
- package/components/row.js.map +1 -1
- package/components/toolbar.js +2 -1
- package/components/toolbar.js.map +1 -1
- package/components/virtualizer.d.ts +45 -1
- package/components/virtualizer.js +184 -3
- package/components/virtualizer.js.map +1 -1
- package/controllers/dom.d.ts +4 -4
- package/controllers/dom.js +40 -22
- package/controllers/dom.js.map +1 -1
- package/controllers/editing.d.ts +104 -37
- package/controllers/editing.js +230 -78
- package/controllers/editing.js.map +1 -1
- package/controllers/expansion.d.ts +14 -5
- package/controllers/expansion.js +24 -1
- package/controllers/expansion.js.map +1 -1
- package/controllers/filter.d.ts +10 -2
- package/controllers/filter.js +62 -17
- package/controllers/filter.js.map +1 -1
- package/controllers/history.d.ts +45 -8
- package/controllers/history.js +58 -20
- package/controllers/history.js.map +1 -1
- package/controllers/navigation.d.ts +68 -13
- package/controllers/navigation.js +196 -51
- package/controllers/navigation.js.map +1 -1
- package/controllers/pagination.d.ts +6 -0
- package/controllers/pagination.js +10 -2
- package/controllers/pagination.js.map +1 -1
- package/controllers/reorder.js +42 -24
- package/controllers/reorder.js.map +1 -1
- package/controllers/resize.d.ts +17 -2
- package/controllers/resize.js +16 -6
- package/controllers/resize.js.map +1 -1
- package/controllers/row-pin.d.ts +20 -2
- package/controllers/row-pin.js +16 -9
- package/controllers/row-pin.js.map +1 -1
- package/controllers/row-reorder.d.ts +31 -11
- package/controllers/row-reorder.js +117 -37
- package/controllers/row-reorder.js.map +1 -1
- package/controllers/selection.d.ts +11 -4
- package/controllers/selection.js +26 -11
- package/controllers/selection.js.map +1 -1
- package/controllers/sort.d.ts +22 -2
- package/controllers/sort.js +29 -23
- package/controllers/sort.js.map +1 -1
- package/controllers/state.d.ts +65 -10
- package/controllers/state.js +86 -6
- package/controllers/state.js.map +1 -1
- package/controllers/tree.d.ts +15 -10
- package/controllers/tree.js +0 -0
- package/controllers/tree.js.map +1 -1
- package/custom-elements.json +3589 -452
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/highlight.css +15 -8
- package/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/ApexGrid.html +660 -0
- package/docs/classes/ApexGridPaginator.html +36 -0
- package/docs/classes/ApexGridToolbar.html +67 -0
- package/docs/functions/localize.html +2 -2
- package/docs/functions/setup.html +1 -2
- package/docs/hierarchy.html +1 -1
- package/docs/index.html +105 -15
- package/docs/interfaces/ApexCellValidationFailedEvent.html +3 -2
- package/docs/interfaces/ApexCellValueChangedEvent.html +4 -2
- package/docs/interfaces/ApexCellValueChangingEvent.html +3 -2
- package/docs/interfaces/ApexColumnMovedEvent.html +2 -2
- package/docs/interfaces/ApexColumnMovingEvent.html +1 -1
- package/docs/interfaces/ApexDetailContext.html +2 -2
- package/docs/interfaces/ApexGridEventMap.html +9 -6
- package/docs/interfaces/ApexRowEditEndedEvent.html +2 -1
- package/docs/interfaces/ApexRowEditStartedEvent.html +2 -1
- package/docs/interfaces/ApexRowMovingEvent.html +9 -3
- package/docs/interfaces/ApexRowPinningEvent.html +1 -1
- package/docs/interfaces/ApexTreeRowExpandedEvent.html +4 -1
- package/docs/interfaces/ApexTreeRowExpandingEvent.html +4 -1
- package/docs/interfaces/BaseApexCellContext.html +1 -1
- package/docs/interfaces/BaseApexEditorContext.html +1 -1
- package/docs/interfaces/BaseColumnConfiguration.html +3 -3
- package/docs/interfaces/BaseFilterExpression.html +2 -2
- package/docs/interfaces/CSVExportOptions.html +13 -5
- package/docs/interfaces/DataPipelineConfiguration.html +2 -2
- package/docs/interfaces/EditingHistoryConfiguration.html +1 -1
- package/docs/interfaces/ExportFormat.html +2 -2
- package/docs/interfaces/ExportOptions.html +5 -3
- package/docs/interfaces/GridExpansionConfiguration.html +6 -2
- package/docs/interfaces/GridRowPinningConfiguration.html +1 -1
- package/docs/interfaces/GridRowReorderingConfiguration.html +2 -2
- package/docs/interfaces/PaginationConfiguration.html +1 -1
- package/docs/interfaces/PaginationState.html +1 -1
- package/docs/interfaces/SetStateResult.html +3 -2
- package/docs/interfaces/ValidatorContext.html +7 -2
- package/docs/media/_token-values.scss +118 -0
- package/docs/media/package.json +5 -5
- package/docs/media/state-persistence.html +0 -10
- package/docs/modules.html +1 -1
- package/docs/types/DataPipelineParams.html +2 -2
- package/docs/types/EditMode.html +2 -2
- package/docs/types/ExportSource.html +6 -4
- package/docs/types/GridLocaleText.html +1 -1
- package/docs/types/OperandKeys.html +9 -0
- package/docs/types/PaginationMode.html +1 -1
- package/docs/variables/EN_LOCALE.html +3 -3
- package/docs/variables/esLocale.html +1 -1
- package/i18n/en.d.ts +19 -0
- package/i18n/en.js +19 -0
- package/i18n/en.js.map +1 -1
- package/i18n/es.js +19 -0
- package/i18n/es.js.map +1 -1
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/index.js.map +1 -1
- package/internal/column-types.d.ts +27 -1
- package/internal/column-types.js +75 -49
- package/internal/column-types.js.map +1 -1
- package/internal/constants.d.ts +1 -2
- package/internal/constants.js +0 -4
- package/internal/constants.js.map +1 -1
- package/internal/document-styles.d.ts +30 -0
- package/internal/document-styles.js +48 -0
- package/internal/document-styles.js.map +1 -0
- package/internal/export.d.ts +39 -7
- package/internal/export.js +35 -34
- package/internal/export.js.map +1 -1
- package/internal/feature-module.d.ts +82 -3
- package/internal/feature-module.js +11 -0
- package/internal/feature-module.js.map +1 -1
- package/internal/index.d.ts +6 -4
- package/internal/index.js +5 -3
- package/internal/index.js.map +1 -1
- package/internal/normalize-case.d.ts +8 -1
- package/internal/normalize-case.js +3 -2
- package/internal/normalize-case.js.map +1 -1
- package/internal/state-snapshot.d.ts +60 -8
- package/internal/state-snapshot.js +87 -19
- package/internal/state-snapshot.js.map +1 -1
- package/internal/types.d.ts +17 -2
- package/internal/types.js.map +1 -1
- package/internal/utils.d.ts +48 -6
- package/internal/utils.js +35 -21
- package/internal/utils.js.map +1 -1
- package/internal/validators.js +1 -0
- package/internal/validators.js.map +1 -1
- package/operations/filter/operands/number.js +9 -6
- package/operations/filter/operands/number.js.map +1 -1
- package/operations/filter/operands/string.js +2 -3
- package/operations/filter/operands/string.js.map +1 -1
- package/operations/filter/types.d.ts +13 -1
- package/operations/filter/types.js.map +1 -1
- package/operations/filter.d.ts +6 -0
- package/operations/filter.js +23 -7
- package/operations/filter.js.map +1 -1
- package/operations/quick-filter.js +2 -5
- package/operations/quick-filter.js.map +1 -1
- package/operations/sort.d.ts +21 -2
- package/operations/sort.js +41 -14
- package/operations/sort.js.map +1 -1
- package/package.json +8 -4
- package/setup.d.ts +23 -11
- package/setup.js +19 -21
- package/setup.js.map +1 -1
- package/styles/_common.css.js +1 -1
- package/styles/_common.css.js.map +1 -1
- package/styles/_token-values.css.d.ts +1 -0
- package/styles/_token-values.css.js +3 -0
- package/styles/_token-values.css.js.map +1 -0
- package/styles/_tokens.css.js +1 -1
- package/styles/_tokens.css.js.map +1 -1
- package/styles/body-cell/body-cell.css.js +1 -1
- package/styles/body-cell/body-cell.css.js.map +1 -1
- package/styles/body-cell/cell-renderers.css.js +1 -1
- package/styles/body-cell/cell-renderers.css.js.map +1 -1
- package/styles/body-row/body-row.css.js +1 -1
- package/styles/body-row/body-row.css.js.map +1 -1
- package/styles/document-tokens.css.d.ts +1 -0
- package/styles/document-tokens.css.js +3 -0
- package/styles/document-tokens.css.js.map +1 -0
- package/styles/filter-row/filter-row.css.js +1 -1
- package/styles/filter-row/filter-row.css.js.map +1 -1
- package/styles/grid/_grid.base.css.js +1 -1
- package/styles/grid/_grid.base.css.js.map +1 -1
- package/styles/grid/grid.css.js +1 -1
- package/styles/grid/grid.css.js.map +1 -1
- package/styles/group-header-row/group-header-row.css.js +1 -1
- package/styles/group-header-row/group-header-row.css.js.map +1 -1
- package/styles/header-cell/header-cell.css.js +1 -1
- package/styles/header-cell/header-cell.css.js.map +1 -1
- package/styles/header-row/header-row.base.css.js +1 -1
- package/styles/header-row/header-row.base.css.js.map +1 -1
- package/styles/paginator/paginator.css.js +1 -1
- package/styles/paginator/paginator.css.js.map +1 -1
- package/styles/toolbar/toolbar.css.js +1 -1
- package/styles/toolbar/toolbar.css.js.map +1 -1
- package/tokens.css +78 -0
- package/LICENSE +0 -80
- package/docs/media/LICENSE +0 -80
- package/docs/media/_tokens.scss +0 -193
package/components/grid.js
CHANGED
|
@@ -10,16 +10,18 @@ import { eventOptions, property, query, queryAll, state } from 'lit/decorators.j
|
|
|
10
10
|
import { styleMap } from 'lit/directives/style-map.js';
|
|
11
11
|
import { DataOperationsController } from '../controllers/data-operation.js';
|
|
12
12
|
import { GridDOMController } from '../controllers/dom.js';
|
|
13
|
+
import { viewIndexer } from '../controllers/history.js';
|
|
13
14
|
import { gridStateContext, StateController } from '../controllers/state.js';
|
|
14
15
|
import { localize as resolveLocaleText, } from '../i18n/index.js';
|
|
15
16
|
import { DEFAULT_COLUMN_CONFIG, PIPELINE, SENTINEL_NODE } from '../internal/constants.js';
|
|
17
|
+
import { adoptGridDocumentStyles } from '../internal/document-styles.js';
|
|
16
18
|
import { buildCSV, downloadBlob, } from '../internal/export.js';
|
|
17
19
|
import { EventEmitterBase } from '../internal/mixins/event-emitter.js';
|
|
18
20
|
import { registerComponent } from '../internal/register.js';
|
|
19
21
|
import { columnSchema, operandsByType } from '../internal/state-schema.js';
|
|
20
|
-
import { applyColumnLayout, deserializeFilter, resolveRowRefs, serializeColumnLayout, serializeFilter, serializeRowRefs, serializeSort, } from '../internal/state-snapshot.js';
|
|
22
|
+
import { applyColumnLayout, deserializeFilter, resolveFilterOperand, resolveRowRefs, SET_STATE_SLICES, serializeColumnLayout, serializeFilter, serializeRowRefs, serializeSort, } from '../internal/state-snapshot.js';
|
|
21
23
|
import { GRID_TAG } from '../internal/tags.js';
|
|
22
|
-
import { asArray, autoGenerateColumns, getDisplayColumns
|
|
24
|
+
import { asArray, autoGenerateColumns, getDisplayColumns } from '../internal/utils.js';
|
|
23
25
|
import { watch } from '../internal/watch.js';
|
|
24
26
|
import { styles as gridStyles } from '../styles/grid/grid.css.js';
|
|
25
27
|
import ApexGridCell from './cell.js';
|
|
@@ -30,19 +32,33 @@ import ApexGridPaginator from './paginator.js';
|
|
|
30
32
|
import ApexGridRow from './row.js';
|
|
31
33
|
import ApexGridToolbar from './toolbar.js';
|
|
32
34
|
import ApexVirtualizer from './virtualizer.js';
|
|
33
|
-
const
|
|
35
|
+
const SORT_DIRECTIONS = ['ascending', 'descending', 'none'];
|
|
36
|
+
const VIEW_SHAPING_SLICES = new Set([
|
|
34
37
|
'columns',
|
|
35
|
-
'sort',
|
|
36
38
|
'filter',
|
|
37
39
|
'quickFilter',
|
|
38
40
|
'modules',
|
|
39
|
-
'rowOrder',
|
|
40
41
|
'tree',
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
'
|
|
44
|
-
'
|
|
45
|
-
]
|
|
42
|
+
]);
|
|
43
|
+
const VIEW_SHAPING_PROPERTIES = new Map([
|
|
44
|
+
['data', null],
|
|
45
|
+
['quickFilter', null],
|
|
46
|
+
['pagination', ['enabled', 'mode', 'pageSize', 'totalItems']],
|
|
47
|
+
['tree', ['enabled', 'getDataPath', 'defaultExpanded']],
|
|
48
|
+
['dataPipelineConfiguration', ['sort', 'filter', 'pagination', 'quickFilter']],
|
|
49
|
+
]);
|
|
50
|
+
function viewShapeChanged(fields, before, after) {
|
|
51
|
+
if (Object.is(before, after))
|
|
52
|
+
return false;
|
|
53
|
+
if (!fields)
|
|
54
|
+
return true;
|
|
55
|
+
const a = (before ?? {});
|
|
56
|
+
const b = (after ?? {});
|
|
57
|
+
return fields.some((field) => !Object.is(a[field], b[field]));
|
|
58
|
+
}
|
|
59
|
+
function warnDropped(message) {
|
|
60
|
+
console.warn(`[apex-grid] ${message}`);
|
|
61
|
+
}
|
|
46
62
|
export class ApexGrid extends EventEmitterBase {
|
|
47
63
|
constructor() {
|
|
48
64
|
super(...arguments);
|
|
@@ -55,21 +71,27 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
55
71
|
});
|
|
56
72
|
this.dataState = [];
|
|
57
73
|
this.columns = [];
|
|
58
|
-
this
|
|
74
|
+
this.#data = [];
|
|
59
75
|
this.autoGenerate = false;
|
|
60
76
|
this.sortConfiguration = {
|
|
61
77
|
multiple: true,
|
|
62
78
|
triState: true,
|
|
63
79
|
};
|
|
64
|
-
this
|
|
80
|
+
this.#quickFilter = '';
|
|
81
|
+
this.#quickFilterWrites = 0;
|
|
65
82
|
this.showQuickFilter = false;
|
|
66
83
|
this.showExport = false;
|
|
67
84
|
this.columnReordering = false;
|
|
68
85
|
this.columnSeparator = true;
|
|
69
86
|
this.coordinateHints = false;
|
|
70
87
|
this.columnMenu = true;
|
|
88
|
+
this.#viewChangePending = false;
|
|
89
|
+
this.#shapingPending = false;
|
|
90
|
+
this.#pipelineRun = 0;
|
|
91
|
+
this.#pipelineSettled = Promise.resolve();
|
|
92
|
+
this.#settlePipeline = null;
|
|
71
93
|
this.#autoAriaLabel = null;
|
|
72
|
-
this.#stateChangedListeners =
|
|
94
|
+
this.#stateChangedListeners = new Map();
|
|
73
95
|
this.#lastStateJson = null;
|
|
74
96
|
}
|
|
75
97
|
static get tagName() {
|
|
@@ -82,6 +104,24 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
82
104
|
createStateController() {
|
|
83
105
|
return new StateController(this);
|
|
84
106
|
}
|
|
107
|
+
#data;
|
|
108
|
+
get data() {
|
|
109
|
+
return this.#data;
|
|
110
|
+
}
|
|
111
|
+
set data(value) {
|
|
112
|
+
if (value == null && !this.#data.length)
|
|
113
|
+
return;
|
|
114
|
+
this.#data = value ?? [];
|
|
115
|
+
}
|
|
116
|
+
get quickFilter() {
|
|
117
|
+
return this.#quickFilter;
|
|
118
|
+
}
|
|
119
|
+
set quickFilter(value) {
|
|
120
|
+
this.#quickFilter = value;
|
|
121
|
+
this.#quickFilterWrites++;
|
|
122
|
+
}
|
|
123
|
+
#quickFilter;
|
|
124
|
+
#quickFilterWrites;
|
|
85
125
|
localize(key, params, fallback) {
|
|
86
126
|
return resolveLocaleText(this.localeText, key, params, fallback);
|
|
87
127
|
}
|
|
@@ -160,26 +200,50 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
160
200
|
isTreeRowExpanded(row) {
|
|
161
201
|
return this.stateController.tree.isExpanded(row);
|
|
162
202
|
}
|
|
203
|
+
#pendingSort;
|
|
204
|
+
#pendingFilter;
|
|
163
205
|
set sortExpressions(expressions) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
206
|
+
this.#pendingSort = [...(expressions ?? [])];
|
|
207
|
+
this.requestUpdate(PIPELINE);
|
|
167
208
|
}
|
|
168
209
|
get sortExpressions() {
|
|
210
|
+
if (this.#pendingSort)
|
|
211
|
+
return [...this.#pendingSort];
|
|
169
212
|
return Array.from(this.stateController.sorting.state.values());
|
|
170
213
|
}
|
|
171
214
|
set filterExpressions(expressions) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
215
|
+
this.#pendingFilter = [...(expressions ?? [])];
|
|
216
|
+
this.#markViewChange();
|
|
175
217
|
}
|
|
176
218
|
get filterExpressions() {
|
|
219
|
+
if (this.#pendingFilter) {
|
|
220
|
+
return this.#pendingFilter.map((each) => {
|
|
221
|
+
const resolved = this.#resolveFilter(each);
|
|
222
|
+
return typeof resolved === 'string' ? each : resolved;
|
|
223
|
+
});
|
|
224
|
+
}
|
|
177
225
|
const expressions = [];
|
|
178
226
|
for (const each of this.stateController.filtering.state.values) {
|
|
179
227
|
expressions.push(...each.all);
|
|
180
228
|
}
|
|
181
229
|
return expressions;
|
|
182
230
|
}
|
|
231
|
+
#resolveFilter(each) {
|
|
232
|
+
const column = each ? this.getColumn(each.key) : undefined;
|
|
233
|
+
if (!column)
|
|
234
|
+
return `filter: unknown column "${String(each?.key)}"; dropped`;
|
|
235
|
+
const defaults = this.stateController.filtering.getDefaultExpression(column);
|
|
236
|
+
const named = each.condition ?? defaults.condition;
|
|
237
|
+
const condition = typeof named === 'string' ? resolveFilterOperand(column, named) : named;
|
|
238
|
+
if (!condition)
|
|
239
|
+
return `filter: unknown operand "${named}" for "${String(each.key)}"; dropped`;
|
|
240
|
+
return {
|
|
241
|
+
...defaults,
|
|
242
|
+
...each,
|
|
243
|
+
condition,
|
|
244
|
+
criteria: each.criteria ?? 'and',
|
|
245
|
+
};
|
|
246
|
+
}
|
|
183
247
|
get rows() {
|
|
184
248
|
return Array.from(this._rows);
|
|
185
249
|
}
|
|
@@ -197,7 +261,7 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
197
261
|
}
|
|
198
262
|
get pageItems() {
|
|
199
263
|
const rowPin = this.stateController.rowPin;
|
|
200
|
-
const base = rowPin?.
|
|
264
|
+
const base = rowPin?.hasPinnedRows
|
|
201
265
|
? this.dataState.filter((row) => !rowPin.isPinned(row))
|
|
202
266
|
: this.dataState;
|
|
203
267
|
if (!this.stateController.pagination.enabled)
|
|
@@ -214,7 +278,33 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
214
278
|
return this.stateController.pagination.page;
|
|
215
279
|
}
|
|
216
280
|
set page(value) {
|
|
217
|
-
this
|
|
281
|
+
if (this.#viewStale) {
|
|
282
|
+
this.stateController.pagination.page = Number.isFinite(value)
|
|
283
|
+
? Math.max(0, Math.trunc(value))
|
|
284
|
+
: 0;
|
|
285
|
+
this.requestUpdate(PIPELINE);
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
this.stateController.pagination.gotoPage(value);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
#viewChangePending;
|
|
292
|
+
#shapingPending;
|
|
293
|
+
get #viewStale() {
|
|
294
|
+
return !this.hasUpdated || this.#viewChangePending || this.#shapingPending;
|
|
295
|
+
}
|
|
296
|
+
#markViewChange() {
|
|
297
|
+
this.#viewChangePending = true;
|
|
298
|
+
this.requestUpdate(PIPELINE);
|
|
299
|
+
}
|
|
300
|
+
requestUpdate(name, oldValue, options, useNewValue, newValue) {
|
|
301
|
+
const fields = name === undefined ? undefined : VIEW_SHAPING_PROPERTIES.get(name);
|
|
302
|
+
if (this.hasUpdated &&
|
|
303
|
+
fields !== undefined &&
|
|
304
|
+
viewShapeChanged(fields, oldValue, useNewValue ? newValue : this[name])) {
|
|
305
|
+
this.#viewChangePending = true;
|
|
306
|
+
}
|
|
307
|
+
super.requestUpdate(name, oldValue, options, useNewValue, newValue);
|
|
218
308
|
}
|
|
219
309
|
get pageSize() {
|
|
220
310
|
return this.stateController.pagination.pageSize;
|
|
@@ -228,21 +318,31 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
228
318
|
watchColumns(_, newConfig = []) {
|
|
229
319
|
this.columns = newConfig.map((config) => ({ ...DEFAULT_COLUMN_CONFIG, ...config }));
|
|
230
320
|
}
|
|
321
|
+
historyRecords() {
|
|
322
|
+
return this.data;
|
|
323
|
+
}
|
|
231
324
|
dataChanged() {
|
|
232
325
|
let next = [...this.data];
|
|
233
326
|
autoGenerateColumns(this);
|
|
327
|
+
this.stateController?.history?.reconcile(() => this.historyRecords());
|
|
234
328
|
this.stateController?.tree?.resetForDataChange();
|
|
329
|
+
const carry = (rows) => this.#carryRows(rows);
|
|
330
|
+
this.stateController?.rowReorder.resetForDataChange(carry);
|
|
331
|
+
this.stateController?.rowPin.resetForDataChange(carry);
|
|
235
332
|
if (this.stateController?.tree?.enabled) {
|
|
236
333
|
next = this.stateController.tree.process(next);
|
|
237
334
|
}
|
|
238
335
|
this.dataState = this.stateController ? this.stateController.applyModuleTransforms(next) : next;
|
|
239
|
-
if (this.hasUpdated)
|
|
240
|
-
this.
|
|
241
|
-
}
|
|
336
|
+
if (this.hasUpdated)
|
|
337
|
+
this.requestUpdate(PIPELINE);
|
|
242
338
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
this.
|
|
339
|
+
#carryRows(rows) {
|
|
340
|
+
const rowId = this.rowId;
|
|
341
|
+
const records = [...this.historyRecords()];
|
|
342
|
+
if (rowId)
|
|
343
|
+
return resolveRowRefs(serializeRowRefs(rows, [], rowId), records, rowId);
|
|
344
|
+
const present = new Set(records);
|
|
345
|
+
return rows.filter((row) => present.has(row));
|
|
246
346
|
}
|
|
247
347
|
paginationChanged() {
|
|
248
348
|
const ctrl = this.stateController?.pagination;
|
|
@@ -255,16 +355,103 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
255
355
|
if (typeof next.page === 'number') {
|
|
256
356
|
ctrl.page = next.page;
|
|
257
357
|
}
|
|
258
|
-
if (this.hasUpdated)
|
|
259
|
-
this.
|
|
260
|
-
}
|
|
358
|
+
if (this.hasUpdated)
|
|
359
|
+
this.requestUpdate(PIPELINE);
|
|
261
360
|
}
|
|
262
361
|
quickFilterChanged() {
|
|
263
|
-
if (this.hasUpdated)
|
|
264
|
-
this.
|
|
362
|
+
if (this.hasUpdated || this.quickFilter)
|
|
363
|
+
this.requestUpdate(PIPELINE);
|
|
364
|
+
}
|
|
365
|
+
treeChanged(previous) {
|
|
366
|
+
if (!viewShapeChanged(VIEW_SHAPING_PROPERTIES.get('tree'), previous, this.tree))
|
|
367
|
+
return;
|
|
368
|
+
this.stateController.tree.resetForDataChange();
|
|
369
|
+
this.requestUpdate(PIPELINE);
|
|
370
|
+
}
|
|
371
|
+
#pipelineRun;
|
|
372
|
+
#pipelineSettled;
|
|
373
|
+
#settlePipeline;
|
|
374
|
+
pipeline() {
|
|
375
|
+
if (!this.#settlePipeline) {
|
|
376
|
+
this.#pipelineSettled = new Promise((settle) => {
|
|
377
|
+
this.#settlePipeline = settle;
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
return this.#runPipeline(++this.#pipelineRun);
|
|
381
|
+
}
|
|
382
|
+
async #runPipeline(run) {
|
|
383
|
+
try {
|
|
384
|
+
this.#flushPendingExpressions();
|
|
385
|
+
if (this.#viewChangePending || !this.hasUpdated)
|
|
386
|
+
this.#shapingPending = true;
|
|
387
|
+
this.#viewChangePending = false;
|
|
388
|
+
const next = await this.dataController.apply([...this.data], this.stateController);
|
|
389
|
+
if (run !== this.#pipelineRun)
|
|
390
|
+
return;
|
|
391
|
+
this.dataState = next;
|
|
392
|
+
this.stateController.dataVersion++;
|
|
393
|
+
this.stateController.pagination.reclamp();
|
|
394
|
+
}
|
|
395
|
+
finally {
|
|
396
|
+
if (run === this.#pipelineRun) {
|
|
397
|
+
this.#settlePipeline?.();
|
|
398
|
+
this.#settlePipeline = null;
|
|
399
|
+
if (this.#shapingPending) {
|
|
400
|
+
this.#shapingPending = false;
|
|
401
|
+
this.#baselineStateIfUnset();
|
|
402
|
+
}
|
|
403
|
+
}
|
|
265
404
|
}
|
|
266
405
|
}
|
|
406
|
+
async pipelineSettled() {
|
|
407
|
+
await this.updateComplete;
|
|
408
|
+
let settled;
|
|
409
|
+
do {
|
|
410
|
+
settled = this.#pipelineSettled;
|
|
411
|
+
await settled;
|
|
412
|
+
await this.updateComplete;
|
|
413
|
+
} while (settled !== this.#pipelineSettled);
|
|
414
|
+
}
|
|
415
|
+
#flushPendingExpressions() {
|
|
416
|
+
const sort = this.#pendingSort;
|
|
417
|
+
const filter = this.#pendingFilter;
|
|
418
|
+
this.#pendingSort = undefined;
|
|
419
|
+
this.#pendingFilter = undefined;
|
|
420
|
+
if (sort)
|
|
421
|
+
this.#replaceSort(sort, warnDropped);
|
|
422
|
+
if (filter)
|
|
423
|
+
this.#replaceFilter(filter);
|
|
424
|
+
}
|
|
425
|
+
#replaceSort(entries, warn) {
|
|
426
|
+
this.stateController.sorting.reset();
|
|
427
|
+
const valid = [];
|
|
428
|
+
for (const entry of entries) {
|
|
429
|
+
const { direction, ...rest } = (entry ?? {});
|
|
430
|
+
if (typeof entry !== 'object' || entry === null || !this.getColumn(rest.key)) {
|
|
431
|
+
warn(`sort: unknown column "${String(rest.key)}"; dropped`);
|
|
432
|
+
}
|
|
433
|
+
else if (direction === undefined) {
|
|
434
|
+
valid.push(rest);
|
|
435
|
+
}
|
|
436
|
+
else if (SORT_DIRECTIONS.includes(direction)) {
|
|
437
|
+
valid.push(entry);
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
warn(`sort: invalid direction "${String(direction)}"; dropped`);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
if (valid.length)
|
|
444
|
+
this.sort(valid);
|
|
445
|
+
this.requestUpdate(PIPELINE);
|
|
446
|
+
}
|
|
447
|
+
#replaceFilter(entries) {
|
|
448
|
+
this.#markViewChange();
|
|
449
|
+
this.stateController.filtering.reset();
|
|
450
|
+
if (entries.length)
|
|
451
|
+
this.filter([...entries]);
|
|
452
|
+
}
|
|
267
453
|
updated() {
|
|
454
|
+
this.#viewChangePending = false;
|
|
268
455
|
this.setAttribute('role', this.stateController.tree.enabled ? 'treegrid' : 'grid');
|
|
269
456
|
const pinned = this.stateController.rowPin.pinnedRows;
|
|
270
457
|
this.setAttribute('aria-rowcount', String(this.headerRowCount + pinned.top.length + this.pageItems.length + pinned.bottom.length));
|
|
@@ -279,9 +466,7 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
279
466
|
else {
|
|
280
467
|
this.removeAttribute('aria-multiselectable');
|
|
281
468
|
}
|
|
282
|
-
|
|
283
|
-
this.scrollContainer.tabIndex = this.stateController.active === SENTINEL_NODE ? 0 : -1;
|
|
284
|
-
}
|
|
469
|
+
this.#syncBodyTabStop();
|
|
285
470
|
const currentLabel = this.getAttribute('aria-label');
|
|
286
471
|
const authorLabeled = this.hasAttribute('aria-labelledby') ||
|
|
287
472
|
(currentLabel !== null && currentLabel !== this.#autoAriaLabel);
|
|
@@ -297,21 +482,54 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
297
482
|
#stateChangedListeners;
|
|
298
483
|
#lastStateJson;
|
|
299
484
|
addEventListener(type, listener, options) {
|
|
300
|
-
if (type === 'stateChanged')
|
|
301
|
-
this.#
|
|
302
|
-
this.#lastStateJson = this.hasUpdated ? JSON.stringify(this.getState()) : null;
|
|
303
|
-
}
|
|
485
|
+
if (type === 'stateChanged' && listener)
|
|
486
|
+
this.#trackStateChangedListener(listener, options);
|
|
304
487
|
super.addEventListener(type, listener, options);
|
|
305
488
|
}
|
|
489
|
+
#trackStateChangedListener(listener, options) {
|
|
490
|
+
const capture = typeof options === 'boolean' ? options : Boolean(options?.capture);
|
|
491
|
+
const captures = this.#stateChangedListeners.get(listener) ?? new Map();
|
|
492
|
+
if (!captures.has(capture) && !options?.signal?.aborted) {
|
|
493
|
+
if (!this.#stateChangedListeners.size) {
|
|
494
|
+
this.#lastStateJson = this.hasUpdated ? JSON.stringify(this.getState()) : null;
|
|
495
|
+
}
|
|
496
|
+
const token = {};
|
|
497
|
+
captures.set(capture, token);
|
|
498
|
+
this.#stateChangedListeners.set(listener, captures);
|
|
499
|
+
const forget = () => this.#forgetStateChangedListener(listener, capture, token);
|
|
500
|
+
if (typeof options === 'object') {
|
|
501
|
+
if (options.once) {
|
|
502
|
+
HTMLElement.prototype.addEventListener.call(this, 'stateChanged', forget, {
|
|
503
|
+
capture,
|
|
504
|
+
once: true,
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
options.signal?.addEventListener('abort', forget, { once: true });
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
#forgetStateChangedListener(listener, capture, token) {
|
|
512
|
+
const captures = this.#stateChangedListeners.get(listener);
|
|
513
|
+
if (!captures || (token && captures.get(capture) !== token))
|
|
514
|
+
return;
|
|
515
|
+
captures.delete(capture);
|
|
516
|
+
if (!captures.size)
|
|
517
|
+
this.#stateChangedListeners.delete(listener);
|
|
518
|
+
}
|
|
306
519
|
removeEventListener(type, listener, options) {
|
|
307
|
-
if (type === 'stateChanged') {
|
|
308
|
-
|
|
520
|
+
if (type === 'stateChanged' && listener) {
|
|
521
|
+
const capture = typeof options === 'boolean' ? options : Boolean(options?.capture);
|
|
522
|
+
this.#forgetStateChangedListener(listener, capture);
|
|
309
523
|
}
|
|
310
524
|
super.removeEventListener(type, listener, options);
|
|
311
525
|
}
|
|
312
526
|
#emitStateChangedIfNeeded() {
|
|
313
|
-
if (this.#stateChangedListeners
|
|
527
|
+
if (!this.#stateChangedListeners.size)
|
|
314
528
|
return;
|
|
529
|
+
if (this.#lastStateJson === null) {
|
|
530
|
+
this.#baselineStateIfUnset();
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
315
533
|
const snapshot = this.getState();
|
|
316
534
|
const json = JSON.stringify(snapshot);
|
|
317
535
|
if (json === this.#lastStateJson)
|
|
@@ -319,6 +537,15 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
319
537
|
this.#lastStateJson = json;
|
|
320
538
|
this.emitEvent('stateChanged', { detail: { state: snapshot } });
|
|
321
539
|
}
|
|
540
|
+
#baselineStateIfUnset() {
|
|
541
|
+
if (this.#lastStateJson === null && this.#stateChangedListeners.size && !this.#shapingPending) {
|
|
542
|
+
this.#lastStateJson = JSON.stringify(this.getState());
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
connectedCallback() {
|
|
546
|
+
super.connectedCallback();
|
|
547
|
+
adoptGridDocumentStyles(this);
|
|
548
|
+
}
|
|
322
549
|
firstUpdated() {
|
|
323
550
|
if (typeof getComputedStyle === 'function' && getComputedStyle(this).display !== 'grid') {
|
|
324
551
|
console.warn('[apex-grid] Host `display` has been overridden. The grid requires ' +
|
|
@@ -328,26 +555,44 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
328
555
|
}
|
|
329
556
|
}
|
|
330
557
|
filter(config) {
|
|
331
|
-
this
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
558
|
+
if (this.#pendingFilter) {
|
|
559
|
+
const added = asArray(config);
|
|
560
|
+
this.#pendingFilter.push(...added);
|
|
561
|
+
if (added.length)
|
|
562
|
+
this.#markViewChange();
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
const expressions = [];
|
|
566
|
+
for (const each of asArray(config)) {
|
|
567
|
+
const resolved = this.#resolveFilter(each);
|
|
568
|
+
if (typeof resolved === 'string')
|
|
569
|
+
warnDropped(resolved);
|
|
570
|
+
else
|
|
571
|
+
expressions.push(resolved);
|
|
572
|
+
}
|
|
573
|
+
if (!expressions.length)
|
|
574
|
+
return;
|
|
575
|
+
this.#markViewChange();
|
|
576
|
+
this.stateController.filtering.filter(expressions);
|
|
337
577
|
}
|
|
338
578
|
sort(expressions) {
|
|
579
|
+
if (this.#pendingSort) {
|
|
580
|
+
this.#pendingSort.push(...asArray(expressions));
|
|
581
|
+
this.requestUpdate(PIPELINE);
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
339
584
|
this.stateController.sorting.sort(expressions);
|
|
340
585
|
}
|
|
341
586
|
getState(options) {
|
|
342
587
|
const rowId = options?.rowId ?? this.rowId;
|
|
343
|
-
const pinned = this.stateController.rowPin.
|
|
588
|
+
const pinned = this.stateController.rowPin.pins;
|
|
344
589
|
const manualOrder = this.stateController.rowReorder.getManualOrder();
|
|
345
590
|
return {
|
|
346
591
|
version: 1,
|
|
347
592
|
columns: serializeColumnLayout(this.columns),
|
|
348
593
|
sort: serializeSort(this.sortExpressions),
|
|
349
594
|
filter: serializeFilter(this.filterExpressions),
|
|
350
|
-
quickFilter: this.quickFilter,
|
|
595
|
+
quickFilter: this.quickFilter ?? '',
|
|
351
596
|
pagination: { page: this.page, pageSize: this.pageSize },
|
|
352
597
|
selection: serializeRowRefs(this.selectedRows, this.data, rowId),
|
|
353
598
|
expansion: serializeRowRefs(this.expandedRows, this.data, rowId),
|
|
@@ -367,7 +612,7 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
367
612
|
columns: this.columns.map((column) => columnSchema(column)),
|
|
368
613
|
capabilities: {
|
|
369
614
|
sort: {
|
|
370
|
-
directions:
|
|
615
|
+
directions: SORT_DIRECTIONS.filter((direction) => direction !== 'none'),
|
|
371
616
|
multi: Boolean(this.sortConfiguration?.multiple),
|
|
372
617
|
},
|
|
373
618
|
filter: { operandsByType: operandsByType(this.columns) },
|
|
@@ -384,6 +629,7 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
384
629
|
const strict = options?.strict ?? false;
|
|
385
630
|
const applied = [];
|
|
386
631
|
const warnings = [];
|
|
632
|
+
const viewSettled = !this.#viewStale;
|
|
387
633
|
const warn = (message) => {
|
|
388
634
|
if (strict)
|
|
389
635
|
throw new Error(`[apex-grid] setState: ${message}`);
|
|
@@ -410,31 +656,23 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
410
656
|
}
|
|
411
657
|
const columns = asArray(state.columns, 'columns');
|
|
412
658
|
if (columns) {
|
|
659
|
+
const unkeyed = columns.filter((entry) => typeof entry?.key !== 'string').length;
|
|
660
|
+
if (unkeyed)
|
|
661
|
+
warn(`columns: ${unkeyed} entr${unkeyed === 1 ? 'y' : 'ies'} without a key; dropped`);
|
|
413
662
|
this.columns = applyColumnLayout(this.columns, columns);
|
|
414
663
|
applied.push('columns');
|
|
415
664
|
}
|
|
416
665
|
const sort = asArray(state.sort, 'sort');
|
|
417
666
|
if (sort) {
|
|
418
|
-
this
|
|
419
|
-
|
|
420
|
-
for (const entry of sort) {
|
|
421
|
-
if (this.getColumn(entry.key)) {
|
|
422
|
-
valid.push(entry);
|
|
423
|
-
}
|
|
424
|
-
else {
|
|
425
|
-
warn(`sort: unknown column "${entry.key}"; dropped`);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
if (valid.length)
|
|
429
|
-
this.sort(valid);
|
|
667
|
+
this.#pendingSort = undefined;
|
|
668
|
+
this.#replaceSort(sort, warn);
|
|
430
669
|
applied.push('sort');
|
|
431
670
|
}
|
|
432
671
|
const filter = asArray(state.filter, 'filter');
|
|
433
672
|
if (filter) {
|
|
434
|
-
this
|
|
673
|
+
this.#pendingFilter = undefined;
|
|
435
674
|
const expressions = deserializeFilter(filter, (key) => this.getColumn(key), (snapshot, reason) => warn(`filter: "${snapshot.key}" dropped (${reason})`));
|
|
436
|
-
|
|
437
|
-
this.filter(expressions);
|
|
675
|
+
this.#replaceFilter(expressions);
|
|
438
676
|
applied.push('filter');
|
|
439
677
|
}
|
|
440
678
|
if (state.quickFilter !== undefined) {
|
|
@@ -449,6 +687,7 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
449
687
|
if (state.modules !== undefined) {
|
|
450
688
|
if (state.modules && typeof state.modules === 'object') {
|
|
451
689
|
this.stateController.restoreModuleState(state.modules);
|
|
690
|
+
this.#markViewChange();
|
|
452
691
|
applied.push('modules');
|
|
453
692
|
}
|
|
454
693
|
else {
|
|
@@ -476,13 +715,22 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
476
715
|
if (state.treeExpanded !== undefined || state.treeExpandedKeys !== undefined) {
|
|
477
716
|
const refs = asArray(state.treeExpanded, 'treeExpanded') ?? [];
|
|
478
717
|
const keys = asArray(state.treeExpandedKeys, 'treeExpandedKeys') ?? [];
|
|
479
|
-
this.stateController.tree.restoreExpansion(resolveCounted(refs, 'treeExpanded'), keys);
|
|
718
|
+
this.stateController.tree.restoreExpansion(resolveCounted(refs, 'treeExpanded'), keys.filter((key) => typeof key === 'string'));
|
|
719
|
+
this.#markViewChange();
|
|
480
720
|
applied.push('tree');
|
|
481
721
|
}
|
|
482
722
|
const expansion = asArray(state.expansion, 'expansion');
|
|
483
723
|
if (expansion) {
|
|
484
|
-
|
|
485
|
-
|
|
724
|
+
const ctrl = this.stateController.expansion;
|
|
725
|
+
const predicate = this.expansion?.isExpandable;
|
|
726
|
+
const rows = resolveCounted(expansion, 'expansion');
|
|
727
|
+
const expected = predicate ? rows.filter((row) => predicate(row)) : rows;
|
|
728
|
+
if (this.#replaceRows('expansion', ctrl.enabled, expected, warn, () => {
|
|
729
|
+
void ctrl.replaceExpansion(expected);
|
|
730
|
+
return ctrl.expandedRows();
|
|
731
|
+
})) {
|
|
732
|
+
applied.push('expansion');
|
|
733
|
+
}
|
|
486
734
|
}
|
|
487
735
|
if (state.rowPinning !== undefined) {
|
|
488
736
|
const pinning = state.rowPinning;
|
|
@@ -496,8 +744,15 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
496
744
|
}
|
|
497
745
|
const selection = asArray(state.selection, 'selection');
|
|
498
746
|
if (selection) {
|
|
499
|
-
|
|
500
|
-
|
|
747
|
+
const ctrl = this.stateController.selection;
|
|
748
|
+
const rows = resolveCounted(selection, 'selection');
|
|
749
|
+
const expected = ctrl.mode === 'single' ? rows.slice(0, 1) : rows;
|
|
750
|
+
if (this.#replaceRows('selection', ctrl.enabled, expected, warn, () => {
|
|
751
|
+
void ctrl.replaceSelection(expected);
|
|
752
|
+
return ctrl.selectedRows();
|
|
753
|
+
})) {
|
|
754
|
+
applied.push('selection');
|
|
755
|
+
}
|
|
501
756
|
}
|
|
502
757
|
if (state.pagination !== undefined) {
|
|
503
758
|
const pagination = state.pagination;
|
|
@@ -514,10 +769,13 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
514
769
|
if (page !== undefined) {
|
|
515
770
|
if (typeof page === 'number' && Number.isFinite(page)) {
|
|
516
771
|
const requested = Math.trunc(page);
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
772
|
+
const reshaped = applied.some((slice) => VIEW_SHAPING_SLICES.has(slice));
|
|
773
|
+
const ctrl = this.stateController.pagination;
|
|
774
|
+
const next = viewSettled && !reshaped ? ctrl.clamp(requested) : Math.max(0, requested);
|
|
775
|
+
if (next !== requested) {
|
|
776
|
+
warn(`pagination: page ${requested} out of range; clamped to ${next}`);
|
|
520
777
|
}
|
|
778
|
+
ctrl.page = next;
|
|
521
779
|
}
|
|
522
780
|
else {
|
|
523
781
|
warn(`pagination: invalid page ${String(page)}; ignored`);
|
|
@@ -533,13 +791,37 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
533
791
|
const skipped = SET_STATE_SLICES.filter((slice) => !applied.includes(slice));
|
|
534
792
|
return { applied, skipped, warnings };
|
|
535
793
|
}
|
|
794
|
+
#replaceRows(label, enabled, expected, warn, replace) {
|
|
795
|
+
if (!enabled) {
|
|
796
|
+
if (expected.length)
|
|
797
|
+
warn(`${label}: the feature is disabled on this grid; skipped`);
|
|
798
|
+
return !expected.length;
|
|
799
|
+
}
|
|
800
|
+
const actual = new Set(replace());
|
|
801
|
+
const took = actual.size === new Set(expected).size && expected.every((row) => actual.has(row));
|
|
802
|
+
if (!took)
|
|
803
|
+
warn(`${label}: the change was cancelled by a listener; skipped`);
|
|
804
|
+
return took;
|
|
805
|
+
}
|
|
536
806
|
clearSort(key) {
|
|
807
|
+
if (this.#pendingSort && key !== undefined) {
|
|
808
|
+
this.#pendingSort = this.#pendingSort.filter((each) => each.key !== key);
|
|
809
|
+
}
|
|
810
|
+
else {
|
|
811
|
+
this.#pendingSort = undefined;
|
|
812
|
+
}
|
|
537
813
|
this.stateController.sorting.reset(key);
|
|
538
814
|
this.requestUpdate(PIPELINE);
|
|
539
815
|
}
|
|
540
816
|
clearFilter(key) {
|
|
817
|
+
if (this.#pendingFilter && key !== undefined) {
|
|
818
|
+
this.#pendingFilter = this.#pendingFilter.filter((each) => each.key !== key);
|
|
819
|
+
}
|
|
820
|
+
else {
|
|
821
|
+
this.#pendingFilter = undefined;
|
|
822
|
+
}
|
|
823
|
+
this.#markViewChange();
|
|
541
824
|
this.stateController.filtering.reset(key);
|
|
542
|
-
this.requestUpdate(PIPELINE);
|
|
543
825
|
}
|
|
544
826
|
gotoPage(page) {
|
|
545
827
|
return this.stateController.pagination.gotoPage(page);
|
|
@@ -563,15 +845,21 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
563
845
|
const next = value ?? '';
|
|
564
846
|
if (next === this.quickFilter)
|
|
565
847
|
return false;
|
|
848
|
+
const previous = this.quickFilter;
|
|
849
|
+
const writes = this.#quickFilterWrites;
|
|
566
850
|
const proceed = this.emitEvent('quickFilterChanging', {
|
|
567
|
-
detail: { value:
|
|
851
|
+
detail: { value: previous, nextValue: next },
|
|
568
852
|
cancelable: true,
|
|
569
853
|
});
|
|
570
854
|
if (!proceed)
|
|
571
855
|
return false;
|
|
572
|
-
this
|
|
573
|
-
|
|
574
|
-
|
|
856
|
+
if (this.#quickFilterWrites === writes)
|
|
857
|
+
this.quickFilter = next;
|
|
858
|
+
if (this.quickFilter === previous)
|
|
859
|
+
return false;
|
|
860
|
+
const pageReset = this.stateController.pagination.resetPage();
|
|
861
|
+
await this.pipelineSettled();
|
|
862
|
+
await pageReset;
|
|
575
863
|
this.emitEvent('quickFilterChanged', { detail: { value: this.quickFilter } });
|
|
576
864
|
return true;
|
|
577
865
|
}
|
|
@@ -637,6 +925,39 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
637
925
|
const editing = this.stateController.editing;
|
|
638
926
|
return editing.mode === 'row' ? editing.commitRow() : editing.commitCell();
|
|
639
927
|
}
|
|
928
|
+
applyEdits(edits) {
|
|
929
|
+
const tally = { applied: 0, unchanged: 0, invalid: 0, cancelled: 0, skipped: 0 };
|
|
930
|
+
if (!edits.length)
|
|
931
|
+
return tally;
|
|
932
|
+
const editing = this.stateController.editing;
|
|
933
|
+
const items = this.pageItems;
|
|
934
|
+
const indexOf = viewIndexer(items, edits.length);
|
|
935
|
+
const version = editing.validationVersion;
|
|
936
|
+
this.stateController.history.beginBatch();
|
|
937
|
+
try {
|
|
938
|
+
for (const { row, rowIndex, column, value } of edits) {
|
|
939
|
+
const record = row ?? items[rowIndex];
|
|
940
|
+
const config = this.getColumn(column);
|
|
941
|
+
if (!record ||
|
|
942
|
+
!config ||
|
|
943
|
+
!editing.isEditable(config) ||
|
|
944
|
+
!this.stateController.isDataRow(record)) {
|
|
945
|
+
tally.skipped += 1;
|
|
946
|
+
continue;
|
|
947
|
+
}
|
|
948
|
+
const index = row ? indexOf(row) : rowIndex;
|
|
949
|
+
tally[editing.applyCellEdit(index, column, record, value)] += 1;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
finally {
|
|
953
|
+
this.stateController.history.endBatch();
|
|
954
|
+
}
|
|
955
|
+
if (tally.applied > 0)
|
|
956
|
+
this.requestUpdate(PIPELINE);
|
|
957
|
+
else if (editing.validationVersion !== version)
|
|
958
|
+
this.requestUpdate();
|
|
959
|
+
return tally;
|
|
960
|
+
}
|
|
640
961
|
cancelEdit() {
|
|
641
962
|
const editing = this.stateController.editing;
|
|
642
963
|
if (editing.mode === 'row') {
|
|
@@ -706,22 +1027,20 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
706
1027
|
const target = this.columns[toIndex];
|
|
707
1028
|
if ((source.pinned ?? null) !== (target.pinned ?? null))
|
|
708
1029
|
return false;
|
|
709
|
-
const proceed = this.emitEvent('columnMoving', {
|
|
710
|
-
detail: { key: fromKey, fromIndex, toKey, position },
|
|
711
|
-
cancelable: true,
|
|
712
|
-
});
|
|
713
|
-
if (!proceed)
|
|
714
|
-
return false;
|
|
715
1030
|
const next = this.columns.slice();
|
|
716
1031
|
const [moved] = next.splice(fromIndex, 1);
|
|
717
1032
|
let insertIndex = next.findIndex((column) => column.key === toKey);
|
|
718
|
-
if (insertIndex < 0)
|
|
719
|
-
return false;
|
|
720
1033
|
if (position === 'after')
|
|
721
1034
|
insertIndex += 1;
|
|
722
1035
|
next.splice(insertIndex, 0, moved);
|
|
723
1036
|
if (next.every((column, index) => column === this.columns[index]))
|
|
724
1037
|
return false;
|
|
1038
|
+
const proceed = this.emitEvent('columnMoving', {
|
|
1039
|
+
detail: { key: fromKey, fromIndex, toKey, position },
|
|
1040
|
+
cancelable: true,
|
|
1041
|
+
});
|
|
1042
|
+
if (!proceed)
|
|
1043
|
+
return false;
|
|
725
1044
|
this.columns = next;
|
|
726
1045
|
await this.updateComplete;
|
|
727
1046
|
const resolvedIndex = this.columns.findIndex((column) => column.key === fromKey);
|
|
@@ -730,8 +1049,42 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
730
1049
|
});
|
|
731
1050
|
return true;
|
|
732
1051
|
}
|
|
1052
|
+
ownsEvent(event) {
|
|
1053
|
+
for (const node of event.composedPath()) {
|
|
1054
|
+
if (node === this || node === this.scrollContainer)
|
|
1055
|
+
return true;
|
|
1056
|
+
if (node instanceof ApexGrid || node instanceof ApexVirtualizer)
|
|
1057
|
+
return false;
|
|
1058
|
+
}
|
|
1059
|
+
return false;
|
|
1060
|
+
}
|
|
1061
|
+
#syncBodyTabStop() {
|
|
1062
|
+
const body = this.scrollContainer;
|
|
1063
|
+
if (!body)
|
|
1064
|
+
return;
|
|
1065
|
+
const active = this.stateController.active;
|
|
1066
|
+
body.tabIndex = active !== SENTINEL_NODE && body.isRendered(active.row) ? -1 : 0;
|
|
1067
|
+
}
|
|
1068
|
+
bodyRangeChanged(event) {
|
|
1069
|
+
const { first, last } = event;
|
|
1070
|
+
this.#syncBodyTabStop();
|
|
1071
|
+
const focused = this.renderRoot instanceof ShadowRoot ? this.renderRoot.activeElement : null;
|
|
1072
|
+
if (focused instanceof ApexGridRow &&
|
|
1073
|
+
focused.parentElement === this.scrollContainer &&
|
|
1074
|
+
(focused.index < first || focused.index > last)) {
|
|
1075
|
+
this.scrollContainer.focus({ preventScroll: true });
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
#ownCell(event) {
|
|
1079
|
+
if (!this.ownsEvent(event))
|
|
1080
|
+
return undefined;
|
|
1081
|
+
return event.composedPath().find((el) => el instanceof ApexGridCell);
|
|
1082
|
+
}
|
|
733
1083
|
bodyClickHandler(event) {
|
|
734
|
-
|
|
1084
|
+
if (!this.ownsEvent(event))
|
|
1085
|
+
return;
|
|
1086
|
+
const path = event.composedPath();
|
|
1087
|
+
const target = path.find((el) => el instanceof ApexGridCell);
|
|
735
1088
|
if (target) {
|
|
736
1089
|
this.stateController.active = {
|
|
737
1090
|
column: target.column.key,
|
|
@@ -740,11 +1093,22 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
740
1093
|
if (!target.editing)
|
|
741
1094
|
target.focus({ preventScroll: true });
|
|
742
1095
|
}
|
|
1096
|
+
else {
|
|
1097
|
+
const row = path.find((el) => el instanceof ApexGridRow);
|
|
1098
|
+
const cell = row?.presentedCell;
|
|
1099
|
+
if (row && cell && path.includes(cell)) {
|
|
1100
|
+
const { column } = this.stateController.active;
|
|
1101
|
+
this.stateController.active = { column, row: row.index };
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
743
1104
|
}
|
|
744
1105
|
bodyKeydownHandler(event) {
|
|
1106
|
+
if (!this.ownsEvent(event))
|
|
1107
|
+
return;
|
|
745
1108
|
const origin = event.composedPath()[0];
|
|
746
1109
|
const fromBody = this.scrollContainer.isSameNode(event.target);
|
|
747
|
-
const fromCell = origin instanceof ApexGridCell && !origin.editing
|
|
1110
|
+
const fromCell = (origin instanceof ApexGridCell && !origin.editing) ||
|
|
1111
|
+
(event.target instanceof ApexGridRow && event.target.presentedCell === origin);
|
|
748
1112
|
if (fromBody || fromCell) {
|
|
749
1113
|
this.stateController.navigation.navigate(event);
|
|
750
1114
|
}
|
|
@@ -759,7 +1123,7 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
759
1123
|
return;
|
|
760
1124
|
if (event.type === 'pointermove' && event.buttons === 0)
|
|
761
1125
|
return;
|
|
762
|
-
const target = event
|
|
1126
|
+
const target = this.#ownCell(event);
|
|
763
1127
|
if (!target?.row)
|
|
764
1128
|
return;
|
|
765
1129
|
const kind = event.type === 'pointerdown' ? 'down' : event.type === 'pointerup' ? 'up' : 'over';
|
|
@@ -804,6 +1168,7 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
804
1168
|
@click=${this.bodyClickHandler}
|
|
805
1169
|
@keydown=${this.bodyKeydownHandler}
|
|
806
1170
|
@focusin=${this.bodyFocusHandler}
|
|
1171
|
+
@rangeChanged=${this.bodyRangeChanged}
|
|
807
1172
|
@pointerdown=${this.bodyPointerHandler}
|
|
808
1173
|
@pointermove=${this.bodyPointerHandler}
|
|
809
1174
|
@pointerup=${this.bodyPointerHandler}
|
|
@@ -815,8 +1180,6 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
815
1180
|
}
|
|
816
1181
|
renderPinnedTop() {
|
|
817
1182
|
const rowPin = this.stateController.rowPin;
|
|
818
|
-
if (!rowPin.enabled)
|
|
819
|
-
return nothing;
|
|
820
1183
|
const rows = rowPin.pinnedRows.top;
|
|
821
1184
|
if (rows.length === 0)
|
|
822
1185
|
return nothing;
|
|
@@ -826,8 +1189,6 @@ export class ApexGrid extends EventEmitterBase {
|
|
|
826
1189
|
}
|
|
827
1190
|
renderPinnedBottom() {
|
|
828
1191
|
const rowPin = this.stateController.rowPin;
|
|
829
|
-
if (!rowPin.enabled)
|
|
830
|
-
return nothing;
|
|
831
1192
|
const rows = rowPin.pinnedRows.bottom;
|
|
832
1193
|
if (rows.length === 0)
|
|
833
1194
|
return nothing;
|
|
@@ -948,7 +1309,7 @@ __decorate([
|
|
|
948
1309
|
], ApexGrid.prototype, "columns", void 0);
|
|
949
1310
|
__decorate([
|
|
950
1311
|
property({ attribute: false })
|
|
951
|
-
], ApexGrid.prototype, "data",
|
|
1312
|
+
], ApexGrid.prototype, "data", null);
|
|
952
1313
|
__decorate([
|
|
953
1314
|
property({ attribute: false })
|
|
954
1315
|
], ApexGrid.prototype, "rowId", void 0);
|
|
@@ -966,7 +1327,7 @@ __decorate([
|
|
|
966
1327
|
], ApexGrid.prototype, "pagination", void 0);
|
|
967
1328
|
__decorate([
|
|
968
1329
|
property({ type: String, attribute: 'quick-filter' })
|
|
969
|
-
], ApexGrid.prototype, "quickFilter",
|
|
1330
|
+
], ApexGrid.prototype, "quickFilter", null);
|
|
970
1331
|
__decorate([
|
|
971
1332
|
property({ type: Boolean, attribute: 'show-quick-filter' })
|
|
972
1333
|
], ApexGrid.prototype, "showQuickFilter", void 0);
|
|
@@ -1027,15 +1388,18 @@ __decorate([
|
|
|
1027
1388
|
__decorate([
|
|
1028
1389
|
watch('data')
|
|
1029
1390
|
], ApexGrid.prototype, "dataChanged", null);
|
|
1030
|
-
__decorate([
|
|
1031
|
-
watch(PIPELINE)
|
|
1032
|
-
], ApexGrid.prototype, "pipeline", null);
|
|
1033
1391
|
__decorate([
|
|
1034
1392
|
watch('pagination')
|
|
1035
1393
|
], ApexGrid.prototype, "paginationChanged", null);
|
|
1036
1394
|
__decorate([
|
|
1037
1395
|
watch('quickFilter')
|
|
1038
1396
|
], ApexGrid.prototype, "quickFilterChanged", null);
|
|
1397
|
+
__decorate([
|
|
1398
|
+
watch('tree', { waitUntilFirstUpdate: true })
|
|
1399
|
+
], ApexGrid.prototype, "treeChanged", null);
|
|
1400
|
+
__decorate([
|
|
1401
|
+
watch(PIPELINE)
|
|
1402
|
+
], ApexGrid.prototype, "pipeline", null);
|
|
1039
1403
|
__decorate([
|
|
1040
1404
|
eventOptions({ capture: true })
|
|
1041
1405
|
], ApexGrid.prototype, "bodyClickHandler", null);
|