@itrocks/list 0.13.6 → 0.17.1

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/README.md CHANGED
@@ -185,16 +185,33 @@ the dependency hook. The configured data source remains responsible for storage
185
185
  `GET /<business-route>/list?column=<path>&rowIds=<comma-separated-identifiers>` uses the HTML action and returns a JSON
186
186
  column descriptor with rendered cells keyed by root id. It intersects visible ids with the current filters and mandatory
187
187
  scope, makes one projection and performs no count. Invalid selections return 400. Full entity JSON remains available
188
- through `/list/json`. Column configuration saves use the established `columns` parameter and return 204.
188
+ through `/list/json`. Adding `columns=<ordered-paths>` saves the column configuration in the same request.
189
+ Configuration-only requests return 204. The browser serialises both kinds of request to preserve the latest order.
189
190
 
190
191
  The browser entry `@itrocks/list/property-select.js` exports `ListPropertySelect`, with explicit metadata, table
191
192
  addition, column persistence and narrow pane ports. The framework mounts it only on compatible lists and disposes it on
192
- navigation. It reserves pending paths, confirms accepted additions, reveals/highlights duplicates without moving focus,
193
- and checks row identity before insertion. Failed selection leaves the context intact. It loads only currently visible
194
- row ids through the current feed position; changing the row view while loading rejects the obsolete response with a
195
- retry message. Clicks and desktop drags create empty columns and immediately load values asynchronously through the
196
- public table transaction. A new dragged column stays hidden until the pointer first enters the table, so its values can
197
- arrive before it appears. Drag admission waits for drop.
193
+ navigation. It reserves pending paths and confirms accepted additions. The checkbox, its cell and the property name
194
+ toggle terminal and relation columns. The checkbox clears immediately before the red removal flash. Reselecting during that flash cancels removal,
195
+ checks the property again and highlights the retained column green, then saves the restored configuration.
196
+ The removal configuration is sent at the initial click, independently of the animation; deleting the cells sends no
197
+ additional request.
198
+ After a completed removal, a later click returns the column immediately right of its former left neighbour, or left of its former right neighbour if the left one is absent. If neither remains,
199
+ it is appended. Neighbours are kept only in this pane instance's memory and cleared on folding, closure or disposal;
200
+ they never enter session state. Drag placement continues to follow the drop position.
201
+ An explicit addition intention always requests addition: an existing column is revealed and highlighted green without removal.
202
+ Property names and checkbox cells toggle the same selection; chevrons and intermediate path segments navigate.
203
+ The consumer checks row identity before insertion. Failed selection leaves the context intact. It loads only currently
204
+ visible row ids through the current feed position; changing the row view while loading rejects the obsolete response with a
205
+ discreet red background. Clicks commit the translated full-path heading and filter locally, before any server response.
206
+ One asynchronous request
207
+ then saves the column order and loads the values; there is no separate admission request. A new drag calls the same
208
+ addition code as a click with `{ effects: false, hidden: true }`, then moves the committed column through the standard
209
+ table reorder operation with `{ adding: true, removal: true }`. Its combined request loads values immediately while
210
+ cells remain hidden, without width animation, flash or automatic scroll. On first pointer entry, the column appears
211
+ with any values already received and uses a green background and plus indicator. An outside drop deletes it through
212
+ the standard header-origin removal and saves once, even if the pointer never entered. Cancelling a new addition also
213
+ removes it. Dropping inside saves only a changed column order; it never reloads values.
214
+ The optional `request(query, signal)` port shares the list persistence queue.
198
215
  Existing-column moves and removals only save the column configuration. The header contains each label and its filter
199
216
  together, so it uses the table addition plugin's single-header contract.
200
217
 
@@ -227,15 +244,13 @@ The browser displays recoverable persistence errors.
227
244
 
228
245
  ## External column transport
229
246
 
230
- The list consumer supplies immediate empty cells, authorises admission and loads all currently loaded row ids through
231
- the same projection/rendering route used for click additions. `validate=1` on a `column` request checks representation
232
- and permission without selecting records; it prevents unsupported object and nested plural paths from being confirmed
233
- on drop.
234
- Admission is independent of the optional values request. A slow or failed values request leaves valid drops accepted;
235
- failed cells expose `Retry values` without another addition or recent entry.
247
+ The list consumer supplies cells containing a non-breaking space and loads all currently loaded row ids through
248
+ one combined configuration/projection request after the local commit. A failed values request leaves the column
249
+ in place, with a discreet red background on its data cells. The spaces remain; no error text, retry button or automatic
250
+ retry is added. The translated full path comes from the selector's displayed row and available branch metadata.
236
251
 
237
- Pending paths and the active drag are reserved before asynchronous work. Used paths retain their committed order
238
- throughout the drag, and configuration saves exclude provisional cells. New accepted drops feed recents; moves and
252
+ Pending paths and the active drag are reserved before asynchronous work. Used paths include the new column immediately
253
+ and retain their committed order throughout the drag. New accepted drops feed recents; moves and
239
254
  cancellations do not. Row-view changes and view disposal cancel transport. The desktop pane stays open, and capture is
240
255
  owned by the selector's surviving source controller. The consumer imports no home implementation, SQL or hydration code.
241
256
 
@@ -245,6 +260,36 @@ cancellation/rejection; it never focuses the search field. View disposal removes
245
260
  restoration. Custom pane adapters may implement the same optional suspension argument without importing `home`.
246
261
 
247
262
  `node test/browser-requests.mjs` checks request counts and real mouse gestures in Chromium with synthetic HTTP data.
248
- It covers initial mount, click placeholders, hidden drag preloading, reveal on table entry, values for 80 loaded rows,
249
- outside cancellation, duplicates, reordering and removal.
263
+ It covers initial mount, click placeholders, hidden drag additions, values for 80 loaded rows before entry or drop,
264
+ green addition indicators, outside removal before and after entry, terminal toggles and reordering.
265
+ `node test/browser-removal-cancel.mjs` checks immediate unchecking and cancellation during the red flash through
266
+ checkbox, cell and name clicks, for terminal and relation columns, with an immediate removal save followed by a restored configuration save.
267
+ `node test/browser-column-positions.mjs` checks neighbour-based reinsertion in the real table, both fallbacks,
268
+ and memory expiry on closure, including closure during a pending removal. It starts its own synthetic HTTP fixture.
250
269
  Set `PS_PLAYWRIGHT_MODULE` to an installed Playwright module when using a different runtime.
