apex-grid 1.1.0 → 3.0.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/LICENSE +76 -5
- package/README.md +523 -10
- package/components/cell.d.ts +39 -0
- package/components/cell.js +334 -5
- package/components/cell.js.map +1 -1
- package/components/filter-row.d.ts +7 -5
- package/components/filter-row.js +151 -68
- package/components/filter-row.js.map +1 -1
- package/components/grid.d.ts +955 -4
- package/components/grid.js +412 -40
- package/components/grid.js.map +1 -1
- package/components/header-row.d.ts +21 -1
- package/components/header-row.js +140 -7
- package/components/header-row.js.map +1 -1
- package/components/header.d.ts +7 -0
- package/components/header.js +151 -31
- package/components/header.js.map +1 -1
- package/components/paginator.d.ts +56 -0
- package/components/paginator.js +148 -0
- package/components/paginator.js.map +1 -0
- package/components/row.d.ts +21 -2
- package/components/row.js +185 -10
- package/components/row.js.map +1 -1
- package/components/toolbar.d.ts +60 -0
- package/components/toolbar.js +259 -0
- package/components/toolbar.js.map +1 -0
- package/components/virtualizer.js +1 -1
- package/components/virtualizer.js.map +1 -1
- package/controllers/data-operation.d.ts +14 -0
- package/controllers/data-operation.js +19 -2
- package/controllers/data-operation.js.map +1 -1
- package/controllers/dom.d.ts +37 -1
- package/controllers/dom.js +176 -9
- package/controllers/dom.js.map +1 -1
- package/controllers/editing.d.ts +130 -0
- package/controllers/editing.js +197 -0
- package/controllers/editing.js.map +1 -0
- package/controllers/expansion.d.ts +68 -0
- package/controllers/expansion.js +126 -0
- package/controllers/expansion.js.map +1 -0
- package/controllers/navigation.d.ts +17 -6
- package/controllers/navigation.js +24 -8
- package/controllers/navigation.js.map +1 -1
- package/controllers/pagination.d.ts +102 -0
- package/controllers/pagination.js +106 -0
- package/controllers/pagination.js.map +1 -0
- package/controllers/reorder.d.ts +78 -0
- package/controllers/reorder.js +124 -0
- package/controllers/reorder.js.map +1 -0
- package/controllers/selection.d.ts +109 -0
- package/controllers/selection.js +168 -0
- package/controllers/selection.js.map +1 -0
- package/controllers/sort.js +28 -0
- package/controllers/sort.js.map +1 -1
- package/controllers/state.d.ts +47 -1
- package/controllers/state.js +48 -1
- package/controllers/state.js.map +1 -1
- package/controllers/tree.d.ts +87 -0
- package/controllers/tree.js +0 -0
- package/controllers/tree.js.map +1 -0
- package/custom-elements.json +6491 -1355
- package/define.js.map +1 -1
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/highlight.css +77 -14
- package/docs/assets/icons.js +1 -1
- package/docs/assets/icons.svg +1 -1
- package/docs/assets/main.js +4 -4
- package/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/assets/style.css +3 -3
- package/docs/classes/ApexGrid.html +320 -24
- package/docs/functions/setup.html +16 -0
- package/docs/hierarchy.html +1 -1
- package/docs/index.html +543 -9
- package/docs/interfaces/ApexCellValueChangedEvent.html +11 -0
- package/docs/interfaces/ApexCellValueChangingEvent.html +13 -0
- package/docs/interfaces/ApexColumnMovedEvent.html +8 -0
- package/docs/interfaces/ApexColumnMovingEvent.html +11 -0
- package/docs/interfaces/ApexColumnPinnedEvent.html +6 -0
- package/docs/interfaces/ApexColumnPinningEvent.html +8 -0
- package/docs/interfaces/ApexDetailContext.html +8 -0
- package/docs/interfaces/ApexFilteredEvent.html +4 -4
- package/docs/interfaces/ApexFilteringEvent.html +4 -4
- package/docs/interfaces/ApexGridEventMap.html +60 -9
- package/docs/interfaces/ApexHeaderContext.html +4 -4
- package/docs/interfaces/ApexPageChangedEvent.html +10 -0
- package/docs/interfaces/ApexPageChangingEvent.html +10 -0
- package/docs/interfaces/ApexQuickFilterChangedEvent.html +4 -0
- package/docs/interfaces/ApexQuickFilterChangingEvent.html +6 -0
- package/docs/interfaces/ApexRowEditEndedEvent.html +6 -0
- package/docs/interfaces/ApexRowEditStartedEvent.html +4 -0
- package/docs/interfaces/ApexRowExpandedEvent.html +8 -0
- package/docs/interfaces/ApexRowExpandingEvent.html +11 -0
- package/docs/interfaces/ApexRowSelectedEvent.html +8 -0
- package/docs/interfaces/ApexRowSelectingEvent.html +11 -0
- package/docs/interfaces/ApexTreeRowExpandedEvent.html +8 -0
- package/docs/interfaces/ApexTreeRowExpandingEvent.html +10 -0
- package/docs/interfaces/BaseApexCellContext.html +12 -6
- package/docs/interfaces/BaseApexEditorContext.html +19 -0
- package/docs/interfaces/BaseColumnConfiguration.html +88 -14
- package/docs/interfaces/BaseColumnSortConfiguration.html +4 -4
- package/docs/interfaces/BaseFilterExpression.html +5 -5
- package/docs/interfaces/BaseSortExpression.html +4 -4
- package/docs/interfaces/CSVExportOptions.html +22 -0
- package/docs/interfaces/ColumnFilterConfiguration.html +3 -3
- package/docs/interfaces/DataPipelineConfiguration.html +13 -4
- package/docs/interfaces/ExportFormat.html +10 -0
- package/docs/interfaces/ExportOptions.html +15 -0
- package/docs/interfaces/GridEditingConfiguration.html +10 -0
- package/docs/interfaces/GridExpansionConfiguration.html +15 -0
- package/docs/interfaces/GridSelectionConfiguration.html +11 -0
- package/docs/interfaces/GridSortConfiguration.html +4 -4
- package/docs/interfaces/GridTreeConfiguration.html +33 -0
- package/docs/interfaces/PaginationConfiguration.html +18 -0
- package/docs/interfaces/PaginationState.html +10 -0
- package/docs/media/LICENSE +80 -0
- package/docs/media/_tokens.scss +106 -0
- package/docs/media/package.json +85 -0
- package/docs/modules.html +1 -1
- package/docs/types/ApexCellContext.html +2 -2
- package/docs/types/ApexEditorContext.html +2 -0
- package/docs/types/BasePropertyType.html +2 -2
- package/docs/types/BaseSortComparer.html +2 -2
- package/docs/types/ColumnConfiguration.html +2 -2
- package/docs/types/ColumnDropPosition.html +2 -0
- package/docs/types/ColumnSortConfiguration.html +2 -2
- package/docs/types/DataPipelineHook.html +2 -2
- package/docs/types/DataPipelineParams.html +5 -5
- package/docs/types/DataType.html +47 -2
- package/docs/types/EditMode.html +6 -0
- package/docs/types/EditTrigger.html +4 -0
- package/docs/types/ExportCellValue.html +5 -0
- package/docs/types/ExportSource.html +8 -0
- package/docs/types/FilterCriteria.html +2 -2
- package/docs/types/FilterExpression.html +2 -2
- package/docs/types/Keys.html +2 -2
- package/docs/types/PaginationMode.html +6 -0
- package/docs/types/PinPosition.html +5 -0
- package/docs/types/PropertyType.html +2 -2
- package/docs/types/SelectionMode.html +7 -0
- package/docs/types/SortComparer.html +2 -2
- package/docs/types/SortExpression.html +2 -2
- package/docs/types/SortState.html +2 -2
- package/docs/types/SortingDirection.html +1 -1
- package/index.d.ts +5 -2
- package/index.js +1 -0
- package/index.js.map +1 -1
- package/internal/column-types.d.ts +94 -0
- package/internal/column-types.js +428 -0
- package/internal/column-types.js.map +1 -0
- package/internal/export.d.ts +114 -0
- package/internal/export.js +122 -0
- package/internal/export.js.map +1 -0
- package/internal/feature-module.d.ts +27 -0
- package/internal/feature-module.js +2 -0
- package/internal/feature-module.js.map +1 -0
- package/internal/flip.d.ts +80 -0
- package/internal/flip.js +52 -0
- package/internal/flip.js.map +1 -0
- package/internal/icons.d.ts +31 -0
- package/internal/icons.js +145 -0
- package/internal/icons.js.map +1 -0
- package/internal/index.d.ts +20 -0
- package/internal/index.js +6 -0
- package/internal/index.js.map +1 -0
- package/internal/tags.d.ts +2 -0
- package/internal/tags.js +2 -0
- package/internal/tags.js.map +1 -1
- package/internal/types.d.ts +473 -3
- package/internal/types.js.map +1 -1
- package/internal/utils.d.ts +24 -1
- package/internal/utils.js +48 -7
- package/internal/utils.js.map +1 -1
- package/operations/filter/tree.d.ts +1 -1
- package/operations/filter/types.js.map +1 -1
- package/operations/pagination.d.ts +19 -0
- package/operations/pagination.js +12 -0
- package/operations/pagination.js.map +1 -0
- package/operations/quick-filter.d.ts +21 -0
- package/operations/quick-filter.js +26 -0
- package/operations/quick-filter.js.map +1 -0
- package/operations/sort/types.js.map +1 -1
- package/package.json +10 -5
- package/setup.d.ts +51 -0
- package/setup.js +31 -0
- package/setup.js.map +1 -0
- package/styles/_checkbox.css.js +3 -0
- package/styles/_checkbox.css.js.map +1 -0
- package/styles/_tokens.css.js +3 -0
- package/styles/_tokens.css.js.map +1 -0
- 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 +3 -0
- package/styles/body-cell/cell-renderers.css.js.map +1 -0
- package/styles/body-row/body-row.css.js +1 -1
- package/styles/body-row/body-row.css.js.map +1 -1
- 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 +3 -0
- package/styles/grid/grid.css.js.map +1 -0
- 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.d.ts +1 -0
- package/styles/paginator/paginator.css.js +3 -0
- package/styles/paginator/paginator.css.js.map +1 -0
- package/styles/toolbar/toolbar.css.d.ts +1 -0
- package/styles/toolbar/toolbar.css.js +3 -0
- package/styles/toolbar/toolbar.css.js.map +1 -0
- package/styles.css +17 -0
- package/internal/icon-registry.d.ts +0 -1
- package/internal/icon-registry.js +0 -29
- package/internal/icon-registry.js.map +0 -1
- package/internal/theming.d.ts +0 -34
- package/internal/theming.js +0 -123
- package/internal/theming.js.map +0 -1
- package/styles/grid/themes/light/grid.bootstrap.css.js +0 -3
- package/styles/grid/themes/light/grid.bootstrap.css.js.map +0 -1
- package/styles/grid/themes/light/grid.fluent.css.js +0 -3
- package/styles/grid/themes/light/grid.fluent.css.js.map +0 -1
- package/styles/grid/themes/light/grid.indigo.css.js +0 -3
- package/styles/grid/themes/light/grid.indigo.css.js.map +0 -1
- package/styles/grid/themes/light/grid.material.css.js +0 -3
- package/styles/grid/themes/light/grid.material.css.js.map +0 -1
- /package/styles/{grid/themes/light/grid.bootstrap.css.d.ts → _checkbox.css.d.ts} +0 -0
- /package/styles/{grid/themes/light/grid.fluent.css.d.ts → _tokens.css.d.ts} +0 -0
- /package/styles/{grid/themes/light/grid.indigo.css.d.ts → body-cell/cell-renderers.css.d.ts} +0 -0
- /package/styles/grid/{themes/light/grid.material.css.d.ts → grid.css.d.ts} +0 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { PIPELINE } from '../internal/constants.js';
|
|
2
|
+
export const DEFAULT_EDITING_CONFIG = Object.freeze({
|
|
3
|
+
enabled: false,
|
|
4
|
+
mode: 'cell',
|
|
5
|
+
trigger: 'doubleClick',
|
|
6
|
+
});
|
|
7
|
+
export class EditingController {
|
|
8
|
+
constructor(host) {
|
|
9
|
+
this.host = host;
|
|
10
|
+
this.activeCell = null;
|
|
11
|
+
this.activeRow = null;
|
|
12
|
+
this.pending = new Map();
|
|
13
|
+
this.host.addController(this);
|
|
14
|
+
}
|
|
15
|
+
hostConnected() { }
|
|
16
|
+
get enabled() {
|
|
17
|
+
return Boolean(this.host.editing?.enabled);
|
|
18
|
+
}
|
|
19
|
+
get mode() {
|
|
20
|
+
return this.host.editing?.mode ?? DEFAULT_EDITING_CONFIG.mode;
|
|
21
|
+
}
|
|
22
|
+
get trigger() {
|
|
23
|
+
return this.host.editing?.trigger ?? DEFAULT_EDITING_CONFIG.trigger;
|
|
24
|
+
}
|
|
25
|
+
isEditable(column) {
|
|
26
|
+
return Boolean(this.enabled && column.editable && !column.hidden);
|
|
27
|
+
}
|
|
28
|
+
isCellEditing(rowIndex, columnKey) {
|
|
29
|
+
return this.activeCell?.rowIndex === rowIndex && this.activeCell?.columnKey === columnKey;
|
|
30
|
+
}
|
|
31
|
+
isRowEditing(rowIndex) {
|
|
32
|
+
return this.activeRow?.rowIndex === rowIndex;
|
|
33
|
+
}
|
|
34
|
+
async editCell(rowIndex, columnKey) {
|
|
35
|
+
if (!this.enabled)
|
|
36
|
+
return false;
|
|
37
|
+
const column = this.host.getColumn(columnKey);
|
|
38
|
+
if (!column || !this.isEditable(column))
|
|
39
|
+
return false;
|
|
40
|
+
const data = this.host.pageItems[rowIndex];
|
|
41
|
+
if (!data)
|
|
42
|
+
return false;
|
|
43
|
+
if (this.mode === 'row') {
|
|
44
|
+
if (!this.activeRow) {
|
|
45
|
+
await this.editRow(rowIndex);
|
|
46
|
+
}
|
|
47
|
+
else if (this.activeRow.rowIndex !== rowIndex) {
|
|
48
|
+
const committed = await this.commitRow();
|
|
49
|
+
if (!committed)
|
|
50
|
+
return false;
|
|
51
|
+
await this.editRow(rowIndex);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else if (this.activeCell && !this.isCellEditing(rowIndex, columnKey)) {
|
|
55
|
+
await this.commitCell();
|
|
56
|
+
}
|
|
57
|
+
this.activeCell = { rowIndex, columnKey, data };
|
|
58
|
+
this.host.requestUpdate();
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
async editRow(rowIndex) {
|
|
62
|
+
if (!this.enabled || this.mode !== 'row')
|
|
63
|
+
return false;
|
|
64
|
+
const data = this.host.pageItems[rowIndex];
|
|
65
|
+
if (!data)
|
|
66
|
+
return false;
|
|
67
|
+
if (this.activeRow && this.activeRow.rowIndex !== rowIndex) {
|
|
68
|
+
const committed = await this.commitRow();
|
|
69
|
+
if (!committed)
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
if (this.activeRow?.rowIndex === rowIndex)
|
|
73
|
+
return true;
|
|
74
|
+
this.activeRow = { rowIndex, data };
|
|
75
|
+
this.pending.clear();
|
|
76
|
+
this.host.requestUpdate();
|
|
77
|
+
this.host.emitEvent('rowEditStarted', { detail: { rowIndex } });
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
async commitCell(value) {
|
|
81
|
+
const active = this.activeCell;
|
|
82
|
+
if (!active)
|
|
83
|
+
return false;
|
|
84
|
+
const { rowIndex, columnKey, data } = active;
|
|
85
|
+
const oldValue = data[columnKey];
|
|
86
|
+
const candidate = value === undefined ? oldValue : value;
|
|
87
|
+
if (this.mode === 'cell') {
|
|
88
|
+
if (Object.is(candidate, oldValue)) {
|
|
89
|
+
this.activeCell = null;
|
|
90
|
+
this.host.requestUpdate();
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
const proceed = this.host.emitEvent('cellValueChanging', {
|
|
94
|
+
detail: { key: columnKey, rowIndex, data, oldValue, newValue: candidate },
|
|
95
|
+
cancelable: true,
|
|
96
|
+
});
|
|
97
|
+
if (!proceed)
|
|
98
|
+
return false;
|
|
99
|
+
data[columnKey] = candidate;
|
|
100
|
+
this.activeCell = null;
|
|
101
|
+
this.host.requestUpdate(PIPELINE);
|
|
102
|
+
await this.host.updateComplete;
|
|
103
|
+
this.host.emitEvent('cellValueChanged', {
|
|
104
|
+
detail: { key: columnKey, rowIndex, data, value: candidate },
|
|
105
|
+
});
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
if (!Object.is(candidate, oldValue)) {
|
|
109
|
+
this.pending.set(columnKey, candidate);
|
|
110
|
+
}
|
|
111
|
+
this.activeCell = null;
|
|
112
|
+
this.host.requestUpdate();
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
async commitImmediate(rowIndex, columnKey, value) {
|
|
116
|
+
if (!this.enabled)
|
|
117
|
+
return false;
|
|
118
|
+
const column = this.host.getColumn(columnKey);
|
|
119
|
+
if (!column || !this.isEditable(column))
|
|
120
|
+
return false;
|
|
121
|
+
const data = this.host.pageItems[rowIndex];
|
|
122
|
+
if (!data)
|
|
123
|
+
return false;
|
|
124
|
+
const oldValue = data[columnKey];
|
|
125
|
+
if (Object.is(value, oldValue))
|
|
126
|
+
return true;
|
|
127
|
+
const proceed = this.host.emitEvent('cellValueChanging', {
|
|
128
|
+
detail: { key: columnKey, rowIndex, data, oldValue, newValue: value },
|
|
129
|
+
cancelable: true,
|
|
130
|
+
});
|
|
131
|
+
if (!proceed)
|
|
132
|
+
return false;
|
|
133
|
+
data[columnKey] = value;
|
|
134
|
+
this.host.requestUpdate(PIPELINE);
|
|
135
|
+
await this.host.updateComplete;
|
|
136
|
+
this.host.emitEvent('cellValueChanged', {
|
|
137
|
+
detail: { key: columnKey, rowIndex, data, value },
|
|
138
|
+
});
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
cancelCell() {
|
|
142
|
+
if (!this.activeCell)
|
|
143
|
+
return;
|
|
144
|
+
this.activeCell = null;
|
|
145
|
+
this.host.requestUpdate();
|
|
146
|
+
}
|
|
147
|
+
async commitRow() {
|
|
148
|
+
const active = this.activeRow;
|
|
149
|
+
if (!active)
|
|
150
|
+
return false;
|
|
151
|
+
const { rowIndex, data } = active;
|
|
152
|
+
if (this.activeCell?.rowIndex === rowIndex) {
|
|
153
|
+
await this.commitCell();
|
|
154
|
+
}
|
|
155
|
+
for (const [columnKey, candidate] of this.pending) {
|
|
156
|
+
const oldValue = data[columnKey];
|
|
157
|
+
if (Object.is(candidate, oldValue))
|
|
158
|
+
continue;
|
|
159
|
+
const proceed = this.host.emitEvent('cellValueChanging', {
|
|
160
|
+
detail: { key: columnKey, rowIndex, data, oldValue, newValue: candidate },
|
|
161
|
+
cancelable: true,
|
|
162
|
+
});
|
|
163
|
+
if (!proceed)
|
|
164
|
+
return false;
|
|
165
|
+
data[columnKey] = candidate;
|
|
166
|
+
this.host.emitEvent('cellValueChanged', {
|
|
167
|
+
detail: { key: columnKey, rowIndex, data, value: candidate },
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
this.pending.clear();
|
|
171
|
+
this.activeRow = null;
|
|
172
|
+
this.host.requestUpdate(PIPELINE);
|
|
173
|
+
await this.host.updateComplete;
|
|
174
|
+
this.host.emitEvent('rowEditEnded', { detail: { rowIndex, committed: true } });
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
cancelRow() {
|
|
178
|
+
if (!this.activeRow)
|
|
179
|
+
return;
|
|
180
|
+
const { rowIndex } = this.activeRow;
|
|
181
|
+
this.activeCell = null;
|
|
182
|
+
this.activeRow = null;
|
|
183
|
+
this.pending.clear();
|
|
184
|
+
this.host.requestUpdate();
|
|
185
|
+
this.host.emitEvent('rowEditEnded', { detail: { rowIndex, committed: false } });
|
|
186
|
+
}
|
|
187
|
+
getValue(rowIndex, columnKey) {
|
|
188
|
+
if (this.mode === 'row' &&
|
|
189
|
+
this.activeRow?.rowIndex === rowIndex &&
|
|
190
|
+
this.pending.has(columnKey)) {
|
|
191
|
+
return this.pending.get(columnKey);
|
|
192
|
+
}
|
|
193
|
+
const data = this.host.pageItems[rowIndex];
|
|
194
|
+
return data ? data[columnKey] : undefined;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=editing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editing.js","sourceRoot":"","sources":["../../src/controllers/editing.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController } from 'lit';\nimport { PIPELINE } from '../internal/constants.js';\nimport type {\n ColumnConfiguration,\n EditMode,\n EditTrigger,\n GridHost,\n Keys,\n} from '../internal/types.js';\n\n/**\n * The default editing configuration used when the grid has none set.\n */\nexport const DEFAULT_EDITING_CONFIG = Object.freeze({\n enabled: false,\n mode: 'cell' as EditMode,\n trigger: 'doubleClick' as EditTrigger,\n});\n\ninterface ActiveCell<T> {\n rowIndex: number;\n columnKey: Keys<T>;\n /**\n * Live reference to the underlying record (preserved through the pipeline so\n * the source array can be mutated on commit).\n */\n data: T;\n}\n\n/**\n * Reactive controller backing inline cell + row editing.\n *\n * @remarks\n * Holds the currently active cell / row, pending values for row-mode edits,\n * and is responsible for emitting the cancellable `cellValueChanging` event\n * and follow-up `cellValueChanged` event. The actual data mutation writes\n * through to {@link ApexGrid.data} so changes survive pipeline runs and\n * propagate to consumers that own the source array.\n */\nexport class EditingController<T extends object> implements ReactiveController {\n /** The cell currently in edit mode, or `null`. */\n public activeCell: ActiveCell<T> | null = null;\n\n /** The row currently in edit mode (row mode only), or `null`. */\n public activeRow: { rowIndex: number; data: T } | null = null;\n\n /**\n * Pending values keyed by column for the currently editing row. Populated in\n * row mode as the user edits individual cells; flushed on\n * {@link commitRow}.\n */\n public pending: Map<Keys<T>, unknown> = new Map();\n\n constructor(protected host: GridHost<T>) {\n this.host.addController(this);\n }\n\n public hostConnected() {}\n\n /**\n * Whether editing is enabled at the grid level.\n */\n public get enabled(): boolean {\n return Boolean(this.host.editing?.enabled);\n }\n\n /**\n * The resolved edit mode (`'cell'` if unset).\n */\n public get mode(): EditMode {\n return this.host.editing?.mode ?? DEFAULT_EDITING_CONFIG.mode;\n }\n\n /**\n * The resolved edit trigger (`'doubleClick'` if unset).\n */\n public get trigger(): EditTrigger {\n return this.host.editing?.trigger ?? DEFAULT_EDITING_CONFIG.trigger;\n }\n\n /**\n * Whether the given column can be edited.\n */\n public isEditable(column: ColumnConfiguration<T>): boolean {\n return Boolean(this.enabled && column.editable && !column.hidden);\n }\n\n /**\n * Whether the cell `(rowIndex, columnKey)` is currently in edit mode.\n */\n public isCellEditing(rowIndex: number, columnKey: Keys<T>): boolean {\n return this.activeCell?.rowIndex === rowIndex && this.activeCell?.columnKey === columnKey;\n }\n\n /**\n * Whether the given row is currently in edit mode (row mode).\n */\n public isRowEditing(rowIndex: number): boolean {\n return this.activeRow?.rowIndex === rowIndex;\n }\n\n /**\n * Begins editing the cell at `(rowIndex, columnKey)`. Auto-commits any cell\n * already in edit mode (in cell mode); in row mode it switches the active\n * cell within the same row and rejects cross-row moves.\n *\n * @returns `true` if edit started, `false` if rejected (column not editable\n * or row-mode constraint violated).\n */\n public async editCell(rowIndex: number, columnKey: Keys<T>): Promise<boolean> {\n if (!this.enabled) return false;\n const column = this.host.getColumn(columnKey);\n if (!column || !this.isEditable(column)) return false;\n const data = this.host.pageItems[rowIndex] as T | undefined;\n if (!data) return false;\n\n if (this.mode === 'row') {\n if (!this.activeRow) {\n await this.editRow(rowIndex);\n } else if (this.activeRow.rowIndex !== rowIndex) {\n // Switching rows in row mode — commit pending edits in the previous row\n // before starting elsewhere.\n const committed = await this.commitRow();\n if (!committed) return false;\n await this.editRow(rowIndex);\n }\n } else if (this.activeCell && !this.isCellEditing(rowIndex, columnKey)) {\n // Cell mode — commit any in-flight cell before switching.\n await this.commitCell();\n }\n\n this.activeCell = { rowIndex, columnKey, data };\n this.host.requestUpdate();\n return true;\n }\n\n /**\n * Begins editing the given row in row mode. Emits `rowEditStarted` on\n * success. No-op in cell mode.\n */\n public async editRow(rowIndex: number): Promise<boolean> {\n if (!this.enabled || this.mode !== 'row') return false;\n const data = this.host.pageItems[rowIndex] as T | undefined;\n if (!data) return false;\n if (this.activeRow && this.activeRow.rowIndex !== rowIndex) {\n const committed = await this.commitRow();\n if (!committed) return false;\n }\n if (this.activeRow?.rowIndex === rowIndex) return true;\n this.activeRow = { rowIndex, data };\n this.pending.clear();\n this.host.requestUpdate();\n this.host.emitEvent('rowEditStarted', { detail: { rowIndex } });\n return true;\n }\n\n /**\n * Commits a candidate value for the currently editing cell. In cell mode the\n * value is written through to {@link ApexGrid.data}; in row mode it is staged\n * in {@link pending} until {@link commitRow} is called.\n *\n * @param value - The candidate value.\n * @returns `true` if accepted (or staged), `false` if rejected by\n * `cellValueChanging.preventDefault()`.\n */\n public async commitCell(value?: unknown): Promise<boolean> {\n const active = this.activeCell;\n if (!active) return false;\n const { rowIndex, columnKey, data } = active;\n const oldValue = (data as Record<string, unknown>)[columnKey as string];\n const candidate = value === undefined ? oldValue : value;\n\n if (this.mode === 'cell') {\n if (Object.is(candidate, oldValue)) {\n this.activeCell = null;\n this.host.requestUpdate();\n return true;\n }\n const proceed = this.host.emitEvent('cellValueChanging', {\n detail: { key: columnKey, rowIndex, data, oldValue, newValue: candidate },\n cancelable: true,\n });\n if (!proceed) return false;\n (data as Record<string, unknown>)[columnKey as string] = candidate;\n this.activeCell = null;\n this.host.requestUpdate(PIPELINE);\n await this.host.updateComplete;\n this.host.emitEvent('cellValueChanged', {\n detail: { key: columnKey, rowIndex, data, value: candidate },\n });\n return true;\n }\n\n // Row mode — stage and exit cell edit (row stays open).\n if (!Object.is(candidate, oldValue)) {\n this.pending.set(columnKey, candidate);\n }\n this.activeCell = null;\n this.host.requestUpdate();\n return true;\n }\n\n /**\n * Writes `value` directly to the cell at `(rowIndex, columnKey)` without\n * entering edit mode. Used by interactive display widgets (e.g. the boolean\n * checkbox) that handle their own input. Goes through the same\n * `cellValueChanging` / `cellValueChanged` event path as a normal commit.\n *\n * @returns `true` if the value was applied, `false` if rejected by\n * `cellValueChanging.preventDefault()` or the column is not editable.\n */\n public async commitImmediate(\n rowIndex: number,\n columnKey: Keys<T>,\n value: unknown\n ): Promise<boolean> {\n if (!this.enabled) return false;\n const column = this.host.getColumn(columnKey);\n if (!column || !this.isEditable(column)) return false;\n const data = this.host.pageItems[rowIndex] as T | undefined;\n if (!data) return false;\n const oldValue = (data as Record<string, unknown>)[columnKey as string];\n if (Object.is(value, oldValue)) return true;\n const proceed = this.host.emitEvent('cellValueChanging', {\n detail: { key: columnKey, rowIndex, data, oldValue, newValue: value },\n cancelable: true,\n });\n if (!proceed) return false;\n (data as Record<string, unknown>)[columnKey as string] = value;\n this.host.requestUpdate(PIPELINE);\n await this.host.updateComplete;\n this.host.emitEvent('cellValueChanged', {\n detail: { key: columnKey, rowIndex, data, value },\n });\n return true;\n }\n\n /**\n * Discards the current cell edit without writing. In row mode the row edit\n * stays open.\n */\n public cancelCell(): void {\n if (!this.activeCell) return;\n this.activeCell = null;\n this.host.requestUpdate();\n }\n\n /**\n * Commits all pending edits for the currently editing row. Emits\n * `cellValueChanging` for every changed cell and `rowEditEnded` at the end.\n *\n * @returns `true` if the row was applied, `false` if any cell change was\n * cancelled (the row remains in edit mode in that case).\n */\n public async commitRow(): Promise<boolean> {\n const active = this.activeRow;\n if (!active) return false;\n const { rowIndex, data } = active;\n\n // Make sure any open cell editor flushes its pending value first.\n if (this.activeCell?.rowIndex === rowIndex) {\n await this.commitCell();\n }\n\n for (const [columnKey, candidate] of this.pending) {\n const oldValue = (data as Record<string, unknown>)[columnKey as string];\n if (Object.is(candidate, oldValue)) continue;\n const proceed = this.host.emitEvent('cellValueChanging', {\n detail: { key: columnKey, rowIndex, data, oldValue, newValue: candidate },\n cancelable: true,\n });\n if (!proceed) return false;\n (data as Record<string, unknown>)[columnKey as string] = candidate;\n this.host.emitEvent('cellValueChanged', {\n detail: { key: columnKey, rowIndex, data, value: candidate },\n });\n }\n\n this.pending.clear();\n this.activeRow = null;\n this.host.requestUpdate(PIPELINE);\n await this.host.updateComplete;\n this.host.emitEvent('rowEditEnded', { detail: { rowIndex, committed: true } });\n return true;\n }\n\n /**\n * Discards every pending edit for the active row and exits row edit mode.\n * Emits `rowEditEnded` with `committed: false`.\n */\n public cancelRow(): void {\n if (!this.activeRow) return;\n const { rowIndex } = this.activeRow;\n this.activeCell = null;\n this.activeRow = null;\n this.pending.clear();\n this.host.requestUpdate();\n this.host.emitEvent('rowEditEnded', { detail: { rowIndex, committed: false } });\n }\n\n /**\n * Returns the current pending or committed value for `(rowIndex, columnKey)`.\n * Cells in row mode read this so unstaged-but-pending values are reflected\n * during the edit.\n */\n public getValue(rowIndex: number, columnKey: Keys<T>): unknown {\n if (\n this.mode === 'row' &&\n this.activeRow?.rowIndex === rowIndex &&\n this.pending.has(columnKey)\n ) {\n return this.pending.get(columnKey);\n }\n const data = this.host.pageItems[rowIndex] as T | undefined;\n return data ? (data as Record<string, unknown>)[columnKey as string] : undefined;\n }\n}\n"]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ReactiveController } from 'lit';
|
|
2
|
+
import type { GridHost } from '../internal/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The default expansion configuration used when the grid has none set.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* `detailTemplate` defaults to `null` here — the controller will refuse to
|
|
8
|
+
* toggle rows when the configuration is missing or no template is provided.
|
|
9
|
+
*/
|
|
10
|
+
export declare const DEFAULT_EXPANSION_CONFIG: Readonly<{
|
|
11
|
+
enabled: false;
|
|
12
|
+
showToggleColumn: true;
|
|
13
|
+
}>;
|
|
14
|
+
/**
|
|
15
|
+
* Reactive controller backing row expansion (master-detail).
|
|
16
|
+
*
|
|
17
|
+
* @remarks
|
|
18
|
+
* Expansion state is reference-based — the controller holds a `Set` of row
|
|
19
|
+
* data references so the expansion set survives sort / filter / pagination as
|
|
20
|
+
* long as those operations preserve row identity (the default in-place
|
|
21
|
+
* pipeline does). Replacing {@link ApexGrid.data} wholesale invalidates any
|
|
22
|
+
* expanded rows that are no longer present; consumers should call
|
|
23
|
+
* {@link collapseAll} or re-apply expansion afterwards.
|
|
24
|
+
*
|
|
25
|
+
* Mutations go through the cancellable `rowExpanding` event and emit a
|
|
26
|
+
* follow-up `rowExpanded` event, mirroring the pattern used by selection
|
|
27
|
+
* and editing.
|
|
28
|
+
*/
|
|
29
|
+
export declare class ExpansionController<T extends object> implements ReactiveController {
|
|
30
|
+
#private;
|
|
31
|
+
protected host: GridHost<T>;
|
|
32
|
+
/** Set of currently expanded row data references. */
|
|
33
|
+
expanded: Set<T>;
|
|
34
|
+
constructor(host: GridHost<T>);
|
|
35
|
+
hostConnected(): void;
|
|
36
|
+
/** Whether expansion is enabled at the grid level. */
|
|
37
|
+
get enabled(): boolean;
|
|
38
|
+
/** Whether the built-in chevron toggle column should be rendered. */
|
|
39
|
+
get showToggleColumn(): boolean;
|
|
40
|
+
/** Whether `row` is currently expanded. */
|
|
41
|
+
isExpanded(row: T): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Whether `row` is permitted to expand. Combines the grid-wide `enabled`
|
|
44
|
+
* flag, the presence of a `detailTemplate`, and the optional per-row
|
|
45
|
+
* `isExpandable` predicate.
|
|
46
|
+
*/
|
|
47
|
+
canExpand(row: T): boolean;
|
|
48
|
+
/** Snapshot of currently expanded rows in insertion order. */
|
|
49
|
+
expandedRows(): T[];
|
|
50
|
+
/** Toggles the expansion of `row`. */
|
|
51
|
+
toggleRow(row: T): Promise<boolean>;
|
|
52
|
+
/** Expands `row`. No-op when the row is already expanded or not expandable. */
|
|
53
|
+
expandRow(row: T): Promise<boolean>;
|
|
54
|
+
/** Collapses `row`. No-op when the row is not expanded. */
|
|
55
|
+
collapseRow(row: T): Promise<boolean>;
|
|
56
|
+
/**
|
|
57
|
+
* Expands every expandable row in {@link ApexGrid.dataView}. Skips rows
|
|
58
|
+
* the optional `isExpandable` predicate rejects.
|
|
59
|
+
*/
|
|
60
|
+
expandAll(): Promise<boolean>;
|
|
61
|
+
/** Collapses every currently expanded row. */
|
|
62
|
+
collapseAll(): Promise<boolean>;
|
|
63
|
+
/**
|
|
64
|
+
* Replaces the expansion set with `rows`. Used by the public
|
|
65
|
+
* {@link ApexGrid.expandedRows} setter for programmatic control.
|
|
66
|
+
*/
|
|
67
|
+
replaceExpansion(rows: ReadonlyArray<T>): Promise<boolean>;
|
|
68
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
export const DEFAULT_EXPANSION_CONFIG = Object.freeze({
|
|
2
|
+
enabled: false,
|
|
3
|
+
showToggleColumn: true,
|
|
4
|
+
});
|
|
5
|
+
export class ExpansionController {
|
|
6
|
+
constructor(host) {
|
|
7
|
+
this.host = host;
|
|
8
|
+
this.expanded = new Set();
|
|
9
|
+
this.host.addController(this);
|
|
10
|
+
}
|
|
11
|
+
hostConnected() { }
|
|
12
|
+
get enabled() {
|
|
13
|
+
return Boolean(this.host.expansion?.enabled);
|
|
14
|
+
}
|
|
15
|
+
get showToggleColumn() {
|
|
16
|
+
return (this.enabled &&
|
|
17
|
+
(this.host.expansion?.showToggleColumn ?? DEFAULT_EXPANSION_CONFIG.showToggleColumn));
|
|
18
|
+
}
|
|
19
|
+
isExpanded(row) {
|
|
20
|
+
return this.expanded.has(row);
|
|
21
|
+
}
|
|
22
|
+
canExpand(row) {
|
|
23
|
+
if (!this.enabled)
|
|
24
|
+
return false;
|
|
25
|
+
if (typeof this.host.expansion?.detailTemplate !== 'function')
|
|
26
|
+
return false;
|
|
27
|
+
const predicate = this.host.expansion?.isExpandable;
|
|
28
|
+
return predicate ? Boolean(predicate(row)) : true;
|
|
29
|
+
}
|
|
30
|
+
expandedRows() {
|
|
31
|
+
return Array.from(this.expanded);
|
|
32
|
+
}
|
|
33
|
+
async toggleRow(row) {
|
|
34
|
+
if (!this.canExpand(row) && !this.expanded.has(row))
|
|
35
|
+
return false;
|
|
36
|
+
const next = new Set(this.expanded);
|
|
37
|
+
if (next.has(row))
|
|
38
|
+
next.delete(row);
|
|
39
|
+
else
|
|
40
|
+
next.add(row);
|
|
41
|
+
return this.#commit(next);
|
|
42
|
+
}
|
|
43
|
+
async expandRow(row) {
|
|
44
|
+
if (this.expanded.has(row))
|
|
45
|
+
return true;
|
|
46
|
+
if (!this.canExpand(row))
|
|
47
|
+
return false;
|
|
48
|
+
const next = new Set(this.expanded);
|
|
49
|
+
next.add(row);
|
|
50
|
+
return this.#commit(next);
|
|
51
|
+
}
|
|
52
|
+
async collapseRow(row) {
|
|
53
|
+
if (!this.expanded.has(row))
|
|
54
|
+
return true;
|
|
55
|
+
const next = new Set(this.expanded);
|
|
56
|
+
next.delete(row);
|
|
57
|
+
return this.#commit(next);
|
|
58
|
+
}
|
|
59
|
+
async expandAll() {
|
|
60
|
+
if (!this.enabled || typeof this.host.expansion?.detailTemplate !== 'function')
|
|
61
|
+
return false;
|
|
62
|
+
const predicate = this.host.expansion?.isExpandable;
|
|
63
|
+
const next = new Set(this.expanded);
|
|
64
|
+
for (const row of this.host.dataView) {
|
|
65
|
+
if (!predicate || predicate(row))
|
|
66
|
+
next.add(row);
|
|
67
|
+
}
|
|
68
|
+
return this.#commit(next);
|
|
69
|
+
}
|
|
70
|
+
async collapseAll() {
|
|
71
|
+
if (this.expanded.size === 0)
|
|
72
|
+
return true;
|
|
73
|
+
return this.#commit(new Set());
|
|
74
|
+
}
|
|
75
|
+
async replaceExpansion(rows) {
|
|
76
|
+
if (!this.enabled)
|
|
77
|
+
return false;
|
|
78
|
+
const predicate = this.host.expansion?.isExpandable;
|
|
79
|
+
const next = new Set(predicate ? rows.filter((row) => predicate(row)) : rows);
|
|
80
|
+
return this.#commit(next);
|
|
81
|
+
}
|
|
82
|
+
async #commit(next) {
|
|
83
|
+
const added = [];
|
|
84
|
+
const removed = [];
|
|
85
|
+
for (const row of next) {
|
|
86
|
+
if (!this.expanded.has(row))
|
|
87
|
+
added.push(row);
|
|
88
|
+
}
|
|
89
|
+
for (const row of this.expanded) {
|
|
90
|
+
if (!next.has(row))
|
|
91
|
+
removed.push(row);
|
|
92
|
+
}
|
|
93
|
+
if (added.length === 0 && removed.length === 0)
|
|
94
|
+
return true;
|
|
95
|
+
const proceed = this.host.emitEvent('rowExpanding', {
|
|
96
|
+
detail: {
|
|
97
|
+
added,
|
|
98
|
+
removed,
|
|
99
|
+
current: Array.from(this.expanded),
|
|
100
|
+
next: Array.from(next),
|
|
101
|
+
},
|
|
102
|
+
cancelable: true,
|
|
103
|
+
});
|
|
104
|
+
if (!proceed)
|
|
105
|
+
return false;
|
|
106
|
+
this.expanded = next;
|
|
107
|
+
this.host.requestUpdate();
|
|
108
|
+
if (added.length === 1 && removed.length === 0) {
|
|
109
|
+
this.host.announce('Row expanded');
|
|
110
|
+
}
|
|
111
|
+
else if (added.length === 0 && removed.length === 1) {
|
|
112
|
+
this.host.announce('Row collapsed');
|
|
113
|
+
}
|
|
114
|
+
else if (added.length > 0 && next.size === added.length + this.expanded.size - added.length) {
|
|
115
|
+
this.host.announce(`${added.length} rows expanded`);
|
|
116
|
+
}
|
|
117
|
+
else if (removed.length > 0 && next.size === 0) {
|
|
118
|
+
this.host.announce('All rows collapsed');
|
|
119
|
+
}
|
|
120
|
+
this.host.emitEvent('rowExpanded', {
|
|
121
|
+
detail: { added, removed, expanded: Array.from(this.expanded) },
|
|
122
|
+
});
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=expansion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expansion.js","sourceRoot":"","sources":["../../src/controllers/expansion.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController } from 'lit';\nimport type { GridHost } from '../internal/types.js';\n\n/**\n * The default expansion configuration used when the grid has none set.\n *\n * @remarks\n * `detailTemplate` defaults to `null` here — the controller will refuse to\n * toggle rows when the configuration is missing or no template is provided.\n */\nexport const DEFAULT_EXPANSION_CONFIG = Object.freeze({\n enabled: false,\n showToggleColumn: true,\n});\n\n/**\n * Reactive controller backing row expansion (master-detail).\n *\n * @remarks\n * Expansion state is reference-based — the controller holds a `Set` of row\n * data references so the expansion set survives sort / filter / pagination as\n * long as those operations preserve row identity (the default in-place\n * pipeline does). Replacing {@link ApexGrid.data} wholesale invalidates any\n * expanded rows that are no longer present; consumers should call\n * {@link collapseAll} or re-apply expansion afterwards.\n *\n * Mutations go through the cancellable `rowExpanding` event and emit a\n * follow-up `rowExpanded` event, mirroring the pattern used by selection\n * and editing.\n */\nexport class ExpansionController<T extends object> implements ReactiveController {\n /** Set of currently expanded row data references. */\n public expanded: Set<T> = new Set();\n\n constructor(protected host: GridHost<T>) {\n this.host.addController(this);\n }\n\n public hostConnected() {}\n\n /** Whether expansion is enabled at the grid level. */\n public get enabled(): boolean {\n return Boolean(this.host.expansion?.enabled);\n }\n\n /** Whether the built-in chevron toggle column should be rendered. */\n public get showToggleColumn(): boolean {\n return (\n this.enabled &&\n (this.host.expansion?.showToggleColumn ?? DEFAULT_EXPANSION_CONFIG.showToggleColumn)\n );\n }\n\n /** Whether `row` is currently expanded. */\n public isExpanded(row: T): boolean {\n return this.expanded.has(row);\n }\n\n /**\n * Whether `row` is permitted to expand. Combines the grid-wide `enabled`\n * flag, the presence of a `detailTemplate`, and the optional per-row\n * `isExpandable` predicate.\n */\n public canExpand(row: T): boolean {\n if (!this.enabled) return false;\n if (typeof this.host.expansion?.detailTemplate !== 'function') return false;\n const predicate = this.host.expansion?.isExpandable;\n return predicate ? Boolean(predicate(row)) : true;\n }\n\n /** Snapshot of currently expanded rows in insertion order. */\n public expandedRows(): T[] {\n return Array.from(this.expanded);\n }\n\n /** Toggles the expansion of `row`. */\n public async toggleRow(row: T): Promise<boolean> {\n if (!this.canExpand(row) && !this.expanded.has(row)) return false;\n const next = new Set(this.expanded);\n if (next.has(row)) next.delete(row);\n else next.add(row);\n return this.#commit(next);\n }\n\n /** Expands `row`. No-op when the row is already expanded or not expandable. */\n public async expandRow(row: T): Promise<boolean> {\n if (this.expanded.has(row)) return true;\n if (!this.canExpand(row)) return false;\n const next = new Set(this.expanded);\n next.add(row);\n return this.#commit(next);\n }\n\n /** Collapses `row`. No-op when the row is not expanded. */\n public async collapseRow(row: T): Promise<boolean> {\n if (!this.expanded.has(row)) return true;\n const next = new Set(this.expanded);\n next.delete(row);\n return this.#commit(next);\n }\n\n /**\n * Expands every expandable row in {@link ApexGrid.dataView}. Skips rows\n * the optional `isExpandable` predicate rejects.\n */\n public async expandAll(): Promise<boolean> {\n if (!this.enabled || typeof this.host.expansion?.detailTemplate !== 'function') return false;\n const predicate = this.host.expansion?.isExpandable;\n const next = new Set(this.expanded);\n for (const row of this.host.dataView as ReadonlyArray<T>) {\n if (!predicate || predicate(row)) next.add(row);\n }\n return this.#commit(next);\n }\n\n /** Collapses every currently expanded row. */\n public async collapseAll(): Promise<boolean> {\n if (this.expanded.size === 0) return true;\n return this.#commit(new Set());\n }\n\n /**\n * Replaces the expansion set with `rows`. Used by the public\n * {@link ApexGrid.expandedRows} setter for programmatic control.\n */\n public async replaceExpansion(rows: ReadonlyArray<T>): Promise<boolean> {\n if (!this.enabled) return false;\n const predicate = this.host.expansion?.isExpandable;\n const next = new Set(predicate ? rows.filter((row) => predicate(row)) : rows);\n return this.#commit(next);\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.expanded.has(row)) added.push(row);\n }\n for (const row of this.expanded) {\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('rowExpanding', {\n detail: {\n added,\n removed,\n current: Array.from(this.expanded),\n next: Array.from(next),\n },\n cancelable: true,\n });\n if (!proceed) return false;\n\n this.expanded = next;\n this.host.requestUpdate();\n\n if (added.length === 1 && removed.length === 0) {\n this.host.announce('Row expanded');\n } else if (added.length === 0 && removed.length === 1) {\n this.host.announce('Row collapsed');\n } else if (added.length > 0 && next.size === added.length + this.expanded.size - added.length) {\n this.host.announce(`${added.length} rows expanded`);\n } else if (removed.length > 0 && next.size === 0) {\n this.host.announce('All rows collapsed');\n }\n\n this.host.emitEvent('rowExpanded', {\n detail: { added, removed, expanded: Array.from(this.expanded) },\n });\n return true;\n }\n}\n"]}
|
|
@@ -2,15 +2,25 @@ import type { ReactiveController } from 'lit';
|
|
|
2
2
|
import type { ActiveNode, GridHost, Keys } from '../internal/types.js';
|
|
3
3
|
export declare class NavigationController<T extends object> implements ReactiveController {
|
|
4
4
|
protected host: GridHost<T>;
|
|
5
|
-
protected handlers: Map<string, (() => void) | (() => void) | (() => void) | (() => void) | (() => void) | (() => void)>;
|
|
5
|
+
protected handlers: Map<string, (() => void) | (() => void) | (() => void) | (() => void) | (() => void) | (() => void) | (() => void)>;
|
|
6
6
|
protected get virtualizer(): import("../components/virtualizer.js").default;
|
|
7
7
|
protected state: Map<import("../internal/types.js").NavigationState, ActiveNode<any>>;
|
|
8
8
|
protected _active: Readonly<ActiveNode<any>>;
|
|
9
|
-
protected get nextNode():
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
protected
|
|
9
|
+
protected get nextNode(): {
|
|
10
|
+
column: keyof T;
|
|
11
|
+
row: number;
|
|
12
|
+
};
|
|
13
|
+
protected get columns(): Array<{
|
|
14
|
+
key: Keys<T>;
|
|
15
|
+
hidden?: boolean;
|
|
16
|
+
}>;
|
|
17
|
+
protected get visibleColumns(): {
|
|
18
|
+
key: Keys<T>;
|
|
19
|
+
hidden?: boolean;
|
|
20
|
+
}[];
|
|
21
|
+
protected get firstColumn(): Keys<T>;
|
|
22
|
+
protected getPreviousColumn(key: Keys<T>): Keys<T>;
|
|
23
|
+
protected getNextColumn(key: Keys<T>): Keys<T>;
|
|
14
24
|
protected scrollToCell(node: ActiveNode<T>): void;
|
|
15
25
|
get active(): ActiveNode<T>;
|
|
16
26
|
set active(node: ActiveNode<T>);
|
|
@@ -21,6 +31,7 @@ export declare class NavigationController<T extends object> implements ReactiveC
|
|
|
21
31
|
protected arrowUp(): void;
|
|
22
32
|
protected arrowLeft(): void;
|
|
23
33
|
protected arrowRight(): void;
|
|
34
|
+
protected toggleSelection(): void;
|
|
24
35
|
hostConnected(): void;
|
|
25
36
|
hostDisconnected(): void;
|
|
26
37
|
navigate(event: KeyboardEvent): void;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import ApexGridRow from '../components/row.js';
|
|
2
1
|
import { NAVIGATION_STATE, SENTINEL_NODE } from '../internal/constants.js';
|
|
2
|
+
import { GRID_ROW_TAG } from '../internal/tags.js';
|
|
3
|
+
import { getDisplayColumns } from '../internal/utils.js';
|
|
3
4
|
export class NavigationController {
|
|
4
5
|
get virtualizer() {
|
|
5
6
|
return this.host.scrollContainer;
|
|
@@ -11,19 +12,27 @@ export class NavigationController {
|
|
|
11
12
|
: { ...node };
|
|
12
13
|
}
|
|
13
14
|
get columns() {
|
|
14
|
-
return this.host.columns;
|
|
15
|
+
return getDisplayColumns(this.host.columns);
|
|
16
|
+
}
|
|
17
|
+
get visibleColumns() {
|
|
18
|
+
return this.columns.filter((column) => !column.hidden);
|
|
15
19
|
}
|
|
16
20
|
get firstColumn() {
|
|
17
|
-
|
|
21
|
+
const first = this.visibleColumns.at(0);
|
|
22
|
+
return (first?.key ?? this.host.columns[0]?.key);
|
|
18
23
|
}
|
|
19
24
|
getPreviousColumn(key) {
|
|
20
|
-
|
|
25
|
+
const columns = this.visibleColumns;
|
|
26
|
+
const idx = columns.findIndex((column) => column.key === key);
|
|
27
|
+
return columns[Math.max(idx - 1, 0)].key;
|
|
21
28
|
}
|
|
22
29
|
getNextColumn(key) {
|
|
23
|
-
|
|
30
|
+
const columns = this.visibleColumns;
|
|
31
|
+
const idx = columns.findIndex((column) => column.key === key);
|
|
32
|
+
return columns[Math.min(idx + 1, columns.length - 1)].key;
|
|
24
33
|
}
|
|
25
34
|
scrollToCell(node) {
|
|
26
|
-
const row = Array.from(this.virtualizer.querySelectorAll(
|
|
35
|
+
const row = Array.from(this.virtualizer.querySelectorAll(GRID_ROW_TAG)).find((row) => row.index === node.row);
|
|
27
36
|
if (row) {
|
|
28
37
|
row.cells
|
|
29
38
|
.find((cell) => cell.column.key === node.column)
|
|
@@ -48,6 +57,7 @@ export class NavigationController {
|
|
|
48
57
|
ArrowRight: this.arrowRight,
|
|
49
58
|
Home: this.home,
|
|
50
59
|
End: this.end,
|
|
60
|
+
' ': this.toggleSelection,
|
|
51
61
|
}));
|
|
52
62
|
this.state = NAVIGATION_STATE;
|
|
53
63
|
this._active = SENTINEL_NODE;
|
|
@@ -58,13 +68,13 @@ export class NavigationController {
|
|
|
58
68
|
this.virtualizer.element(this.active.row)?.scrollIntoView({ block: 'nearest' });
|
|
59
69
|
}
|
|
60
70
|
end() {
|
|
61
|
-
this.active = Object.assign(this.nextNode, { row: this.host.
|
|
71
|
+
this.active = Object.assign(this.nextNode, { row: this.host.pageItems.length - 1 });
|
|
62
72
|
this.virtualizer.element(this.active.row)?.scrollIntoView({ block: 'nearest' });
|
|
63
73
|
}
|
|
64
74
|
arrowDown() {
|
|
65
75
|
const next = this.nextNode;
|
|
66
76
|
this.active = Object.assign(next, {
|
|
67
|
-
row: Math.min(next.row + 1, this.host.
|
|
77
|
+
row: Math.min(next.row + 1, this.host.pageItems.length - 1),
|
|
68
78
|
});
|
|
69
79
|
this.virtualizer.element(next.row)?.scrollIntoView({ block: 'nearest' });
|
|
70
80
|
}
|
|
@@ -83,6 +93,12 @@ export class NavigationController {
|
|
|
83
93
|
this.active = Object.assign(next, { column: this.getNextColumn(next.column) });
|
|
84
94
|
this.scrollToCell(this.active);
|
|
85
95
|
}
|
|
96
|
+
toggleSelection() {
|
|
97
|
+
const data = this.host.pageItems[this.active.row];
|
|
98
|
+
if (!data)
|
|
99
|
+
return;
|
|
100
|
+
void this.host.toggleRowSelection(data);
|
|
101
|
+
}
|
|
86
102
|
hostConnected() { }
|
|
87
103
|
hostDisconnected() {
|
|
88
104
|
this.active = SENTINEL_NODE;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../src/controllers/navigation.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../src/controllers/navigation.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController } from 'lit';\nimport type ApexGridRow from '../components/row.js';\nimport { NAVIGATION_STATE, SENTINEL_NODE } from '../internal/constants.js';\nimport { GRID_ROW_TAG } from '../internal/tags.js';\nimport type { ActiveNode, GridHost, Keys } from '../internal/types.js';\nimport { getDisplayColumns } from '../internal/utils.js';\n\nexport class NavigationController<T extends object> implements ReactiveController {\n protected handlers = new Map(\n Object.entries({\n ArrowDown: this.arrowDown,\n ArrowUp: this.arrowUp,\n ArrowLeft: this.arrowLeft,\n ArrowRight: this.arrowRight,\n Home: this.home,\n End: this.end,\n ' ': this.toggleSelection,\n })\n );\n\n protected get virtualizer() {\n // @ts-expect-error - Protected member access\n return this.host.scrollContainer;\n }\n\n protected state = NAVIGATION_STATE;\n protected _active = SENTINEL_NODE;\n\n protected get nextNode() {\n const node = this.state.get('current')!;\n return node === SENTINEL_NODE\n ? { column: this.firstColumn, row: 0 }\n : ({ ...node } as ActiveNode<T>);\n }\n\n protected get columns(): Array<{ key: Keys<T>; hidden?: boolean }> {\n return getDisplayColumns(this.host.columns) as unknown as Array<{\n key: Keys<T>;\n hidden?: boolean;\n }>;\n }\n\n protected get visibleColumns() {\n return this.columns.filter((column) => !column.hidden);\n }\n\n protected get firstColumn(): Keys<T> {\n const first = this.visibleColumns.at(0);\n return (first?.key ?? (this.host.columns[0]?.key as Keys<T>)) as Keys<T>;\n }\n\n protected getPreviousColumn(key: Keys<T>): Keys<T> {\n const columns = this.visibleColumns;\n const idx = columns.findIndex((column) => column.key === key);\n return columns[Math.max(idx - 1, 0)].key;\n }\n\n protected getNextColumn(key: Keys<T>): Keys<T> {\n const columns = this.visibleColumns;\n const idx = columns.findIndex((column) => column.key === key);\n return columns[Math.min(idx + 1, columns.length - 1)].key;\n }\n\n protected scrollToCell(node: ActiveNode<T>) {\n const row = Array.from(this.virtualizer.querySelectorAll(GRID_ROW_TAG)).find(\n (row) => (row as unknown as ApexGridRow<T>).index === node.row\n ) as unknown as ApexGridRow<T>;\n\n if (row) {\n row.cells\n .find((cell) => cell.column.key === node.column)\n ?.scrollIntoView({ block: 'nearest' });\n }\n }\n\n public get active() {\n return this._active as ActiveNode<T>;\n }\n\n public set active(node: ActiveNode<T>) {\n this._active = node;\n this.state.set('previous', this._active);\n this.state.set('current', node);\n this.host.requestUpdate();\n }\n\n constructor(protected host: GridHost<T>) {\n this.host.addController(this);\n }\n\n protected home() {\n this.active = Object.assign(this.nextNode, { row: 0 });\n this.virtualizer.element(this.active.row)?.scrollIntoView({ block: 'nearest' });\n }\n\n protected end() {\n this.active = Object.assign(this.nextNode, { row: this.host.pageItems.length - 1 });\n this.virtualizer.element(this.active.row)?.scrollIntoView({ block: 'nearest' });\n }\n\n protected arrowDown() {\n const next = this.nextNode;\n\n this.active = Object.assign(next, {\n row: Math.min(next.row + 1, this.host.pageItems.length - 1),\n });\n this.virtualizer.element(next.row)?.scrollIntoView({ block: 'nearest' });\n }\n\n protected arrowUp() {\n const next = this.nextNode;\n this.active = Object.assign(next, { row: Math.max(0, next.row - 1) });\n this.virtualizer.element(next.row)?.scrollIntoView({ block: 'nearest' });\n }\n\n protected arrowLeft() {\n const next = this.nextNode;\n this.active = Object.assign(next, { column: this.getPreviousColumn(next.column) });\n this.scrollToCell(this.active);\n }\n\n protected arrowRight() {\n const next = this.nextNode;\n this.active = Object.assign(next, { column: this.getNextColumn(next.column) });\n this.scrollToCell(this.active);\n }\n\n protected toggleSelection() {\n // Space on the focused grid toggles selection of the active row.\n // No-op when selection is disabled or there's no active row.\n const data = this.host.pageItems[this.active.row] as T | undefined;\n if (!data) return;\n void this.host.toggleRowSelection(data);\n }\n\n public hostConnected() {}\n\n public hostDisconnected() {\n this.active = SENTINEL_NODE as ActiveNode<T>;\n this.state = NAVIGATION_STATE;\n }\n\n public navigate(event: KeyboardEvent) {\n if (this.handlers.has(event.key)) {\n event.preventDefault();\n this.handlers.get(event.key)!.call(this);\n }\n }\n}\n"]}
|