270
+
271
+ `node test/browser-toggle.mjs` exercises the real framework list against the synthetic recipe database on port 13316
272
+ (`PS_DATABASE_PORT` overrides it). It starts and stops its own application instance and checks desktop keyboard, touch,
273
+ reduced motion, horizontal reveal, removal and temporary-row transitions. `PS_TOGGLE_URL` reuses an existing instance
274
+ of that recipe instead. The synthetic database must already be running; this test never resets it.
275
+
276
+ ## Component column batches
277
+
278
+ Selecting a component group sends one request with `loadColumns` listing missing paths, `columns` containing the
279
+ complete final configuration and `rowIds` identifying visible roots. The response contains `columns`, each with
280
+ `path`, `label`, `sortable` and rendered `rows`. Empty `loadColumns` saves group removals without querying records.
281
+ Failed batch projections preserve the saved configuration. Checkboxes update optimistically before metadata or HTTP
282
+ requests, for both individual selections and groups. The host commits placeholders and starts the shared green flash
283
+ before the values request; early values remain masked until the flash finishes, while late values appear immediately.
284
+ Removal starts one red flash after publishing the unchecked state, then sends the configuration request. Front-end
285
+ columns disappear at the animation deadline even if that request is still pending. Group actions use one HTTP request.
286
+
287
+ Sub-properties reached through one collection marked `@Component` are projected together when its element type
288
+ has one matching `@Composite` back-reference. The list reads visible root identities once, then all requested fields
289
+ in one query per component collection, restricted to those identities. Singular ancestors and related object labels
290
+ share these queries. Values retain component order and duplicates; these columns are not sortable.
291
+ The same projection is used when reloading the list or adding an individual component sub-property.
292
+
293
+ `test/browser-component-batch.mjs` verifies real browser interactions, HTTP request counts, MySQL query counts,
294
+ rendered values, mandatory row scope, partial selection, removal and session restoration. It creates and cleans up
295
+ its own temporary database and server using the existing selector integration recipe.
package/build.js CHANGED
@@ -11,24 +11,27 @@ import { tableByElement } from '@itrocks/table/table.js';
11
11
  import { xTargetCall } from '@itrocks/xtarget/xtarget.js';
12
12
  build('article[data-action="list"] > form > table.objects', table => {
13
13
  let saving = Promise.resolve();
14
- const saveColumns = () => {
14
+ const request = (query, signal) => {
15
+ const form = table.closest('form');
16
+ const result = saving.then(() => fetch(`${form.action}?${query}`, { keepalive: true, signal }));
17
+ saving = result.then(() => { }, () => { });
18
+ return result;
19
+ };
20
+ const saveColumns = (paths) => {
15
21
  const columns = table.tHead?.rows[0].querySelectorAll(':scope > th[data-property]:not(.column-add-provisional)');
16
22
  const form = table.closest('form');
17
23
  if (!columns || !form)
18
24
  return Promise.resolve();
19
- const properties = Array.from(columns, column => encodeURIComponent(column.dataset.property ?? ''));
20
- const url = form.action + '?columns=' + properties.join(',');
21
- saving = saving.catch(() => { }).then(async () => {
22
- const response = await fetch(url, { keepalive: true });
25
+ const properties = paths ?? Array.from(columns, column => column.dataset.property ?? '');
26
+ return request(new URLSearchParams({ columns: properties.join(',') })).then(response => {
23
27
  if (!response.ok)
24
28
  throw new Error('Unable to save columns');
25
29
  });
26
- return saving;
27
30
  };
28
31
  const tableColumnReorder = new TableColumnReorder({
29
32
  isReorderable: header => header.dataset.property !== undefined,
30
- onRemove: saveColumns,
31
- onReorder: saveColumns
33
+ onRemove: () => saveColumns(),
34
+ onReorder: () => saveColumns()
32
35
  });
33
36
  const tableLink = new TableLink({
34
37
  call: url => xTargetCall(url, 'main'),
@@ -46,7 +49,7 @@ build('article[data-action="list"] > form > table.objects', table => {
46
49
  ]
47
50
  });
48
51
  table.dispatchEvent(new CustomEvent('itrocks:list-ready', {
49
- bubbles: true, detail: { addition, instance, saveColumns }
52
+ bubbles: true, detail: { addition, instance, request, saveColumns }
50
53
  }));
51
54
  table.addEventListener('click', event => {
52
55
  const element = event.target;
package/cjs/columns.js CHANGED
@@ -5,6 +5,7 @@ exports.projectColumns = projectColumns;
5
5
  const list_properties_1 = require("@itrocks/list-properties");
6
6
  const storage_1 = require("@itrocks/storage");
7
7
  const storage_2 = require("@itrocks/storage");
8
+ const storage_3 = require("@itrocks/storage");
8
9
  const parameter_1 = require("./parameter");
9
10
  const projection_1 = require("./projection");
10
11
  class Columns extends parameter_1.Parameter {
@@ -74,12 +75,51 @@ class Columns extends parameter_1.Parameter {
74
75
  exports.Columns = Columns;
75
76
  async function projectColumns(type, columns, search, options) {
76
77
  const singular = columns.filter(column => !column.multiple);
77
- const result = await (0, storage_1.dataSource)().select(type, singular.map(column => column.selection()), search, options);
78
+ const selections = singular.map(column => column.selection());
79
+ const parents = [...new Set(columns.flatMap(column => column.component?.parent ? [column.component.parent] : []))]
80
+ .filter(path => !singular.some(column => column.name === path));
81
+ selections.push(...parents.map(path => ({ path, objectFields: [] })));
82
+ const result = await (0, storage_1.dataSource)().select(type, selections, search, options);
78
83
  if (!columns.some(column => column.multiple) || !result.rows.length)
79
84
  return result;
80
85
  const ids = result.rows.map(row => row.rootId);
81
- const objects = await (0, storage_1.dataSource)().search(type, search, [new storage_2.Identifiers(ids)]);
86
+ const direct = columns.filter(column => column.multiple && !column.component);
87
+ const objects = direct.length ? await (0, storage_1.dataSource)().search(type, search, [new storage_2.Identifiers(ids)]) : [];
82
88
  const byId = new Map(objects.map(object => [String(object.id), object]));
89
+ const components = new Map();
90
+ for (const path of new Set(columns.flatMap(column => column.component ? [column.component.path] : []))) {
91
+ const grouped = columns.filter(column => column.component?.path === path);
92
+ const component = grouped[0].component;
93
+ const parentIds = result.rows.map(row => {
94
+ if (!component.parent)
95
+ return row.rootId;
96
+ const index = singular.findIndex(column => column.name === component.parent);
97
+ const cell = row.cells[index < 0 ? singular.length + parents.indexOf(component.parent) : index];
98
+ return cell.kind === 'value' ? cell.value?.id : undefined;
99
+ });
100
+ const selectedIds = [...new Set(parentIds.filter((id) => id !== undefined))];
101
+ const selections = new Map([
102
+ [component.inverse, { path: component.inverse, objectFields: [] }]
103
+ ]);
104
+ for (const column of grouped)
105
+ selections.set(column.component.column.name, column.component.column.selection());
106
+ const names = [...selections.keys()];
107
+ const values = selectedIds.length ? await (0, storage_1.dataSource)().select(component.type, [...selections.values()], { [component.inverse]: selectedIds }, [new storage_3.Sort([])]) : undefined;
108
+ for (const column of grouped) {
109
+ const byParent = new Map();
110
+ for (const row of values?.rows ?? []) {
111
+ const owner = row.cells[0];
112
+ if (owner.kind !== 'value' || !owner.value)
113
+ continue;
114
+ const id = String(owner.value.id);
115
+ let cells = byParent.get(id);
116
+ if (!cells)
117
+ byParent.set(id, cells = []);
118
+ cells.push(row.cells[names.indexOf(column.component.column.name)]);
119
+ }
120
+ components.set(column.name, new Map(result.rows.map((row, index) => [String(row.rootId), byParent.get(String(parentIds[index])) ?? []])));
121
+ }
122
+ }
83
123
  return {
84
124
  rootType: type,
85
125
  columns: columns.map(column => ({ path: column.name, kind: column.multiple ? 'scalar' :
@@ -89,6 +129,9 @@ async function projectColumns(type, columns, search, options) {
89
129
  const cells = await Promise.all(columns.map(async (column) => {
90
130
  if (!column.multiple)
91
131
  return row.cells[singular.indexOf(column)];
132
+ if (column.component)
133
+ return { kind: 'value',
134
+ value: components.get(column.name).get(String(row.rootId)) ?? [] };
92
135
  const value = object ? await object[column.name] : [];
93
136
  return { kind: 'value', value: value ?? [] };
94
137
  }));
@@ -1 +1 @@
1
- {"version":3,"file":"columns.js","sourceRoot":"","sources":["../src/columns.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"columns.js","sourceRoot":"","sources":["../src/columns.ts"],"names":[],"mappings":"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"}
package/cjs/list.js CHANGED
@@ -89,7 +89,8 @@ let List = (() => {
89
89
  for (const field of column.fields ?? [])
90
90
  await access.authorise(`${column.name}.${field}`);
91
91
  }
92
- if (request.request.data.columns !== undefined) {
92
+ const batch = typeof request.request.data.loadColumns === 'string';
93
+ if (request.request.data.columns !== undefined && typeof request.request.data.column !== 'string' && !batch) {
93
94
  (0, persist_2.save)(this, request.request, type);
94
95
  return this.htmlResponse('', 204);
95
96
  }
@@ -98,13 +99,44 @@ let List = (() => {
98
99
  for (const column of columns)
99
100
  column.search = filter.search[column.name] ?? '';
100
101
  sort.getParams(request.request, type);
101
- (0, persist_2.save)(this, request.request, type);
102
+ if (!batch)
103
+ (0, persist_2.save)(this, request.request, type);
102
104
  const options = feed.readOptions(filter.readOptions(sort.readOptions()));
103
105
  const search = Object.assign(filter.searchObject(type), scope);
104
106
  for (const path of Object.keys(search))
105
107
  await access.authorise(path);
106
108
  for (const path of Object.keys(sort.sorted ?? {}))
107
109
  await access.authorise(path);
110
+ if (batch) {
111
+ try {
112
+ const names = request.request.data.loadColumns.split(',').filter(Boolean);
113
+ if (new Set(names).size !== names.length)
114
+ throw new Error('Duplicate column paths');
115
+ const requested = names.map(path => new projection_1.ListColumn(type, path));
116
+ for (const column of requested) {
117
+ if (!columns.serialize().includes(column.name))
118
+ throw new Error('Column absent from configuration');
119
+ await access.authorise(column.name);
120
+ for (const field of column.fields ?? [])
121
+ await access.authorise(`${column.name}.${field}`);
122
+ }
123
+ const ids = request.request.data.rowIds;
124
+ if (typeof ids !== 'string')
125
+ throw new Error('Visible row identities are required');
126
+ const result = requested.length ? await (0, columns_2.projectColumns)(type, requested, search, [new storage_1.Identifiers(ids ? ids.split(',') : [])]) : undefined;
127
+ const response = await Promise.all(requested.map(async (column, index) => ({
128
+ label: column.label, path: column.name, sortable: column.sortable,
129
+ rows: await Promise.all(result.rows.map(async (row) => ({
130
+ id: row.rootId, html: await column.output(row.cells[index])
131
+ })))
132
+ })));
133
+ (0, persist_2.save)(this, request.request, type);
134
+ return this.jsonResponse({ columns: response });
135
+ }
136
+ catch (error) {
137
+ return this.jsonResponse({ error: error instanceof Error ? error.message : 'Columns unavailable' }, 400);
138
+ }
139
+ }
108
140
  if (typeof request.request.data.column === 'string') {
109
141
  try {
110
142
  const path = request.request.data.column;
package/cjs/list.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"list.js","sourceRoot":"","sources":["../src/list.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../src/list.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,13 @@ export declare function representationFields(type: Type, ancestors?: readonly Ty
11
11
  export declare class ListColumn {
12
12
  readonly root: Type;
13
13
  readonly name: string;
14
+ readonly component?: {
15
+ column: ListColumn;
16
+ inverse: string;
17
+ parent: string;
18
+ path: string;
19
+ type: Type;
20
+ };
14
21
  readonly field: string;
15
22
  readonly fields?: readonly string[];
16
23
  readonly label: string;
package/cjs/projection.js CHANGED
@@ -3,13 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ListColumn = void 0;
4
4
  exports.listRepresentationDependsOn = listRepresentationDependsOn;
5
5
  exports.representationFields = representationFields;
6
+ const class_type_1 = require("@itrocks/class-type");
6
7
  const class_view_1 = require("@itrocks/class-view");
7
8
  const class_view_2 = require("@itrocks/class-view");
8
9
  const class_view_3 = require("@itrocks/class-view");
9
10
  const composition_1 = require("@itrocks/composition");
11
+ const composition_2 = require("@itrocks/composition");
10
12
  const core_transformers_1 = require("@itrocks/core-transformers");
11
13
  const core_transformers_2 = require("@itrocks/core-transformers");
12
14
  const property_view_1 = require("@itrocks/property-view");
15
+ const reflect_1 = require("@itrocks/reflect");
16
+ const reflect_2 = require("@itrocks/reflect");
13
17
  const reflect_path_1 = require("@itrocks/reflect-path");
14
18
  const reflect_path_2 = require("@itrocks/reflect-path");
15
19
  const storage_1 = require("@itrocks/storage");
@@ -41,6 +45,7 @@ function representationFields(type, ancestors = [], property) {
41
45
  class ListColumn {
42
46
  root;
43
47
  name;
48
+ component;
44
49
  field;
45
50
  fields;
46
51
  label;
@@ -58,12 +63,36 @@ class ListColumn {
58
63
  const resolved = (0, reflect_path_2.resolvePropertyPath)(root, name);
59
64
  this.multiple = resolved.targetMultiplicity === 'many';
60
65
  this.sortable = !this.multiple;
61
- if (resolved.targetMultiplicity !== 'one' && !(this.multiple && resolved.segments.length === 1)) {
66
+ if (this.multiple && resolved.segments.length > 1) {
67
+ const index = resolved.segments.findIndex(property => property.relation?.targetMultiplicity === 'many');
68
+ const property = resolved.segments[index];
69
+ const target = property?.relation?.target;
70
+ if (target && (0, composition_1.componentOf)(property.class.type, property.name) && index < resolved.segments.length - 1) {
71
+ const inverses = Object.keys(new reflect_1.ReflectClass(target).propertyTypes).filter(name => {
72
+ if (!(0, composition_2.compositeOf)(target, name))
73
+ return false;
74
+ const relation = new reflect_2.ReflectProperty(target, name).relation;
75
+ return relation?.target && (0, class_type_1.inherits)(property.class.type, relation.target);
76
+ });
77
+ if (inverses.length === 1) {
78
+ const column = new ListColumn(target, name.split('.').slice(index + 1).join('.'));
79
+ if (!column.multiple)
80
+ this.component = {
81
+ column, inverse: inverses[0], parent: name.split('.').slice(0, index).join('.'),
82
+ path: name.split('.').slice(0, index + 1).join('.'), type: target
83
+ };
84
+ }
85
+ }
86
+ }
87
+ if (resolved.targetMultiplicity !== 'one' && !(this.multiple && resolved.segments.length === 1)
88
+ && !this.component) {
62
89
  throw new storage_1.SelectionError(resolved.targetMultiplicity === 'many' ? 'multiple-values' : 'unsupported-selection', name);
63
90
  }
64
91
  this.property = new reflect_path_1.ReflectPropertyPath(root, name);
65
92
  this.target = resolved.target;
66
93
  this.label = resolved.segments.map(property => (0, translate_1.tr)((0, property_view_1.displayOf)(property.class.type, property.name))).join('.');
94
+ if (this.component)
95
+ this.fields = this.component.column.fields;
67
96
  if (resolved.target && !this.multiple)
68
97
  this.fields = representationFields(resolved.target, [], resolved.property);
69
98
  }
@@ -72,6 +101,12 @@ class ListColumn {
72
101
  return '';
73
102
  if (cell.kind === 'unloaded')
74
103
  return '<span class="unavailable">Unavailable</span>';
104
+ if (this.component) {
105
+ const cells = cell.value;
106
+ const values = await Promise.all(cells.map(cell => this.component.column.output(cell)));
107
+ return (0, multiple_output_1.multipleOutputOf)(this.property.class.type, this.property.name) === 'lines'
108
+ ? '<ul>' + values.map(value => `<li>${value}</li>`).join('') + '</ul>' : values.join(', ');
109
+ }
75
110
  if (this.fields && this.fields.some(field => !cell.loadedFields?.includes(field)))
76
111
  throw new storage_1.SelectionError('unsupported-selection', this.name);
77
112
  if (this.multiple && (0, composition_1.componentOf)(this.property.class.type, this.property.name)) {
@@ -1 +1 @@
1
- {"version":3,"file":"projection.js","sourceRoot":"","sources":["../src/projection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"projection.js","sourceRoot":"","sources":["../src/projection.ts"],"names":[],"mappings":"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"}
package/list.css CHANGED
@@ -33,7 +33,7 @@ table.objects {
33
33
  overflow-y: auto;
34
34
  }
35
35
  table.objects th {
36
- background: #f8f8f8;
36
+ background: #eee;
37
37
  }
38
38
  table.objects tr td, table.objects tr th {
39
39
  border-bottom: 1px solid #d8d8d8;
@@ -51,7 +51,7 @@ table.objects > * > tr > .column-reorder {
51
51
  --column-reorder-tint: inset 0 0 0 100vmax rgb(0 0 0 / 2%);
52
52
  box-shadow: var(--column-reorder-tint);
53
53
  }
54
- table.objects > * > tr > .column-reorder.column-add-provisional {
54
+ table.objects > * > tr > .column-reorder.adding {
55
55
  --column-reorder-tint: inset 0 0 0 100vmax rgb(47 137 67 / 5%);
56
56
  }
57
57
  table.objects > * > tr > .column-reorder.moving {
@@ -84,10 +84,10 @@ table.objects > tbody:first-child > tr:nth-child(odd) > td {
84
84
  table.objects > thead > tr.properties > th {
85
85
  user-select: none;
86
86
  }
87
- table.objects > thead > tr.properties > th.moving, table.objects > thead > tr.properties > th.removing {
87
+ table.objects > thead > tr.properties > th.adding, table.objects > thead > tr.properties > th.moving, table.objects > thead > tr.properties > th.removing {
88
88
  z-index: 2;
89
89
  }
90
- table.objects > thead > tr.properties > th.moving::before, table.objects > thead > tr.properties > th.removing::before, table.objects > thead > tr.properties > th.column-add-provisional.column-reorder::before {
90
+ table.objects > thead > tr.properties > th.adding::before, table.objects > thead > tr.properties > th.moving::before, table.objects > thead > tr.properties > th.removing::before {
91
91
  content: "";
92
92
  display: inline-block;
93
93
  height: 1em;
@@ -108,7 +108,7 @@ table.objects > thead > tr.properties > th.removing::before {
108
108
  background: #d51d2c;
109
109
  mask-image: url("column-remove.svg");
110
110
  }
111
- table.objects > thead > tr.properties > th.column-add-provisional.column-reorder::before {
111
+ table.objects > thead > tr.properties > th.adding::before {
112
112
  background: #2f8943;
113
113
  mask-image: url("column-add.svg");
114
114
  }
@@ -164,12 +164,15 @@ table.objects > thead > tr.search > th > input:focus {
164
164
  outline: none;
165
165
  box-shadow: inset 0 0 0 2px Highlight, var(--column-reorder-tint, 0 0 transparent);
166
166
  }
167
- table.objects > thead + tbody > tr:nth-child(even) > td {
167
+ table.objects > thead + tbody > tr:nth-child(even) {
168
168
  background: #f8f8f8;
169
169
  }
170
- table.objects > thead + tbody > tr:nth-child(odd) > td {
170
+ table.objects > thead + tbody > tr:nth-child(odd) {
171
171
  background: white;
172
172
  }
173
+ table.objects > thead + tbody > tr:hover {
174
+ background: #f4f4f4;
175
+ }
173
176
 
174
177
  :not(article, article > form) > table.objects td, :not(article, article > form) > table.objects th {
175
178
  border-top: 1px solid #d8d8d8;
package/package.json CHANGED
@@ -93,5 +93,5 @@
93
93
  "build:front": "tsc -p src/front/tsconfig.json",
94
94
  "test": "npm run build:cjs && node --test \"test/**/*.test.mjs\""
95
95
  },
96
- "version": "0.13.6"
96
+ "version": "0.17.1"
97
97
  }
@@ -25,32 +25,41 @@ export type ListSelectorOptions = {
25
25
  addition: TableColumnAdd;
26
26
  metadata: MetadataProvider;
27
27
  mountSelector(content: HTMLElement, title: string): PaneHandle;
28
- saveColumns(): Promise<void>;
28
+ request?(query: URLSearchParams, signal: AbortSignal): Promise<Response>;
29
+ saveColumns(paths?: readonly string[]): Promise<void>;
29
30
  templateUrl?: string;
30
31
  };
31
32
  export declare class ListPropertySelect implements ConsumerPort {
32
33
  private readonly article;
33
34
  private readonly options;
34
35
  private readonly abort;
36
+ private batch;
35
37
  private readonly button;
38
+ private readonly content;
36
39
  private disposed;
37
40
  private drag?;
38
41
  private readonly observer;
39
42
  private readonly pane;
40
43
  private readonly pending;
44
+ private readonly removals;
45
+ private removedColumns;
46
+ private readonly unsubscribePane;
41
47
  readonly view: PropertySelectView;
42
48
  constructor(article: HTMLElement, options: ListSelectorOptions);
43
49
  private loadColumn;
50
+ private loadColumns;
51
+ private columnContent;
44
52
  private add;
45
53
  begin(intent: SelectionIntent, sample: PointerSample): void;
46
54
  private columnSource;
47
- private validateColumn;
48
55
  cancel(id: string): void;
49
- private previewError;
56
+ private displayedPaths;
50
57
  dispose(): void;
51
58
  end(id: string, sample: PointerSample): Promise<Outcome>;
52
59
  mount(): Promise<boolean>;
53
60
  move(id: string, sample: PointerSample): void;
54
61
  paths(): string[];
62
+ private remove;
55
63
  select(intent: SelectionIntent, signal: AbortSignal): Promise<Outcome>;
64
+ selectMany(intents: readonly SelectionIntent[], signal: AbortSignal): Promise<readonly Outcome[]>;
56
65
  }
@@ -5,27 +5,37 @@ export class ListPropertySelect {
5
5
  article;
6
6
  options;
7
7
  abort = new AbortController();
8
+ batch = false;
8
9
  button;
10
+ content = document.createElement('div');
9
11
  disposed = false;
10
12
  drag;
11
13
  observer;
12
14
  pane;
13
15
  pending = new Map();
16
+ removals = new Map();
17
+ removedColumns = new Map();
18
+ unsubscribePane;
14
19
  view;
15
20
  constructor(article, options) {
16
21
  this.article = article;
17
22
  this.options = options;
18
- const content = document.createElement('div');
23
+ const content = this.content;
19
24
  this.button = article.querySelector('.add-column');
20
- this.pane = options.mountSelector(content, 'Add columns');
21
25
  const initial = article.dataset.selectorBootstrap
22
26
  ? JSON.parse(decodeURIComponent(article.dataset.selectorBootstrap)) : undefined;
27
+ const translate = (text) => initial?.translations?.[text] ?? text;
28
+ this.pane = options.mountSelector(content, translate('Add columns'));
29
+ this.unsubscribePane = this.pane.subscribe(state => {
30
+ if (!state.open || !state.present)
31
+ this.removedColumns = new Map();
32
+ });
23
33
  this.view = new PropertySelectView(content, {
24
34
  consumer: this, metadata: options.metadata, root: article.dataset.propertyRoot,
25
35
  advice: options.advice, stateKey: article.dataset.selectorKey, stateMode: 'session',
26
36
  stateStore: new HttpStateStore(undefined, undefined, initial && {
27
37
  ...initial, key: article.dataset.selectorKey
28
- }), usedPaths: this.paths()
38
+ }), translate, usedPaths: this.paths()
29
39
  }, { template: initial?.template, templateUrl: options.templateUrl });
30
40
  this.observer = new MutationObserver(records => {
31
41
  if (this.drag && records.some(record => record.target.tagName === 'TBODY'))
@@ -37,23 +47,36 @@ export class ListPropertySelect {
37
47
  this.button.addEventListener('click', () => this.pane.open(), { signal: this.abort.signal });
38
48
  }
39
49
  async loadColumn(path, signal) {
50
+ return (await this.loadColumns([path], this.paths(), signal, true)).get(path);
51
+ }
52
+ async loadColumns(paths, columns, signal, single = false) {
40
53
  const form = this.article.querySelector('form');
41
54
  const table = this.article.querySelector('table.objects');
42
55
  const rows = [...table.tBodies[0].rows];
43
56
  const ids = rows.map(row => row.querySelector('input[name=id]').value);
44
- const query = new URLSearchParams({ column: path, rowIds: ids.join(',') });
45
- const response = await fetch(`${form.action}?${query}`, {
46
- signal: AbortSignal.any([signal, this.abort.signal])
47
- });
48
- const result = await response.json();
49
- if (!response.ok)
57
+ const query = new URLSearchParams({ columns: columns.join(','), rowIds: ids.join(',') });
58
+ query.set(single ? 'column' : 'loadColumns', paths.join(','));
59
+ const combined = AbortSignal.any([signal, this.abort.signal]);
60
+ const response = this.options.request
61
+ ? await this.options.request(query, combined)
62
+ : await fetch(`${form.action}?${query}`, { signal: combined });
63
+ if (!response.ok) {
64
+ const result = await response.json();
50
65
  throw new Error(result.error ?? 'Column unavailable');
66
+ }
67
+ const result = single ? [await response.json()]
68
+ : (await response.json()).columns;
51
69
  signal.throwIfAborted();
52
70
  if (this.disposed || !this.article.isConnected || rows.some((row, index) => table.tBodies[0].rows[index] !== row)
53
71
  || rows.length !== table.tBodies[0].rows.length)
54
72
  throw new Error('The row view changed; try again');
55
- if (result.path !== path)
73
+ if (!Array.isArray(result) || result.length !== paths.length
74
+ || paths.some(path => result.filter(column => column.path === path).length !== 1))
56
75
  throw new Error('Mismatched column response');
76
+ return new Map(result.map(column => [column.path, this.columnContent(column, ids, table)]));
77
+ }
78
+ columnContent(result, ids, table) {
79
+ const path = result.path;
57
80
  const values = new Map(result.rows.map(row => [String(row.id), row.html]));
58
81
  if (ids.some(id => !values.has(id)))
59
82
  throw new Error('The row scope changed; reload the list');
@@ -64,6 +87,16 @@ export class ListPropertySelect {
64
87
  input.name = `search.${encodeURIComponent(path).replaceAll('.', '%2E')}`;
65
88
  input.dataset.propertyFilter = path;
66
89
  input.setAttribute('aria-label', `Filter ${result.label}`);
90
+ const previous = [...table.tHead.rows[0].cells].find(header => header.dataset.property === path);
91
+ const previousInput = previous?.querySelector('input[data-property-filter]');
92
+ input.value = previousInput?.value ?? '';
93
+ if (previousInput && document.activeElement === previousInput)
94
+ requestAnimationFrame(() => {
95
+ if (!input.isConnected || document.activeElement !== document.body)
96
+ return;
97
+ input.focus();
98
+ input.setSelectionRange(previousInput.selectionStart, previousInput.selectionEnd);
99
+ });
67
100
  header.append(result.label, input);
68
101
  const cells = ids.map(id => {
69
102
  const cell = document.createElement('td');
@@ -72,17 +105,32 @@ export class ListPropertySelect {
72
105
  });
73
106
  return { cells, header };
74
107
  }
75
- async add(path, signal) {
108
+ add(path, signal, options = {}, load) {
76
109
  const source = this.columnSource(path);
77
- const operation = this.options.addition.begin(source);
110
+ if (load)
111
+ source.load = load;
112
+ const operation = this.options.addition.begin(source, options);
78
113
  try {
79
- await this.validateColumn(path, signal);
80
114
  signal.throwIfAborted();
81
115
  if (this.disposed)
82
116
  throw new Error('List disposed');
117
+ const position = this.removedColumns.get(path);
118
+ if (position) {
119
+ const headers = [...this.article.querySelectorAll('thead [data-property]:not(.column-add-provisional)')];
120
+ const left = headers.find(header => header.dataset.property === position.left);
121
+ const right = headers.find(header => header.dataset.property === position.right);
122
+ const anchor = left ?? right;
123
+ if (anchor) {
124
+ let index = anchor.cellIndex + (left ? 1 : 0);
125
+ if (operation.header.cellIndex < index)
126
+ index--;
127
+ operation.moveTo(index);
128
+ }
129
+ }
83
130
  const outcome = operation.commit();
84
- await this.options.saveColumns();
85
- this.view.selector.setConfiguration({ usedPaths: this.paths() });
131
+ this.removedColumns.delete(path);
132
+ if (!this.batch)
133
+ this.view.selector.setConfiguration({ usedPaths: this.paths() });
86
134
  return outcome;
87
135
  }
88
136
  catch (error) {
@@ -91,24 +139,17 @@ export class ListPropertySelect {
91
139
  }
92
140
  }
93
141
  begin(intent, sample) {
94
- if (this.disposed || this.drag || this.pending.size)
142
+ if (this.disposed || this.batch || this.drag || this.pending.size)
95
143
  throw new Error('A column operation is pending');
96
144
  const path = normalisePath(intent.path);
97
- const paths = this.paths();
145
+ const added = !this.paths().includes(path);
98
146
  const suspension = this.pane.suspend({ mobileOnly: true });
99
147
  try {
100
- let drop;
101
- const dropped = new Promise(resolve => { drop = resolve; });
102
- const source = this.columnSource(path);
103
- // Existing columns already have their metadata and cells. Only new drops need admission.
104
- if (!paths.includes(path))
105
- source.accept = async (signal) => {
106
- await dropped;
107
- signal.throwIfAborted();
108
- await this.validateColumn(path, signal);
109
- };
110
- const operation = this.options.addition.beginExternal(source, sample);
111
- this.drag = { drop, intent, operation, paths, restore: () => suspension.restore() };
148
+ if (added)
149
+ this.add(path, this.abort.signal, { effects: false, hidden: true });
150
+ const paths = this.paths();
151
+ const operation = this.options.addition.beginExternal(this.columnSource(path), sample, { adding: added, removal: true });
152
+ this.drag = { added, intent, operation, paths, restore: () => suspension.restore() };
112
153
  }
113
154
  catch (error) {
114
155
  suspension.restore();
@@ -116,10 +157,17 @@ export class ListPropertySelect {
116
157
  }
117
158
  }
118
159
  columnSource(path) {
119
- const result = this.view.selector.search.results.find(result => result.property.path === path);
120
- const property = result?.property || this.view.selector.context?.properties.find(property => property.path === path);
121
- const label = result ? [...result.segments.map(segment => segment.label), result.property.label].join('.')
122
- : property?.label || path;
160
+ const selector = this.view.selector;
161
+ const row = this.content.querySelector(`.ps-property[data-path="${CSS.escape(path)}"]`);
162
+ const result = selector.search.results.find(result => result.property.path === path);
163
+ const properties = selector.branchContexts.flatMap(context => context.properties);
164
+ const label = row?.title || selector.usedProperties.find(property => property.path === path)?.label
165
+ || path.split('.').map((name, index, names) => {
166
+ const prefix = names.slice(0, index + 1).join('.');
167
+ return properties.find(property => property.path === prefix)?.label
168
+ || result?.segments.find(segment => segment.path === prefix)?.label
169
+ || (prefix === path ? result?.property.label || row?.querySelector('.ps-main')?.textContent : '') || name;
170
+ }).join('.');
123
171
  return {
124
172
  id: path,
125
173
  create: () => {
@@ -127,33 +175,27 @@ export class ListPropertySelect {
127
175
  header.dataset.property = path;
128
176
  header.dataset.sortable = 'false';
129
177
  header.textContent = label;
178
+ const input = document.createElement('input');
179
+ input.name = `search.${encodeURIComponent(path).replaceAll('.', '%2E')}`;
180
+ input.dataset.propertyFilter = path;
181
+ input.setAttribute('aria-label', `Filter ${label}`);
182
+ header.append(input);
130
183
  const cells = [...this.article.querySelectorAll('table.objects tbody tr')]
131
- .map(() => document.createElement('td'));
184
+ .map(() => {
185
+ const cell = document.createElement('td');
186
+ cell.textContent = '\u00a0';
187
+ return cell;
188
+ });
132
189
  return { cells, header };
133
190
  },
134
191
  load: async (signal) => {
192
+ // Commit the placeholder before reading the column configuration.
193
+ await Promise.resolve();
135
194
  signal.throwIfAborted();
136
- try {
137
- return await this.loadColumn(path, signal);
138
- }
139
- catch (error) {
140
- if (!this.disposed && !signal.aborted)
141
- this.previewError(path);
142
- throw error;
143
- }
195
+ return this.loadColumn(path, signal);
144
196
  }
145
197
  };
146
198
  }
147
- async validateColumn(path, signal) {
148
- const form = this.article.querySelector('form');
149
- const query = new URLSearchParams({ column: path, validate: '1' });
150
- const response = await fetch(`${form.action}?${query}`, {
151
- signal: AbortSignal.any([signal, this.abort.signal])
152
- });
153
- const result = await response.json();
154
- if (!response.ok || result.path !== path)
155
- throw new Error(result.error ?? 'Column unavailable');
156
- }
157
199
  cancel(id) {
158
200
  if (this.drag?.intent.id !== id)
159
201
  return;
@@ -163,24 +205,10 @@ export class ListPropertySelect {
163
205
  drag.restore();
164
206
  this.view.selector.setConfiguration({ usedPaths: this.paths() });
165
207
  }
166
- previewError(path) {
167
- const header = [...this.article.querySelectorAll('thead [data-property]')]
168
- .find(header => header.dataset.property === path);
169
- if (!header)
170
- return;
171
- for (const row of this.article.querySelectorAll('tbody tr'))
172
- row.cells[header.cellIndex].textContent = 'Unable to load values';
173
- if (header.querySelector('.column-retry'))
174
- return;
175
- const retry = document.createElement('button');
176
- retry.type = 'button';
177
- retry.className = 'column-retry';
178
- retry.textContent = 'Retry values';
179
- retry.addEventListener('click', event => {
180
- event.stopPropagation();
181
- void this.options.addition.retry(path);
182
- }, { signal: this.abort.signal });
183
- header.append(retry);
208
+ displayedPaths() {
209
+ return [...this.article.querySelectorAll('thead [data-property]:not(.column-add-provisional)')]
210
+ .map(header => header.dataset.property)
211
+ .filter(path => !this.removals.has(path) || this.removals.get(path).signal.aborted);
184
212
  }
185
213
  dispose() {
186
214
  if (this.disposed)
@@ -190,22 +218,30 @@ export class ListPropertySelect {
190
218
  this.disposed = true;
191
219
  this.abort.abort();
192
220
  this.observer.disconnect();
221
+ this.unsubscribePane();
222
+ this.removedColumns.clear();
193
223
  this.view.dispose();
194
224
  this.pane.remove();
195
225
  this.options.addition.dispose();
196
226
  this.button.hidden = true;
197
227
  this.pending.clear();
228
+ for (const removal of this.removals.values())
229
+ removal.abort();
230
+ this.removals.clear();
198
231
  }
199
232
  async end(id, sample) {
200
233
  const drag = this.drag;
201
234
  if (!drag || drag.intent.id !== id)
202
235
  throw new Error('Unknown column transport');
203
236
  try {
204
- const finished = drag.operation.finish(sample);
205
- drag.drop();
206
- const status = await finished;
207
- if (status !== 'cancelled')
208
- await this.options.saveColumns();
237
+ let status = await drag.operation.finish(sample);
238
+ if (status !== 'cancelled') {
239
+ this.removedColumns.delete(drag.intent.path);
240
+ if (status === 'moved' && this.displayedPaths().join(',') !== drag.paths.join(','))
241
+ await this.options.saveColumns();
242
+ if (drag.added && status === 'moved')
243
+ status = 'added';
244
+ }
209
245
  if (this.disposed)
210
246
  return { ...drag.intent, status: 'cancelled' };
211
247
  if (this.drag === drag)
@@ -234,26 +270,77 @@ export class ListPropertySelect {
234
270
  this.drag.operation.move(sample);
235
271
  }
236
272
  paths() {
237
- if (this.drag)
238
- return [...this.drag.paths];
239
- return [...this.article.querySelectorAll('thead [data-property]:not(.column-add-provisional)')]
240
- .map(header => header.dataset.property);
273
+ return this.drag ? [...this.drag.paths] : this.displayedPaths();
274
+ }
275
+ async remove(path, signal) {
276
+ const positions = this.removedColumns;
277
+ const paths = this.paths();
278
+ const index = paths.indexOf(path);
279
+ const position = { left: paths[index - 1], right: paths[index + 1] };
280
+ const removal = new AbortController();
281
+ this.removals.set(path, removal);
282
+ // Publish the unchecked state before starting the table's removal flash.
283
+ this.view.selector.setConfiguration({ usedPaths: this.paths() });
284
+ try {
285
+ const animation = this.options.addition.remove(path, AbortSignal.any([signal, removal.signal]));
286
+ const saving = this.options.saveColumns(this.paths());
287
+ const [animationResult, saved] = await Promise.allSettled([animation, saving]);
288
+ if (animationResult.status === 'rejected')
289
+ throw animationResult.reason;
290
+ if (saved.status === 'rejected')
291
+ throw saved.reason;
292
+ const removed = animationResult.value;
293
+ if (!removed) {
294
+ if (removal.signal.aborted && !signal.aborted && !this.disposed) {
295
+ await this.options.saveColumns(this.paths());
296
+ return 'existing';
297
+ }
298
+ throw new Error('Column removal cancelled');
299
+ }
300
+ if (!this.disposed && positions === this.removedColumns && index >= 0)
301
+ positions.set(path, position);
302
+ return 'removed';
303
+ }
304
+ catch (error) {
305
+ removal.abort();
306
+ throw error;
307
+ }
308
+ finally {
309
+ if (this.removals.get(path) === removal)
310
+ this.removals.delete(path);
311
+ if (!this.disposed)
312
+ this.view.selector.setConfiguration({ usedPaths: this.paths() });
313
+ }
241
314
  }
242
315
  async select(intent, signal) {
243
316
  const path = normalisePath(intent.path);
244
317
  try {
245
- if (this.drag)
246
- throw new Error('A column transport is pending');
318
+ if (this.drag || this.batch)
319
+ throw new Error('A column operation is pending');
247
320
  let status;
248
321
  const pending = this.pending.get(path);
249
322
  if (pending) {
250
- await pending;
323
+ const removal = this.removals.get(path);
324
+ if (removal && !removal.signal.aborted) {
325
+ removal.abort();
326
+ this.view.selector.setConfiguration({ usedPaths: this.paths() });
327
+ }
328
+ else if (removal && intent.action !== 'add') {
329
+ await pending;
330
+ return this.select(intent, signal);
331
+ }
332
+ const outcome = await pending;
333
+ if (removal && outcome === 'removed')
334
+ return this.select({ ...intent, action: 'add' }, signal);
251
335
  status = 'existing';
252
336
  }
253
- else if (this.paths().includes(path))
254
- status = 'existing';
255
337
  else {
256
- const operation = this.add(path, signal);
338
+ const existing = this.paths().includes(path);
339
+ const operation = existing && (intent.action !== 'add')
340
+ ? this.remove(path, signal)
341
+ : existing
342
+ ? Promise.resolve('existing')
343
+ : Promise.resolve(this.add(path, signal));
257
344
  this.pending.set(path, operation);
258
345
  try {
259
346
  status = await operation;
@@ -266,12 +353,78 @@ export class ListPropertySelect {
266
353
  signal.throwIfAborted();
267
354
  if (this.disposed)
268
355
  throw new Error('List disposed');
269
- this.options.addition.reveal(path);
270
- this.options.addition.highlight(path);
356
+ if (status !== 'removed' && this.paths().includes(path)) {
357
+ this.options.addition.reveal(path);
358
+ if (status === 'existing')
359
+ this.options.addition.highlight(path);
360
+ }
271
361
  return { ...intent, status };
272
362
  }
273
363
  catch (error) {
274
364
  return { ...intent, message: error instanceof Error ? error.message : 'Column unavailable', status: 'rejected' };
275
365
  }
276
366
  }
367
+ async selectMany(intents, signal) {
368
+ if (!intents.length)
369
+ return [];
370
+ if (this.batch || this.drag || this.pending.size || this.disposed)
371
+ return intents.map(intent => ({ ...intent, status: 'rejected', message: 'A column operation is pending' }));
372
+ this.batch = true;
373
+ const previous = this.paths();
374
+ const added = intents.filter(intent => !previous.includes(intent.path)).map(intent => intent.path);
375
+ const removed = intents.filter(intent => previous.includes(intent.path) && intent.action !== 'add')
376
+ .map(intent => intent.path);
377
+ const paths = [...previous.filter(path => !removed.includes(path)), ...added];
378
+ const abort = new AbortController();
379
+ const combined = AbortSignal.any([signal, this.abort.signal, abort.signal]);
380
+ let receive;
381
+ let fail;
382
+ const content = new Promise((resolve, reject) => { receive = resolve; fail = reject; });
383
+ void content.catch(() => { });
384
+ try {
385
+ for (const path of removed)
386
+ this.removals.set(path, abort);
387
+ for (const path of added) {
388
+ let first = true;
389
+ this.add(path, combined, { effects: 'deferred' }, signal => {
390
+ if (!first)
391
+ return this.loadColumn(path, signal);
392
+ first = false;
393
+ return content.then(content => content.get(path));
394
+ });
395
+ }
396
+ this.view.selector.setConfiguration({ usedPaths: this.paths() });
397
+ if (added.length)
398
+ this.options.addition.reveal(added.at(-1));
399
+ this.options.addition.highlightMany(added, true);
400
+ const animation = removed.length ? this.options.addition.removeMany(removed, combined) : Promise.resolve(true);
401
+ const request = this.loadColumns(added, paths, combined).then(receive, error => { fail(error); throw error; });
402
+ const [animated, loaded] = await Promise.allSettled([animation, request]);
403
+ if (animated.status === 'rejected')
404
+ throw animated.reason;
405
+ if (loaded.status === 'rejected')
406
+ throw loaded.reason;
407
+ if (!animated.value)
408
+ throw new Error('Column removal cancelled');
409
+ for (const path of removed) {
410
+ const index = previous.indexOf(path);
411
+ this.removedColumns.set(path, { left: previous[index - 1], right: previous[index + 1] });
412
+ }
413
+ return intents.map(intent => ({ ...intent, status: added.includes(intent.path) ? 'added'
414
+ : removed.includes(intent.path) ? 'removed' : 'existing' }));
415
+ }
416
+ catch (error) {
417
+ fail(error);
418
+ return intents.map(intent => ({ ...intent, status: 'rejected',
419
+ message: error instanceof Error ? error.message : 'Columns unavailable' }));
420
+ }
421
+ finally {
422
+ for (const path of removed)
423
+ if (this.removals.get(path) === abort)
424
+ this.removals.delete(path);
425
+ this.batch = false;
426
+ if (!this.disposed)
427
+ this.view.selector.setConfiguration({ usedPaths: this.paths() });
428
+ }
429
+ }
277
430
  }