apex-grid 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +29 -0
  3. package/components/cell.d.ts +2 -2
  4. package/components/cell.js +30 -2
  5. package/components/cell.js.map +1 -1
  6. package/components/filter-row.js +65 -6
  7. package/components/filter-row.js.map +1 -1
  8. package/components/grid.d.ts +5 -0
  9. package/components/grid.js +34 -2
  10. package/components/grid.js.map +1 -1
  11. package/components/header.js +80 -9
  12. package/components/header.js.map +1 -1
  13. package/controllers/expansion.js +4 -4
  14. package/controllers/expansion.js.map +1 -1
  15. package/controllers/filter.js +11 -0
  16. package/controllers/filter.js.map +1 -1
  17. package/controllers/history.js +11 -4
  18. package/controllers/history.js.map +1 -1
  19. package/controllers/navigation.d.ts +34 -2
  20. package/controllers/navigation.js +92 -3
  21. package/controllers/navigation.js.map +1 -1
  22. package/controllers/pagination.js +1 -1
  23. package/controllers/pagination.js.map +1 -1
  24. package/controllers/row-pin.js +2 -2
  25. package/controllers/row-pin.js.map +1 -1
  26. package/controllers/row-reorder.js +6 -6
  27. package/controllers/row-reorder.js.map +1 -1
  28. package/controllers/selection.js +3 -3
  29. package/controllers/selection.js.map +1 -1
  30. package/controllers/sort.js +4 -2
  31. package/controllers/sort.js.map +1 -1
  32. package/custom-elements.json +223 -7
  33. package/docs/assets/hierarchy.js +1 -1
  34. package/docs/assets/search.js +1 -1
  35. package/docs/functions/localize.html +2 -2
  36. package/docs/hierarchy.html +1 -1
  37. package/docs/index.html +62 -1
  38. package/docs/interfaces/BaseApexCellContext.html +1 -1
  39. package/docs/interfaces/BaseApexEditorContext.html +1 -1
  40. package/docs/interfaces/CSVExportOptions.html +1 -1
  41. package/docs/interfaces/ExportOptions.html +1 -1
  42. package/docs/interfaces/PaginationState.html +1 -1
  43. package/docs/media/package.json +2 -2
  44. package/docs/variables/EN_LOCALE.html +2 -2
  45. package/i18n/en.d.ts +95 -1
  46. package/i18n/en.js +95 -1
  47. package/i18n/en.js.map +1 -1
  48. package/i18n/es.js +95 -1
  49. package/i18n/es.js.map +1 -1
  50. package/internal/column-types.js +1 -1
  51. package/internal/column-types.js.map +1 -1
  52. package/package.json +1 -1
  53. package/styles/body-cell/body-cell.css.js +1 -1
  54. package/styles/body-cell/body-cell.css.js.map +1 -1
@@ -117,6 +117,54 @@ export default class ApexGridHeader extends LitElement {
117
117
  return;
118
118
  this.menuOpen = false;
119
119
  };
120
+ this.#handleMenuTriggerKeydown = (e) => {
121
+ if (e.key !== 'ArrowDown' && e.key !== 'Down')
122
+ return;
123
+ e.preventDefault();
124
+ e.stopPropagation();
125
+ if (!this.menuOpen) {
126
+ this.#handleMenuClick(e);
127
+ }
128
+ this.#focusMenuItem(0);
129
+ };
130
+ this.#handleColMenuKeydown = (e) => {
131
+ const items = this.#menuItems();
132
+ const current = items.findIndex((item) => item.matches(':focus'));
133
+ switch (e.key) {
134
+ case 'ArrowDown':
135
+ case 'Down':
136
+ e.preventDefault();
137
+ e.stopPropagation();
138
+ items[(current + 1) % items.length]?.focus();
139
+ return;
140
+ case 'ArrowUp':
141
+ case 'Up':
142
+ e.preventDefault();
143
+ e.stopPropagation();
144
+ items[(current - 1 + items.length) % items.length]?.focus();
145
+ return;
146
+ case 'Home':
147
+ e.preventDefault();
148
+ e.stopPropagation();
149
+ items[0]?.focus();
150
+ return;
151
+ case 'End':
152
+ e.preventDefault();
153
+ e.stopPropagation();
154
+ items[items.length - 1]?.focus();
155
+ return;
156
+ case 'Escape':
157
+ e.preventDefault();
158
+ e.stopPropagation();
159
+ this.#closeMenu(true);
160
+ return;
161
+ case 'Tab':
162
+ this.#closeMenu();
163
+ return;
164
+ default:
165
+ return;
166
+ }
167
+ };
120
168
  }
121
169
  static get tagName() {
122
170
  return GRID_HEADER_TAG;
@@ -183,6 +231,25 @@ export default class ApexGridHeader extends LitElement {
183
231
  #handleFilterClick;
184
232
  #handleMenuClick;
185
233
  #handleMenuOutside;
234
+ #handleMenuTriggerKeydown;
235
+ #menuItems() {
236
+ return Array.from(this.renderRoot.querySelectorAll('[role="menuitem"]'));
237
+ }
238
+ #focusMenuItem(index) {
239
+ requestAnimationFrame(() => {
240
+ const items = this.#menuItems();
241
+ if (!items.length)
242
+ return;
243
+ items[(index + items.length) % items.length]?.focus();
244
+ });
245
+ }
246
+ #closeMenu(returnFocus = false) {
247
+ this.menuOpen = false;
248
+ if (returnFocus) {
249
+ requestAnimationFrame(() => this.renderRoot.querySelector('[part~="menu-btn"]')?.focus());
250
+ }
251
+ }
252
+ #handleColMenuKeydown;
186
253
  updated() {
187
254
  this.setAttribute('data-cell-type', this.column.type ?? 'string');
188
255
  if (this.isDraggable) {
@@ -224,10 +291,10 @@ export default class ApexGridHeader extends LitElement {
224
291
  : nothing;
225
292
  const direction = state?.direction ?? 'none';
226
293
  const label = state?.direction === 'ascending'
227
- ? 'Sorted ascending. Activate to sort descending.'
294
+ ? this.state.localize('header.sortedAscending')
228
295
  : state?.direction === 'descending'
229
- ? 'Sorted descending. Activate to clear sort.'
230
- : 'Not sorted. Activate to sort ascending.';
296
+ ? this.state.localize('header.sortedDescending')
297
+ : this.state.localize('header.notSorted');
231
298
  return state || this.isSortable
232
299
  ? this.isSortable
233
300
  ? html `<button
@@ -257,7 +324,7 @@ export default class ApexGridHeader extends LitElement {
257
324
  return html `<button
258
325
  type="button"
259
326
  part=${partNameMap({ action: true, 'filter-btn': true, 'filter-active': this.hasActiveFilter })}
260
- aria-label="Filter column"
327
+ aria-label=${this.state.localize('header.filterColumn')}
261
328
  @click=${this.#handleFilterClick}
262
329
  >
263
330
  ${renderIcon('filter')}
@@ -273,10 +340,11 @@ export default class ApexGridHeader extends LitElement {
273
340
  return html `<button
274
341
  type="button"
275
342
  part=${partNameMap({ action: true, 'menu-btn': true })}
276
- aria-label="Column menu"
343
+ aria-label=${this.state.localize('header.columnMenu')}
277
344
  aria-expanded=${this.menuOpen ? 'true' : 'false'}
278
345
  aria-haspopup="menu"
279
346
  @click=${this.#handleMenuClick}
347
+ @keydown=${this.#handleMenuTriggerKeydown}
280
348
  >
281
349
  ${renderIcon('more-vert')}
282
350
  </button>`;
@@ -284,30 +352,32 @@ export default class ApexGridHeader extends LitElement {
284
352
  renderColumnMenu() {
285
353
  if (!this.menuOpen)
286
354
  return nothing;
287
- return html `<div part="col-menu" role="menu">
355
+ return html `<div part="col-menu" role="menu" @keydown=${this.#handleColMenuKeydown}>
288
356
  ${this.isSortable
289
357
  ? html `
290
358
  <button
291
359
  type="button"
292
360
  part="col-menu-item"
293
361
  role="menuitem"
362
+ tabindex="-1"
294
363
  @click=${() => {
295
364
  this.state.sorting.sort({ key: this.column.key, direction: 'ascending' });
296
365
  this.menuOpen = false;
297
366
  }}
298
367
  >
299
- ${renderIcon('sort-asc')} Sort Ascending
368
+ ${renderIcon('sort-asc')} ${this.state.localize('contextMenu.sortAsc')}
300
369
  </button>
301
370
  <button
302
371
  type="button"
303
372
  part="col-menu-item"
304
373
  role="menuitem"
374
+ tabindex="-1"
305
375
  @click=${() => {
306
376
  this.state.sorting.sort({ key: this.column.key, direction: 'descending' });
307
377
  this.menuOpen = false;
308
378
  }}
309
379
  >
310
- ${renderIcon('sort-desc')} Sort Descending
380
+ ${renderIcon('sort-desc')} ${this.state.localize('contextMenu.sortDesc')}
311
381
  </button>
312
382
  `
313
383
  : nothing}
@@ -316,12 +386,13 @@ export default class ApexGridHeader extends LitElement {
316
386
  type="button"
317
387
  part="col-menu-item"
318
388
  role="menuitem"
389
+ tabindex="-1"
319
390
  @click=${() => {
320
391
  this.state.resizing.autosize(this.column, this);
321
392
  this.menuOpen = false;
322
393
  }}
323
394
  >
324
- ${renderIcon('arrow-upward')} Autosize Column
395
+ ${renderIcon('arrow-upward')} ${this.state.localize('header.autosizeColumn')}
325
396
  </button>`
326
397
  : nothing}
327
398
  </div>`;
@@ -1 +1 @@
1
- {"version":3,"file":"header.js","sourceRoot":"","sources":["../../src/components/header.ts"],"names":[],"mappings":"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","sourcesContent":["import { consume } from '@lit/context';\nimport { html, LitElement, nothing } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { gridStateContext, type StateController } from '../controllers/state.js';\nimport { MIN_COL_RESIZE_WIDTH } from '../internal/constants.js';\nimport { renderIcon, renderSortArrows } from '../internal/icons.js';\nimport { partNameMap } from '../internal/part-map.js';\nimport { registerComponent } from '../internal/register.js';\nimport { GRID_HEADER_TAG } from '../internal/tags.js';\nimport type { ApexHeaderContext, ColumnConfiguration } from '../internal/types.js';\nimport { styles } from '../styles/header-cell/header-cell.css.js';\n\n/** Pixels of pointer travel before a hold turns into a drag — avoids\n * accidental drags on click. */\nconst DRAG_THRESHOLD_PX = 4;\n\nexport default class ApexGridHeader<T extends object> extends LitElement {\n public static get tagName() {\n return GRID_HEADER_TAG;\n }\n\n public static override styles = styles;\n\n public static register(): void {\n registerComponent(ApexGridHeader);\n }\n\n protected get context(): ApexHeaderContext<T> {\n return {\n parent: this,\n column: this.column,\n };\n }\n\n protected get isSortable() {\n // Sort affordances are hidden while a manual row order is active (F5):\n // sorting and manual order are mutually exclusive.\n return Boolean(this.column.sort) && !this.state?.rowReorder?.hasManualOrder;\n }\n\n protected get isFilterable() {\n return Boolean(this.column.filter);\n }\n\n protected get hasActiveFilter() {\n return Boolean(this.state?.filtering?.state.has(this.column.key));\n }\n\n protected get resizeController() {\n return this.state.resizing;\n }\n\n protected get reorderController() {\n return this.state.reordering;\n }\n\n protected get isDraggable() {\n return Boolean(this.state?.reordering?.isDraggable(this.column));\n }\n\n @consume({ context: gridStateContext, subscribe: true })\n @property({ attribute: false })\n public state!: StateController<T>;\n\n @property({ attribute: false })\n public column!: ColumnConfiguration<T>;\n\n /** 1-based column index passed in by the parent header row for `aria-colindex`. */\n @property({ attribute: false, type: Number })\n public colindex = 0;\n\n /**\n * Spreadsheet column letter (A, B, C, ...) shown as a prefix while formula\n * coordinate hints are active. Empty string hides it.\n */\n @property({ attribute: false })\n public coordinateLetter = '';\n\n @state()\n protected menuOpen = false;\n\n #addResizeEventHandlers() {\n const config: AddEventListenerOptions = { once: true };\n\n this.addEventListener(\n 'gotpointercapture',\n () => {\n this.resizeController.indicatorActive = true;\n },\n config\n );\n this.addEventListener('lostpointercapture', this.#handlePointerLost, config);\n this.addEventListener('pointerup', (e) => this.releasePointerCapture(e.pointerId), config);\n this.addEventListener('pointermove', this.#handleResize);\n }\n\n #handleClick(e: MouseEvent) {\n e.stopPropagation();\n // Ctrl/Cmd+click appends to a multi-column sort; a plain click sorts by\n // this column alone. Keyboard activation (Enter/Space) fires a click whose\n // modifier flags mirror the keys held, so Ctrl/Cmd+Enter is additive too.\n this.state.sorting.sortFromHeaderClick(this.column, e.ctrlKey || e.metaKey);\n }\n\n #handleResize = ({ clientX }: PointerEvent) => {\n const { left } = this.getBoundingClientRect();\n const width = Math.max(clientX - left, MIN_COL_RESIZE_WIDTH);\n const x = this.offsetLeft + width;\n\n this.resizeController.resize(this.column, width, x);\n };\n\n #handleResizeStart(ev: PointerEvent) {\n const { target, pointerId } = ev;\n\n ev.preventDefault();\n // Resize takes priority over reorder — stop the event so the header's\n // pointerdown listener doesn't also start arming a drag.\n ev.stopPropagation();\n\n this.#addResizeEventHandlers();\n this.resizeController.start(this);\n\n (target as HTMLElement).setPointerCapture(pointerId);\n }\n\n #handlePointerLost = () => {\n this.resizeController.indicatorActive = false;\n this.removeEventListener('pointermove', this.#handleResize);\n this.resizeController.stop();\n };\n\n #handleAutosize = () => this.resizeController.autosize(this.column, this);\n\n // --- Reorder (pointer-driven) ------------------------------------------\n\n #dragStartX = 0;\n #dragStartY = 0;\n #dragPointerId = -1;\n #isDragging = false;\n\n #handleReorderPointerDown = (event: PointerEvent) => {\n if (!this.isDraggable) return;\n if (event.button !== 0) return;\n // Skip if the user grabbed an interactive sub-part of the header — the\n // resize handle, sort/action icons, filter button, menu button, or the\n // column menu dropdown. Those have their own handlers and should not arm\n // a drag.\n const path = event.composedPath();\n const target = path[0];\n if (\n target instanceof Element &&\n target.closest?.('[part~=\"resizable\"], [part~=\"action\"], [part~=\"col-menu\"]')\n ) {\n return;\n }\n this.#dragStartX = event.clientX;\n this.#dragStartY = event.clientY;\n this.#dragPointerId = event.pointerId;\n this.addEventListener('pointermove', this.#handleReorderPointerMove);\n this.addEventListener('pointerup', this.#handleReorderPointerUp);\n this.addEventListener('pointercancel', this.#handleReorderPointerUp);\n };\n\n #handleReorderPointerMove = (event: PointerEvent) => {\n if (event.pointerId !== this.#dragPointerId) return;\n if (!this.#isDragging) {\n const dx = event.clientX - this.#dragStartX;\n const dy = event.clientY - this.#dragStartY;\n if (Math.hypot(dx, dy) < DRAG_THRESHOLD_PX) return;\n // Threshold crossed — start the drag in earnest.\n this.#isDragging = true;\n this.setPointerCapture(event.pointerId);\n this.setAttribute('data-dragging', '');\n const rect = this.getBoundingClientRect();\n const label = this.column.headerText ?? String(this.column.key);\n this.reorderController.start(\n this.column.key,\n rect,\n this.#dragStartX,\n this.#dragStartY,\n label\n );\n }\n this.reorderController.move(event.clientX, event.clientY);\n };\n\n #handleReorderPointerUp = (event: PointerEvent) => {\n if (event.pointerId !== this.#dragPointerId) return;\n this.removeEventListener('pointermove', this.#handleReorderPointerMove);\n this.removeEventListener('pointerup', this.#handleReorderPointerUp);\n this.removeEventListener('pointercancel', this.#handleReorderPointerUp);\n try {\n if (this.hasPointerCapture(event.pointerId)) {\n this.releasePointerCapture(event.pointerId);\n }\n } catch {\n /* capture was already released */\n }\n if (this.#isDragging) {\n this.#isDragging = false;\n this.removeAttribute('data-dragging');\n this.reorderController.end();\n }\n this.#dragPointerId = -1;\n };\n\n // --- Filter button --------------------------------------------------\n\n #handleFilterClick = (e: MouseEvent) => {\n e.stopPropagation();\n const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();\n this.state.filtering.setActiveColumn(this.column, rect);\n };\n\n // --- Column menu button ---------------------------------------------\n\n #handleMenuClick = (e: MouseEvent) => {\n e.stopPropagation();\n // Offer the column menu to a feature module first: the enterprise context\n // menu opens its richer, shared menu (sort / pin / hide / group / chart)\n // anchored at this button and calls preventDefault(). When nothing handles\n // it (the community grid), fall back to the built-in inline menu below.\n // Event name is a stable string contract (mirrored in the enterprise\n // context-menu controller), like the native 'contextmenu'.\n const request = new CustomEvent('apex-grid-column-menu', {\n detail: { column: this.column, anchor: e.currentTarget as HTMLElement },\n bubbles: true,\n composed: true,\n cancelable: true,\n });\n this.dispatchEvent(request);\n if (request.defaultPrevented) return;\n this.menuOpen = !this.menuOpen;\n };\n\n #handleMenuOutside = (e: PointerEvent) => {\n if (!this.menuOpen) return;\n if (e.composedPath().includes(this)) return;\n this.menuOpen = false;\n };\n\n protected override updated() {\n // Mirror the column's data type onto the host so the header label can match\n // the body cell alignment by default (numeric/currency cells right-align,\n // so their headers should too). Kept in sync on every update because the\n // column config can change at runtime (reorder, reconfigure).\n this.setAttribute('data-cell-type', this.column.type ?? 'string');\n // Reflect reorderability as an attribute so SCSS can show the grab\n // cursor only on columns that can actually be dragged. Tracks both the\n // grid-level `columnReordering` flag (arriving via context) and the\n // per-column `reorderable: false` opt-out at runtime.\n if (this.isDraggable) {\n this.setAttribute('data-reorderable', '');\n } else {\n this.removeAttribute('data-reorderable');\n }\n // aria-colindex stays in sync with the parent header row's numbering.\n if (this.colindex > 0) {\n this.setAttribute('aria-colindex', String(this.colindex));\n }\n // aria-sort communicates current sort direction for this column to AT.\n if (this.isSortable) {\n const sort = this.state?.sorting.state.get(this.column.key);\n const dir = sort?.direction;\n this.setAttribute(\n 'aria-sort',\n dir === 'ascending' ? 'ascending' : dir === 'descending' ? 'descending' : 'none'\n );\n } else {\n this.removeAttribute('aria-sort');\n }\n }\n\n public override connectedCallback(): void {\n super.connectedCallback();\n this.setAttribute('role', 'columnheader');\n this.addEventListener('pointerdown', this.#handleReorderPointerDown);\n document.addEventListener('pointerdown', this.#handleMenuOutside, true);\n }\n\n public override disconnectedCallback(): void {\n this.removeEventListener('pointerdown', this.#handleReorderPointerDown);\n document.removeEventListener('pointerdown', this.#handleMenuOutside, true);\n super.disconnectedCallback();\n }\n\n protected renderSortPart() {\n const state = this.state.sorting.state.get(this.column.key);\n const idx = Array.from(this.state.sorting.state.values()).indexOf(state!);\n const attr = this.state.host.sortConfiguration.multiple\n ? idx > -1\n ? idx + 1\n : nothing\n : nothing;\n const direction = state?.direction ?? 'none';\n\n const label =\n state?.direction === 'ascending'\n ? 'Sorted ascending. Activate to sort descending.'\n : state?.direction === 'descending'\n ? 'Sorted descending. Activate to clear sort.'\n : 'Not sorted. Activate to sort ascending.';\n\n return state || this.isSortable\n ? this.isSortable\n ? html`<button\n type=\"button\"\n part=${partNameMap({ action: true, sorted: !!state?.direction })}\n data-sort-active=${direction}\n data-sort-index=${attr === nothing ? '' : (attr as number)}\n aria-label=${label}\n @click=${this.#handleClick}\n >\n ${renderSortArrows()}\n </button>`\n : html`<span\n part=${partNameMap({ action: true, sorted: !!state?.direction })}\n data-sort-active=${direction}\n data-sort-index=${attr === nothing ? '' : (attr as number)}\n aria-hidden=\"true\"\n >\n ${renderSortArrows()}\n </span>`\n : nothing;\n }\n\n protected renderFilterButton() {\n if (!this.isFilterable) return nothing;\n const count = this.state?.filtering?.state.get(this.column.key)?.length ?? 0;\n return html`<button\n type=\"button\"\n part=${partNameMap({ action: true, 'filter-btn': true, 'filter-active': this.hasActiveFilter })}\n aria-label=\"Filter column\"\n @click=${this.#handleFilterClick}\n >\n ${renderIcon('filter')}\n ${count > 1 ? html`<span part=\"filter-count\">${count}</span>` : nothing}\n </button>`;\n }\n\n protected renderMenuButton() {\n // Suppress the kebab entirely when the grid opts out.\n if (this.state?.host?.columnMenu === false) return nothing;\n // Show the kebab when this column has built-in menu items (sort / autosize)\n // or when a feature module provides a richer column menu (the enterprise\n // context menu: pin / hide / group / chart), so the affordance appears even\n // on columns that are neither sortable nor resizable.\n const hasMenuItems =\n this.isSortable || this.column.resizable || Boolean(this.state?.hasColumnMenu);\n if (!hasMenuItems) return nothing;\n return html`<button\n type=\"button\"\n part=${partNameMap({ action: true, 'menu-btn': true })}\n aria-label=\"Column menu\"\n aria-expanded=${this.menuOpen ? 'true' : 'false'}\n aria-haspopup=\"menu\"\n @click=${this.#handleMenuClick}\n >\n ${renderIcon('more-vert')}\n </button>`;\n }\n\n protected renderColumnMenu() {\n if (!this.menuOpen) return nothing;\n return html`<div part=\"col-menu\" role=\"menu\">\n ${\n this.isSortable\n ? html`\n <button\n type=\"button\"\n part=\"col-menu-item\"\n role=\"menuitem\"\n @click=${() => {\n this.state.sorting.sort({ key: this.column.key, direction: 'ascending' } as any);\n this.menuOpen = false;\n }}\n >\n ${renderIcon('sort-asc')} Sort Ascending\n </button>\n <button\n type=\"button\"\n part=\"col-menu-item\"\n role=\"menuitem\"\n @click=${() => {\n this.state.sorting.sort({ key: this.column.key, direction: 'descending' } as any);\n this.menuOpen = false;\n }}\n >\n ${renderIcon('sort-desc')} Sort Descending\n </button>\n `\n : nothing\n }\n ${\n this.column.resizable\n ? html`<button\n type=\"button\"\n part=\"col-menu-item\"\n role=\"menuitem\"\n @click=${() => {\n this.state.resizing.autosize(this.column, this);\n this.menuOpen = false;\n }}\n >\n ${renderIcon('arrow-upward')} Autosize Column\n </button>`\n : nothing\n }\n </div>`;\n }\n\n protected renderContentPart() {\n const defaultContent = this.column.headerText ?? this.column.key;\n const template = this.column.headerTemplate;\n\n return html`\n <span part=\"title\">\n ${\n this.coordinateLetter\n ? html`<span part=\"coord-letter\" aria-hidden=\"true\">${this.coordinateLetter}</span>`\n : nothing\n }\n <span>${template ? template(this.context) : html`${defaultContent}`}</span>\n </span>\n `;\n }\n\n protected renderResizePart() {\n return this.column.resizable\n ? html`<span\n part=\"resizable\"\n @dblclick=${this.#handleAutosize}\n @pointerdown=${this.#handleResizeStart}\n ></span>`\n : nothing;\n }\n\n protected override render() {\n return html`\n <div\n part=${partNameMap({\n content: true,\n sortable: this.isSortable,\n resizing: this.resizeController.indicatorActive,\n })}\n >\n ${this.renderContentPart()}\n <div part=\"actions\">\n ${this.renderSortPart()}\n ${this.renderFilterButton()}\n ${this.renderMenuButton()}\n </div>\n </div>\n ${this.renderResizePart()}\n ${this.renderColumnMenu()}\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n [ApexGridHeader.tagName]: ApexGridHeader<object>;\n }\n}\n"]}
1
+ {"version":3,"file":"header.js","sourceRoot":"","sources":["../../src/components/header.ts"],"names":[],"mappings":"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","sourcesContent":["import { consume } from '@lit/context';\nimport { html, LitElement, nothing } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { gridStateContext, type StateController } from '../controllers/state.js';\nimport { MIN_COL_RESIZE_WIDTH } from '../internal/constants.js';\nimport { renderIcon, renderSortArrows } from '../internal/icons.js';\nimport { partNameMap } from '../internal/part-map.js';\nimport { registerComponent } from '../internal/register.js';\nimport { GRID_HEADER_TAG } from '../internal/tags.js';\nimport type { ApexHeaderContext, ColumnConfiguration } from '../internal/types.js';\nimport { styles } from '../styles/header-cell/header-cell.css.js';\n\n/** Pixels of pointer travel before a hold turns into a drag — avoids\n * accidental drags on click. */\nconst DRAG_THRESHOLD_PX = 4;\n\nexport default class ApexGridHeader<T extends object> extends LitElement {\n public static get tagName() {\n return GRID_HEADER_TAG;\n }\n\n public static override styles = styles;\n\n public static register(): void {\n registerComponent(ApexGridHeader);\n }\n\n protected get context(): ApexHeaderContext<T> {\n return {\n parent: this,\n column: this.column,\n };\n }\n\n protected get isSortable() {\n // Sort affordances are hidden while a manual row order is active (F5):\n // sorting and manual order are mutually exclusive.\n return Boolean(this.column.sort) && !this.state?.rowReorder?.hasManualOrder;\n }\n\n protected get isFilterable() {\n return Boolean(this.column.filter);\n }\n\n protected get hasActiveFilter() {\n return Boolean(this.state?.filtering?.state.has(this.column.key));\n }\n\n protected get resizeController() {\n return this.state.resizing;\n }\n\n protected get reorderController() {\n return this.state.reordering;\n }\n\n protected get isDraggable() {\n return Boolean(this.state?.reordering?.isDraggable(this.column));\n }\n\n @consume({ context: gridStateContext, subscribe: true })\n @property({ attribute: false })\n public state!: StateController<T>;\n\n @property({ attribute: false })\n public column!: ColumnConfiguration<T>;\n\n /** 1-based column index passed in by the parent header row for `aria-colindex`. */\n @property({ attribute: false, type: Number })\n public colindex = 0;\n\n /**\n * Spreadsheet column letter (A, B, C, ...) shown as a prefix while formula\n * coordinate hints are active. Empty string hides it.\n */\n @property({ attribute: false })\n public coordinateLetter = '';\n\n @state()\n protected menuOpen = false;\n\n #addResizeEventHandlers() {\n const config: AddEventListenerOptions = { once: true };\n\n this.addEventListener(\n 'gotpointercapture',\n () => {\n this.resizeController.indicatorActive = true;\n },\n config\n );\n this.addEventListener('lostpointercapture', this.#handlePointerLost, config);\n this.addEventListener('pointerup', (e) => this.releasePointerCapture(e.pointerId), config);\n this.addEventListener('pointermove', this.#handleResize);\n }\n\n #handleClick(e: MouseEvent) {\n e.stopPropagation();\n // Ctrl/Cmd+click appends to a multi-column sort; a plain click sorts by\n // this column alone. Keyboard activation (Enter/Space) fires a click whose\n // modifier flags mirror the keys held, so Ctrl/Cmd+Enter is additive too.\n this.state.sorting.sortFromHeaderClick(this.column, e.ctrlKey || e.metaKey);\n }\n\n #handleResize = ({ clientX }: PointerEvent) => {\n const { left } = this.getBoundingClientRect();\n const width = Math.max(clientX - left, MIN_COL_RESIZE_WIDTH);\n const x = this.offsetLeft + width;\n\n this.resizeController.resize(this.column, width, x);\n };\n\n #handleResizeStart(ev: PointerEvent) {\n const { target, pointerId } = ev;\n\n ev.preventDefault();\n // Resize takes priority over reorder — stop the event so the header's\n // pointerdown listener doesn't also start arming a drag.\n ev.stopPropagation();\n\n this.#addResizeEventHandlers();\n this.resizeController.start(this);\n\n (target as HTMLElement).setPointerCapture(pointerId);\n }\n\n #handlePointerLost = () => {\n this.resizeController.indicatorActive = false;\n this.removeEventListener('pointermove', this.#handleResize);\n this.resizeController.stop();\n };\n\n #handleAutosize = () => this.resizeController.autosize(this.column, this);\n\n // --- Reorder (pointer-driven) ------------------------------------------\n\n #dragStartX = 0;\n #dragStartY = 0;\n #dragPointerId = -1;\n #isDragging = false;\n\n #handleReorderPointerDown = (event: PointerEvent) => {\n if (!this.isDraggable) return;\n if (event.button !== 0) return;\n // Skip if the user grabbed an interactive sub-part of the header — the\n // resize handle, sort/action icons, filter button, menu button, or the\n // column menu dropdown. Those have their own handlers and should not arm\n // a drag.\n const path = event.composedPath();\n const target = path[0];\n if (\n target instanceof Element &&\n target.closest?.('[part~=\"resizable\"], [part~=\"action\"], [part~=\"col-menu\"]')\n ) {\n return;\n }\n this.#dragStartX = event.clientX;\n this.#dragStartY = event.clientY;\n this.#dragPointerId = event.pointerId;\n this.addEventListener('pointermove', this.#handleReorderPointerMove);\n this.addEventListener('pointerup', this.#handleReorderPointerUp);\n this.addEventListener('pointercancel', this.#handleReorderPointerUp);\n };\n\n #handleReorderPointerMove = (event: PointerEvent) => {\n if (event.pointerId !== this.#dragPointerId) return;\n if (!this.#isDragging) {\n const dx = event.clientX - this.#dragStartX;\n const dy = event.clientY - this.#dragStartY;\n if (Math.hypot(dx, dy) < DRAG_THRESHOLD_PX) return;\n // Threshold crossed — start the drag in earnest.\n this.#isDragging = true;\n this.setPointerCapture(event.pointerId);\n this.setAttribute('data-dragging', '');\n const rect = this.getBoundingClientRect();\n const label = this.column.headerText ?? String(this.column.key);\n this.reorderController.start(\n this.column.key,\n rect,\n this.#dragStartX,\n this.#dragStartY,\n label\n );\n }\n this.reorderController.move(event.clientX, event.clientY);\n };\n\n #handleReorderPointerUp = (event: PointerEvent) => {\n if (event.pointerId !== this.#dragPointerId) return;\n this.removeEventListener('pointermove', this.#handleReorderPointerMove);\n this.removeEventListener('pointerup', this.#handleReorderPointerUp);\n this.removeEventListener('pointercancel', this.#handleReorderPointerUp);\n try {\n if (this.hasPointerCapture(event.pointerId)) {\n this.releasePointerCapture(event.pointerId);\n }\n } catch {\n /* capture was already released */\n }\n if (this.#isDragging) {\n this.#isDragging = false;\n this.removeAttribute('data-dragging');\n this.reorderController.end();\n }\n this.#dragPointerId = -1;\n };\n\n // --- Filter button --------------------------------------------------\n\n #handleFilterClick = (e: MouseEvent) => {\n e.stopPropagation();\n const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();\n this.state.filtering.setActiveColumn(this.column, rect);\n };\n\n // --- Column menu button ---------------------------------------------\n\n #handleMenuClick = (e: MouseEvent) => {\n e.stopPropagation();\n // Offer the column menu to a feature module first: the enterprise context\n // menu opens its richer, shared menu (sort / pin / hide / group / chart)\n // anchored at this button and calls preventDefault(). When nothing handles\n // it (the community grid), fall back to the built-in inline menu below.\n // Event name is a stable string contract (mirrored in the enterprise\n // context-menu controller), like the native 'contextmenu'.\n const request = new CustomEvent('apex-grid-column-menu', {\n detail: { column: this.column, anchor: e.currentTarget as HTMLElement },\n bubbles: true,\n composed: true,\n cancelable: true,\n });\n this.dispatchEvent(request);\n if (request.defaultPrevented) return;\n this.menuOpen = !this.menuOpen;\n };\n\n #handleMenuOutside = (e: PointerEvent) => {\n if (!this.menuOpen) return;\n if (e.composedPath().includes(this)) return;\n this.menuOpen = false;\n };\n\n /** ArrowDown on the kebab opens the menu and moves focus to its first item. */\n #handleMenuTriggerKeydown = (e: KeyboardEvent) => {\n if (e.key !== 'ArrowDown' && e.key !== 'Down') return;\n e.preventDefault();\n e.stopPropagation();\n if (!this.menuOpen) {\n // Route through the same module-first contract as a click.\n this.#handleMenuClick(e as unknown as MouseEvent);\n }\n this.#focusMenuItem(0);\n };\n\n #menuItems(): HTMLElement[] {\n return Array.from(this.renderRoot.querySelectorAll<HTMLElement>('[role=\"menuitem\"]'));\n }\n\n #focusMenuItem(index: number) {\n requestAnimationFrame(() => {\n const items = this.#menuItems();\n if (!items.length) return;\n items[(index + items.length) % items.length]?.focus();\n });\n }\n\n #closeMenu(returnFocus = false) {\n this.menuOpen = false;\n if (returnFocus) {\n requestAnimationFrame(() =>\n this.renderRoot.querySelector<HTMLElement>('[part~=\"menu-btn\"]')?.focus()\n );\n }\n }\n\n /** Menu keyboard pattern: arrows/Home/End rove, Escape closes and restores focus. */\n #handleColMenuKeydown = (e: KeyboardEvent) => {\n const items = this.#menuItems();\n const current = items.findIndex((item) => item.matches(':focus'));\n switch (e.key) {\n case 'ArrowDown':\n case 'Down':\n e.preventDefault();\n e.stopPropagation();\n items[(current + 1) % items.length]?.focus();\n return;\n case 'ArrowUp':\n case 'Up':\n e.preventDefault();\n e.stopPropagation();\n items[(current - 1 + items.length) % items.length]?.focus();\n return;\n case 'Home':\n e.preventDefault();\n e.stopPropagation();\n items[0]?.focus();\n return;\n case 'End':\n e.preventDefault();\n e.stopPropagation();\n items[items.length - 1]?.focus();\n return;\n case 'Escape':\n e.preventDefault();\n e.stopPropagation();\n this.#closeMenu(true);\n return;\n case 'Tab':\n this.#closeMenu();\n return;\n default:\n return;\n }\n };\n\n protected override updated() {\n // Mirror the column's data type onto the host so the header label can match\n // the body cell alignment by default (numeric/currency cells right-align,\n // so their headers should too). Kept in sync on every update because the\n // column config can change at runtime (reorder, reconfigure).\n this.setAttribute('data-cell-type', this.column.type ?? 'string');\n // Reflect reorderability as an attribute so SCSS can show the grab\n // cursor only on columns that can actually be dragged. Tracks both the\n // grid-level `columnReordering` flag (arriving via context) and the\n // per-column `reorderable: false` opt-out at runtime.\n if (this.isDraggable) {\n this.setAttribute('data-reorderable', '');\n } else {\n this.removeAttribute('data-reorderable');\n }\n // aria-colindex stays in sync with the parent header row's numbering.\n if (this.colindex > 0) {\n this.setAttribute('aria-colindex', String(this.colindex));\n }\n // aria-sort communicates current sort direction for this column to AT.\n if (this.isSortable) {\n const sort = this.state?.sorting.state.get(this.column.key);\n const dir = sort?.direction;\n this.setAttribute(\n 'aria-sort',\n dir === 'ascending' ? 'ascending' : dir === 'descending' ? 'descending' : 'none'\n );\n } else {\n this.removeAttribute('aria-sort');\n }\n }\n\n public override connectedCallback(): void {\n super.connectedCallback();\n this.setAttribute('role', 'columnheader');\n this.addEventListener('pointerdown', this.#handleReorderPointerDown);\n document.addEventListener('pointerdown', this.#handleMenuOutside, true);\n }\n\n public override disconnectedCallback(): void {\n this.removeEventListener('pointerdown', this.#handleReorderPointerDown);\n document.removeEventListener('pointerdown', this.#handleMenuOutside, true);\n super.disconnectedCallback();\n }\n\n protected renderSortPart() {\n const state = this.state.sorting.state.get(this.column.key);\n const idx = Array.from(this.state.sorting.state.values()).indexOf(state!);\n const attr = this.state.host.sortConfiguration.multiple\n ? idx > -1\n ? idx + 1\n : nothing\n : nothing;\n const direction = state?.direction ?? 'none';\n\n const label =\n state?.direction === 'ascending'\n ? this.state.localize('header.sortedAscending')\n : state?.direction === 'descending'\n ? this.state.localize('header.sortedDescending')\n : this.state.localize('header.notSorted');\n\n return state || this.isSortable\n ? this.isSortable\n ? html`<button\n type=\"button\"\n part=${partNameMap({ action: true, sorted: !!state?.direction })}\n data-sort-active=${direction}\n data-sort-index=${attr === nothing ? '' : (attr as number)}\n aria-label=${label}\n @click=${this.#handleClick}\n >\n ${renderSortArrows()}\n </button>`\n : html`<span\n part=${partNameMap({ action: true, sorted: !!state?.direction })}\n data-sort-active=${direction}\n data-sort-index=${attr === nothing ? '' : (attr as number)}\n aria-hidden=\"true\"\n >\n ${renderSortArrows()}\n </span>`\n : nothing;\n }\n\n protected renderFilterButton() {\n if (!this.isFilterable) return nothing;\n const count = this.state?.filtering?.state.get(this.column.key)?.length ?? 0;\n return html`<button\n type=\"button\"\n part=${partNameMap({ action: true, 'filter-btn': true, 'filter-active': this.hasActiveFilter })}\n aria-label=${this.state.localize('header.filterColumn')}\n @click=${this.#handleFilterClick}\n >\n ${renderIcon('filter')}\n ${count > 1 ? html`<span part=\"filter-count\">${count}</span>` : nothing}\n </button>`;\n }\n\n protected renderMenuButton() {\n // Suppress the kebab entirely when the grid opts out.\n if (this.state?.host?.columnMenu === false) return nothing;\n // Show the kebab when this column has built-in menu items (sort / autosize)\n // or when a feature module provides a richer column menu (the enterprise\n // context menu: pin / hide / group / chart), so the affordance appears even\n // on columns that are neither sortable nor resizable.\n const hasMenuItems =\n this.isSortable || this.column.resizable || Boolean(this.state?.hasColumnMenu);\n if (!hasMenuItems) return nothing;\n return html`<button\n type=\"button\"\n part=${partNameMap({ action: true, 'menu-btn': true })}\n aria-label=${this.state.localize('header.columnMenu')}\n aria-expanded=${this.menuOpen ? 'true' : 'false'}\n aria-haspopup=\"menu\"\n @click=${this.#handleMenuClick}\n @keydown=${this.#handleMenuTriggerKeydown}\n >\n ${renderIcon('more-vert')}\n </button>`;\n }\n\n protected renderColumnMenu() {\n if (!this.menuOpen) return nothing;\n return html`<div part=\"col-menu\" role=\"menu\" @keydown=${this.#handleColMenuKeydown}>\n ${\n this.isSortable\n ? html`\n <button\n type=\"button\"\n part=\"col-menu-item\"\n role=\"menuitem\"\n tabindex=\"-1\"\n @click=${() => {\n this.state.sorting.sort({ key: this.column.key, direction: 'ascending' } as any);\n this.menuOpen = false;\n }}\n >\n ${renderIcon('sort-asc')} ${this.state.localize('contextMenu.sortAsc')}\n </button>\n <button\n type=\"button\"\n part=\"col-menu-item\"\n role=\"menuitem\"\n tabindex=\"-1\"\n @click=${() => {\n this.state.sorting.sort({ key: this.column.key, direction: 'descending' } as any);\n this.menuOpen = false;\n }}\n >\n ${renderIcon('sort-desc')} ${this.state.localize('contextMenu.sortDesc')}\n </button>\n `\n : nothing\n }\n ${\n this.column.resizable\n ? html`<button\n type=\"button\"\n part=\"col-menu-item\"\n role=\"menuitem\"\n tabindex=\"-1\"\n @click=${() => {\n this.state.resizing.autosize(this.column, this);\n this.menuOpen = false;\n }}\n >\n ${renderIcon('arrow-upward')} ${this.state.localize('header.autosizeColumn')}\n </button>`\n : nothing\n }\n </div>`;\n }\n\n protected renderContentPart() {\n const defaultContent = this.column.headerText ?? this.column.key;\n const template = this.column.headerTemplate;\n\n return html`\n <span part=\"title\">\n ${\n this.coordinateLetter\n ? html`<span part=\"coord-letter\" aria-hidden=\"true\">${this.coordinateLetter}</span>`\n : nothing\n }\n <span>${template ? template(this.context) : html`${defaultContent}`}</span>\n </span>\n `;\n }\n\n protected renderResizePart() {\n return this.column.resizable\n ? html`<span\n part=\"resizable\"\n @dblclick=${this.#handleAutosize}\n @pointerdown=${this.#handleResizeStart}\n ></span>`\n : nothing;\n }\n\n protected override render() {\n return html`\n <div\n part=${partNameMap({\n content: true,\n sortable: this.isSortable,\n resizing: this.resizeController.indicatorActive,\n })}\n >\n ${this.renderContentPart()}\n <div part=\"actions\">\n ${this.renderSortPart()}\n ${this.renderFilterButton()}\n ${this.renderMenuButton()}\n </div>\n </div>\n ${this.renderResizePart()}\n ${this.renderColumnMenu()}\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n [ApexGridHeader.tagName]: ApexGridHeader<object>;\n }\n}\n"]}
@@ -106,16 +106,16 @@ export class ExpansionController {
106
106
  this.expanded = next;
107
107
  this.host.requestUpdate();
108
108
  if (added.length === 1 && removed.length === 0) {
109
- this.host.announce('Row expanded');
109
+ this.host.announce(this.host.localize('announce.rowExpanded'));
110
110
  }
111
111
  else if (added.length === 0 && removed.length === 1) {
112
- this.host.announce('Row collapsed');
112
+ this.host.announce(this.host.localize('announce.rowCollapsed'));
113
113
  }
114
114
  else if (added.length > 0 && next.size === added.length + this.expanded.size - added.length) {
115
- this.host.announce(`${added.length} rows expanded`);
115
+ this.host.announce(this.host.localize('announce.rowsExpanded', { count: added.length }));
116
116
  }
117
117
  else if (removed.length > 0 && next.size === 0) {
118
- this.host.announce('All rows collapsed');
118
+ this.host.announce(this.host.localize('announce.allRowsCollapsed'));
119
119
  }
120
120
  this.host.emitEvent('rowExpanded', {
121
121
  detail: { added, removed, expanded: Array.from(this.expanded) },
@@ -1 +1 @@
1
- {"version":3,"file":"expansion.js","sourceRoot":"","sources":["../../src/controllers/expansion.ts"],"names":[],"mappings":"AAUA,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC,MAAM,CAAC;IACpD,OAAO,EAAE,KAAK;IACd,gBAAgB,EAAE,IAAI;CACvB,CAAC,CAAC;AAiBH,MAAM,OAAO,mBAAmB;IAI9B,YAAsB,IAAiB;QAAjB,SAAI,GAAJ,IAAI,CAAa;QAFhC,aAAQ,GAAW,IAAI,GAAG,EAAE,CAAC;QAGlC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAEM,aAAa,KAAI,CAAC;IAGzB,IAAW,OAAO;QAChB,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;IAGD,IAAW,gBAAgB;QACzB,OAAO,CACL,IAAI,CAAC,OAAO;YACZ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,gBAAgB,IAAI,wBAAwB,CAAC,gBAAgB,CAAC,CACrF,CAAC;IACJ,CAAC;IAGM,UAAU,CAAC,GAAM;QACtB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAOM,SAAS,CAAC,GAAM;QACrB,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QAChC,IAAI,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,KAAK,UAAU;YAAE,OAAO,KAAK,CAAC;QAC5E,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC;QACpD,OAAO,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACpD,CAAC;IAGM,YAAY;QACjB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;IAGM,KAAK,CAAC,SAAS,CAAC,GAAM;QAC3B,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QAClE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;;YAC/B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnB,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAGM,KAAK,CAAC,SAAS,CAAC,GAAM;QAC3B,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QACvC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAGM,KAAK,CAAC,WAAW,CAAC,GAAM;QAC7B,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QACzC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjB,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAMM,KAAK,CAAC,SAAS;QACpB,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,KAAK,UAAU;YAAE,OAAO,KAAK,CAAC;QAC7F,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC;QACpD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,QAA4B,EAAE,CAAC;YACzD,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,GAAG,CAAC;gBAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClD,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAGM,KAAK,CAAC,WAAW;QACtB,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1C,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;IACjC,CAAC;IAMM,KAAK,CAAC,gBAAgB,CAAC,IAAsB;QAClD,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC;QACpD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC9E,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAY;QACxB,MAAM,KAAK,GAAQ,EAAE,CAAC;QACtB,MAAM,OAAO,GAAQ,EAAE,CAAC;QACxB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/C,CAAC;QACD,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACxC,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAE5D,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE;YAClD,MAAM,EAAE;gBACN,KAAK;gBACL,OAAO;gBACP,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAClC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;aACvB;YACD,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC;QACH,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QAE3B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;QAE1B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/C,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;QACrC,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtD,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;QACtC,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;YAC9F,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC,MAAM,gBAAgB,CAAC,CAAC;QACtD,CAAC;aAAM,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACjD,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC;QAC3C,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE;YACjC,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;SAChE,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;CACF","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"]}
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(this.host.localize('announce.rowExpanded'));\n } else if (added.length === 0 && removed.length === 1) {\n this.host.announce(this.host.localize('announce.rowCollapsed'));\n } else if (added.length > 0 && next.size === added.length + this.expanded.size - added.length) {\n this.host.announce(this.host.localize('announce.rowsExpanded', { count: added.length }));\n } else if (removed.length > 0 && next.size === 0) {\n this.host.announce(this.host.localize('announce.allRowsCollapsed'));\n }\n\n this.host.emitEvent('rowExpanded', {\n detail: { added, removed, expanded: Array.from(this.expanded) },\n });\n return true;\n }\n}\n"]}
@@ -86,6 +86,7 @@ export class FilterController {
86
86
  this.reset(key);
87
87
  this.#filter([]);
88
88
  await this.host.updateComplete;
89
+ this.#announce(key, 'cleared');
89
90
  this.#emitFilteredEvent({ key, state: this.get(key)?.all ?? [] });
90
91
  }
91
92
  async removeExpression(expression) {
@@ -99,6 +100,7 @@ export class FilterController {
99
100
  }
100
101
  this.#filter([]);
101
102
  await this.host.updateComplete;
103
+ this.#announce(expression.key, 'cleared');
102
104
  this.#emitFilteredEvent({ key: expression.key, state: state?.all ?? [] });
103
105
  }
104
106
  async filterWithEvent(expression, type) {
@@ -107,8 +109,17 @@ export class FilterController {
107
109
  }
108
110
  this.#filter(expression);
109
111
  await this.host.updateComplete;
112
+ if (type === 'add')
113
+ this.#announce(expression.key, 'applied');
110
114
  this.#emitFilteredEvent({ key: expression.key, state: this.get(expression.key)?.all ?? [] });
111
115
  }
116
+ #announce(key, kind) {
117
+ const column = this.host.getColumn(key);
118
+ const label = column?.headerText ?? String(key);
119
+ this.host.announce(this.host.localize(kind === 'applied' ? 'announce.filtered' : 'announce.filterCleared', {
120
+ label,
121
+ }));
122
+ }
112
123
  filter(expression) {
113
124
  this.#filter(asArray(expression).map((expr) => Object.assign(this.getDefaultExpression(this.host.getColumn(expr.key)), expr)));
114
125
  }
@@ -1 +1 @@
1
- {"version":3,"file":"filter.js","sourceRoot":"","sources":["../../src/controllers/filter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController } from 'lit';\nimport type { ApexFilteredEvent } from '../components/grid.js';\nimport { PIPELINE } from '../internal/constants.js';\nimport type { ColumnConfiguration, GridHost, Keys } from '../internal/types.js';\nimport { asArray, getFilterOperandsFor } from '../internal/utils.js';\nimport { FilterState } from '../operations/filter/state.js';\nimport type { FilterExpression } from '../operations/filter/types.js';\n\nexport class FilterController<T extends object> implements ReactiveController {\n constructor(protected host: GridHost<T>) {\n this.host.addController(this);\n }\n\n public state: FilterState<T> = new FilterState();\n\n public get filterRow() {\n // @ts-expect-error - protected access\n return this.host.filterRow;\n }\n\n get #virtualizer() {\n // @ts-expect-error - protected access\n return this.host.scrollContainer;\n }\n\n #emitFilteringEvent(expression: FilterExpression<T>, type: 'add' | 'modify' | 'remove') {\n return this.host.emitEvent('filtering', {\n detail: {\n key: expression.key,\n expressions: [expression],\n type,\n },\n cancelable: true,\n });\n }\n\n #emitFilteredEvent(detail?: ApexFilteredEvent<T>) {\n return this.host.emitEvent('filtered', { detail });\n }\n\n #filter(expression: FilterExpression<T> | FilterExpression<T>[]) {\n for (const expr of asArray(expression)) {\n this.state.set(expr);\n }\n\n // HACK: In the case where the scrollTop is a large and amount and a big chunk of data is filtered out\n // HACK: the virtualizer can't recalculate its scroll position correctly. Thus, we reset the scrollTop state.\n this.#virtualizer?.scrollTo({ top: 0 });\n this.host.requestUpdate(PIPELINE);\n }\n\n public hostConnected() {}\n\n public hostUpdate(): void {\n this.filterRow?.requestUpdate();\n }\n\n public get(key: Keys<T>) {\n return this.state.get(key);\n }\n\n public reset(key?: Keys<T>) {\n key ? this.state.delete(key) : this.state.clear();\n }\n\n public setActiveColumn(column?: ColumnConfiguration<T>, triggerRect?: DOMRect) {\n const filterRow = this.filterRow;\n if (!filterRow) return;\n\n if (!column?.filter) {\n filterRow.active = false;\n return;\n }\n\n // Toggle: clicking the same column's filter while the panel is open closes it.\n if (filterRow.active && filterRow.column?.key === column.key) {\n filterRow.active = false;\n return;\n }\n\n filterRow.triggerRect = triggerRect ?? null;\n filterRow.column = column;\n filterRow.expression = this.getDefaultExpression(column);\n filterRow.active = true;\n this.host.requestUpdate();\n }\n\n public getDefaultExpression(column: ColumnConfiguration<T>) {\n const caseSensitive =\n typeof column.filter === 'boolean' ? false : Boolean(column.filter?.caseSensitive);\n const operands = getFilterOperandsFor(column);\n const keys = Object.keys(operands) as Keys<typeof operands>[];\n\n // XXX: Types\n return {\n key: column.key,\n condition: operands[keys[0]],\n caseSensitive,\n } as unknown as FilterExpression<T>;\n }\n\n public async removeAllExpressions(key: Keys<T>) {\n const state = this.get(key)?.all ?? [];\n\n if (\n !this.host.emitEvent('filtering', {\n detail: {\n key,\n expressions: state,\n type: 'remove',\n },\n cancelable: true,\n })\n ) {\n return;\n }\n\n this.reset(key);\n this.#filter([]);\n\n await this.host.updateComplete;\n this.#emitFilteredEvent({ key, state: this.get(key)?.all ?? [] });\n }\n\n public async removeExpression(expression: FilterExpression<T>) {\n const state = this.get(expression.key);\n\n if (!this.#emitFilteringEvent(expression, 'remove')) {\n return;\n }\n\n state?.remove(expression);\n\n if (state?.empty) {\n this.reset(state.key);\n }\n\n this.#filter([]);\n\n await this.host.updateComplete;\n this.#emitFilteredEvent({ key: expression.key, state: state?.all ?? [] });\n }\n\n public async filterWithEvent(expression: FilterExpression<T>, type: 'add' | 'modify' | 'remove') {\n if (!this.#emitFilteringEvent(expression, type)) {\n return;\n }\n\n this.#filter(expression);\n\n await this.host.updateComplete;\n this.#emitFilteredEvent({ key: expression.key, state: this.get(expression.key)?.all ?? [] });\n }\n\n public filter(expression: FilterExpression<T> | FilterExpression<T>[]) {\n this.#filter(\n asArray(expression).map((expr) =>\n Object.assign(this.getDefaultExpression(this.host.getColumn(expr.key)!), expr)\n )\n );\n }\n}\n"]}
1
+ {"version":3,"file":"filter.js","sourceRoot":"","sources":["../../src/controllers/filter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController } from 'lit';\nimport type { ApexFilteredEvent } from '../components/grid.js';\nimport { PIPELINE } from '../internal/constants.js';\nimport type { ColumnConfiguration, GridHost, Keys } from '../internal/types.js';\nimport { asArray, getFilterOperandsFor } from '../internal/utils.js';\nimport { FilterState } from '../operations/filter/state.js';\nimport type { FilterExpression } from '../operations/filter/types.js';\n\nexport class FilterController<T extends object> implements ReactiveController {\n constructor(protected host: GridHost<T>) {\n this.host.addController(this);\n }\n\n public state: FilterState<T> = new FilterState();\n\n public get filterRow() {\n // @ts-expect-error - protected access\n return this.host.filterRow;\n }\n\n get #virtualizer() {\n // @ts-expect-error - protected access\n return this.host.scrollContainer;\n }\n\n #emitFilteringEvent(expression: FilterExpression<T>, type: 'add' | 'modify' | 'remove') {\n return this.host.emitEvent('filtering', {\n detail: {\n key: expression.key,\n expressions: [expression],\n type,\n },\n cancelable: true,\n });\n }\n\n #emitFilteredEvent(detail?: ApexFilteredEvent<T>) {\n return this.host.emitEvent('filtered', { detail });\n }\n\n #filter(expression: FilterExpression<T> | FilterExpression<T>[]) {\n for (const expr of asArray(expression)) {\n this.state.set(expr);\n }\n\n // HACK: In the case where the scrollTop is a large and amount and a big chunk of data is filtered out\n // HACK: the virtualizer can't recalculate its scroll position correctly. Thus, we reset the scrollTop state.\n this.#virtualizer?.scrollTo({ top: 0 });\n this.host.requestUpdate(PIPELINE);\n }\n\n public hostConnected() {}\n\n public hostUpdate(): void {\n this.filterRow?.requestUpdate();\n }\n\n public get(key: Keys<T>) {\n return this.state.get(key);\n }\n\n public reset(key?: Keys<T>) {\n key ? this.state.delete(key) : this.state.clear();\n }\n\n public setActiveColumn(column?: ColumnConfiguration<T>, triggerRect?: DOMRect) {\n const filterRow = this.filterRow;\n if (!filterRow) return;\n\n if (!column?.filter) {\n filterRow.active = false;\n return;\n }\n\n // Toggle: clicking the same column's filter while the panel is open closes it.\n if (filterRow.active && filterRow.column?.key === column.key) {\n filterRow.active = false;\n return;\n }\n\n filterRow.triggerRect = triggerRect ?? null;\n filterRow.column = column;\n filterRow.expression = this.getDefaultExpression(column);\n filterRow.active = true;\n this.host.requestUpdate();\n }\n\n public getDefaultExpression(column: ColumnConfiguration<T>) {\n const caseSensitive =\n typeof column.filter === 'boolean' ? false : Boolean(column.filter?.caseSensitive);\n const operands = getFilterOperandsFor(column);\n const keys = Object.keys(operands) as Keys<typeof operands>[];\n\n // XXX: Types\n return {\n key: column.key,\n condition: operands[keys[0]],\n caseSensitive,\n } as unknown as FilterExpression<T>;\n }\n\n public async removeAllExpressions(key: Keys<T>) {\n const state = this.get(key)?.all ?? [];\n\n if (\n !this.host.emitEvent('filtering', {\n detail: {\n key,\n expressions: state,\n type: 'remove',\n },\n cancelable: true,\n })\n ) {\n return;\n }\n\n this.reset(key);\n this.#filter([]);\n\n await this.host.updateComplete;\n this.#announce(key, 'cleared');\n this.#emitFilteredEvent({ key, state: this.get(key)?.all ?? [] });\n }\n\n public async removeExpression(expression: FilterExpression<T>) {\n const state = this.get(expression.key);\n\n if (!this.#emitFilteringEvent(expression, 'remove')) {\n return;\n }\n\n state?.remove(expression);\n\n if (state?.empty) {\n this.reset(state.key);\n }\n\n this.#filter([]);\n\n await this.host.updateComplete;\n this.#announce(expression.key, 'cleared');\n this.#emitFilteredEvent({ key: expression.key, state: state?.all ?? [] });\n }\n\n public async filterWithEvent(expression: FilterExpression<T>, type: 'add' | 'modify' | 'remove') {\n if (!this.#emitFilteringEvent(expression, type)) {\n return;\n }\n\n this.#filter(expression);\n\n await this.host.updateComplete;\n // Announce only on `add`: `modify` fires per keystroke while typing a\n // filter value and would flood the live region.\n if (type === 'add') this.#announce(expression.key, 'applied');\n this.#emitFilteredEvent({ key: expression.key, state: this.get(expression.key)?.all ?? [] });\n }\n\n #announce(key: Keys<T>, kind: 'applied' | 'cleared') {\n const column = this.host.getColumn(key);\n const label = column?.headerText ?? String(key);\n this.host.announce(\n this.host.localize(kind === 'applied' ? 'announce.filtered' : 'announce.filterCleared', {\n label,\n })\n );\n }\n\n public filter(expression: FilterExpression<T> | FilterExpression<T>[]) {\n this.#filter(\n asArray(expression).map((expr) =>\n Object.assign(this.getDefaultExpression(this.host.getColumn(expr.key)!), expr)\n )\n );\n }\n}\n"]}
@@ -77,7 +77,7 @@ export class HistoryController {
77
77
  return;
78
78
  command.revert();
79
79
  this.#redo.push(command);
80
- this.#afterReplay(command, 'Undo');
80
+ this.#afterReplay(command, 'undo');
81
81
  }
82
82
  redo() {
83
83
  const command = this.#redo.pop();
@@ -85,7 +85,7 @@ export class HistoryController {
85
85
  return;
86
86
  command.apply();
87
87
  this.#undo.push(command);
88
- this.#afterReplay(command, 'Redo');
88
+ this.#afterReplay(command, 'redo');
89
89
  }
90
90
  clear() {
91
91
  if (this.#undo.length === 0 && this.#redo.length === 0)
@@ -103,10 +103,17 @@ export class HistoryController {
103
103
  }
104
104
  this.#emitChanged();
105
105
  }
106
- #afterReplay(command, label) {
106
+ #afterReplay(command, kind) {
107
107
  this.host.requestUpdate(PIPELINE);
108
108
  const count = command instanceof CellEditCommand ? command.changes.length : 1;
109
- this.host.announce(`${label} ${count} cell ${count === 1 ? 'change' : 'changes'}`);
109
+ const key = kind === 'undo'
110
+ ? count === 1
111
+ ? 'announce.undoOne'
112
+ : 'announce.undoMany'
113
+ : count === 1
114
+ ? 'announce.redoOne'
115
+ : 'announce.redoMany';
116
+ this.host.announce(this.host.localize(key, { count }));
110
117
  this.#emitChanged();
111
118
  }
112
119
  #emitChanged() {
@@ -1 +1 @@
1
- {"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/controllers/history.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController } from 'lit';\nimport { PIPELINE } from '../internal/constants.js';\nimport type { GridHost, Keys } from '../internal/types.js';\n\n/** Default number of commands retained on the undo stack. */\nconst DEFAULT_STACK_SIZE = 100;\n\n/**\n * A single recorded cell mutation: the record reference, its column, and the\n * before / after values. `rowIndex` is the view-relative index at record time\n * and is used only for the replayed `cellValueChanged` event (the live record\n * reference is authoritative).\n */\nexport interface CellChange<T extends object> {\n record: T;\n key: Keys<T>;\n rowIndex: number;\n oldValue: unknown;\n newValue: unknown;\n}\n\n/**\n * A reversible unit of work on the history stacks.\n *\n * @remarks\n * `apply` re-performs the change (redo); `revert` undoes it. Cell-data edits use\n * {@link CellEditCommand}; the interface is deliberately open so later command\n * kinds (e.g. view-state) can join the same stacks.\n */\nexport interface EditCommand {\n apply(): void;\n revert(): void;\n}\n\n/**\n * One or more cell mutations that undo / redo as a single step. A single-cell\n * edit holds one change; a row-mode commit or a bulk paste / fill holds many so\n * one Ctrl+Z reverts the whole batch.\n */\nexport class CellEditCommand<T extends object> implements EditCommand {\n constructor(\n private host: GridHost<T>,\n public readonly changes: ReadonlyArray<CellChange<T>>\n ) {}\n\n public apply(): void {\n for (const change of this.changes) {\n this.#write(change, change.newValue);\n }\n }\n\n public revert(): void {\n // Revert in reverse so overlapping writes to the same cell unwind cleanly.\n for (let i = this.changes.length - 1; i >= 0; i -= 1) {\n this.#write(this.changes[i], this.changes[i].oldValue);\n }\n }\n\n #write(change: CellChange<T>, value: unknown): void {\n (change.record as Record<string, unknown>)[change.key as string] = value;\n // Re-emit `cellValueChanged` (never the cancellable `cellValueChanging`) so\n // data-bound consumers stay in sync. Validation and history recording are\n // intentionally skipped on replay. Prefer the record's current view index\n // when it is on the page; fall back to the recorded one.\n const liveIndex = this.host.pageItems.indexOf(change.record);\n this.host.emitEvent('cellValueChanged', {\n detail: {\n key: change.key,\n rowIndex: liveIndex >= 0 ? liveIndex : change.rowIndex,\n data: change.record,\n value,\n },\n });\n }\n}\n\n/**\n * Reactive controller owning the undo / redo stacks for cell-data edits.\n *\n * @remarks\n * Recording is driven from the {@link EditingController.applyCellEdit} choke\n * point: every successful write calls {@link record}. Single edits push one\n * command; {@link beginBatch} / {@link endBatch} coalesce a row-mode commit or a\n * bulk paste / fill into one command. Disabled unless\n * `editing.history.enabled` is set, so the community grid pays nothing.\n */\nexport class HistoryController<T extends object> implements ReactiveController {\n #undo: EditCommand[] = [];\n #redo: EditCommand[] = [];\n #batch: CellChange<T>[] | null = null;\n\n constructor(protected host: GridHost<T>) {\n this.host.addController(this);\n }\n\n public hostConnected(): void {}\n\n /** Whether undo / redo tracking is enabled. */\n public get enabled(): boolean {\n return Boolean(this.host.editing?.history?.enabled);\n }\n\n /** Maximum number of commands retained on the undo stack. */\n public get stackSize(): number {\n const size = this.host.editing?.history?.stackSize;\n return typeof size === 'number' && size > 0 ? size : DEFAULT_STACK_SIZE;\n }\n\n /** Whether there is at least one command to undo. */\n public get canUndo(): boolean {\n return this.#undo.length > 0;\n }\n\n /** Whether there is at least one command to redo. */\n public get canRedo(): boolean {\n return this.#redo.length > 0;\n }\n\n /**\n * Records a single cell mutation. Routed to the open batch when one is active,\n * otherwise pushed immediately as a standalone command. No-op when history is\n * disabled.\n */\n public record(change: CellChange<T>): void {\n if (!this.enabled) return;\n if (this.#batch) {\n this.#batch.push(change);\n return;\n }\n this.#push(new CellEditCommand(this.host, [change]));\n }\n\n /**\n * Opens a batch so subsequent {@link record} calls coalesce into one command.\n * Nestable calls are flattened (the outermost {@link endBatch} commits).\n */\n public beginBatch(): void {\n if (!this.enabled) return;\n this.#batch ??= [];\n }\n\n /**\n * Closes the open batch, pushing it as a single command when it captured any\n * changes.\n */\n public endBatch(): void {\n const batch = this.#batch;\n this.#batch = null;\n if (batch && batch.length > 0) {\n this.#push(new CellEditCommand(this.host, batch));\n }\n }\n\n /** Reverts the most recent command. No-op when the undo stack is empty. */\n public undo(): void {\n const command = this.#undo.pop();\n if (!command) return;\n command.revert();\n this.#redo.push(command);\n this.#afterReplay(command, 'Undo');\n }\n\n /** Re-applies the most recently undone command. No-op when nothing to redo. */\n public redo(): void {\n const command = this.#redo.pop();\n if (!command) return;\n command.apply();\n this.#undo.push(command);\n this.#afterReplay(command, 'Redo');\n }\n\n /** Clears both stacks (e.g. after a data reset). */\n public clear(): void {\n if (this.#undo.length === 0 && this.#redo.length === 0) return;\n this.#undo = [];\n this.#redo = [];\n this.#batch = null;\n this.#emitChanged();\n }\n\n #push(command: EditCommand): void {\n this.#undo.push(command);\n // A fresh edit invalidates the redo branch.\n this.#redo = [];\n // Evict the oldest commands once the stack exceeds its cap.\n while (this.#undo.length > this.stackSize) {\n this.#undo.shift();\n }\n this.#emitChanged();\n }\n\n #afterReplay(command: EditCommand, label: string): void {\n this.host.requestUpdate(PIPELINE);\n const count = command instanceof CellEditCommand ? command.changes.length : 1;\n this.host.announce(`${label} ${count} cell ${count === 1 ? 'change' : 'changes'}`);\n this.#emitChanged();\n }\n\n #emitChanged(): void {\n this.host.emitEvent('historyChanged', {\n detail: { canUndo: this.canUndo, canRedo: this.canRedo },\n });\n }\n}\n"]}
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/controllers/history.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController } from 'lit';\nimport { PIPELINE } from '../internal/constants.js';\nimport type { GridHost, Keys } from '../internal/types.js';\n\n/** Default number of commands retained on the undo stack. */\nconst DEFAULT_STACK_SIZE = 100;\n\n/**\n * A single recorded cell mutation: the record reference, its column, and the\n * before / after values. `rowIndex` is the view-relative index at record time\n * and is used only for the replayed `cellValueChanged` event (the live record\n * reference is authoritative).\n */\nexport interface CellChange<T extends object> {\n record: T;\n key: Keys<T>;\n rowIndex: number;\n oldValue: unknown;\n newValue: unknown;\n}\n\n/**\n * A reversible unit of work on the history stacks.\n *\n * @remarks\n * `apply` re-performs the change (redo); `revert` undoes it. Cell-data edits use\n * {@link CellEditCommand}; the interface is deliberately open so later command\n * kinds (e.g. view-state) can join the same stacks.\n */\nexport interface EditCommand {\n apply(): void;\n revert(): void;\n}\n\n/**\n * One or more cell mutations that undo / redo as a single step. A single-cell\n * edit holds one change; a row-mode commit or a bulk paste / fill holds many so\n * one Ctrl+Z reverts the whole batch.\n */\nexport class CellEditCommand<T extends object> implements EditCommand {\n constructor(\n private host: GridHost<T>,\n public readonly changes: ReadonlyArray<CellChange<T>>\n ) {}\n\n public apply(): void {\n for (const change of this.changes) {\n this.#write(change, change.newValue);\n }\n }\n\n public revert(): void {\n // Revert in reverse so overlapping writes to the same cell unwind cleanly.\n for (let i = this.changes.length - 1; i >= 0; i -= 1) {\n this.#write(this.changes[i], this.changes[i].oldValue);\n }\n }\n\n #write(change: CellChange<T>, value: unknown): void {\n (change.record as Record<string, unknown>)[change.key as string] = value;\n // Re-emit `cellValueChanged` (never the cancellable `cellValueChanging`) so\n // data-bound consumers stay in sync. Validation and history recording are\n // intentionally skipped on replay. Prefer the record's current view index\n // when it is on the page; fall back to the recorded one.\n const liveIndex = this.host.pageItems.indexOf(change.record);\n this.host.emitEvent('cellValueChanged', {\n detail: {\n key: change.key,\n rowIndex: liveIndex >= 0 ? liveIndex : change.rowIndex,\n data: change.record,\n value,\n },\n });\n }\n}\n\n/**\n * Reactive controller owning the undo / redo stacks for cell-data edits.\n *\n * @remarks\n * Recording is driven from the {@link EditingController.applyCellEdit} choke\n * point: every successful write calls {@link record}. Single edits push one\n * command; {@link beginBatch} / {@link endBatch} coalesce a row-mode commit or a\n * bulk paste / fill into one command. Disabled unless\n * `editing.history.enabled` is set, so the community grid pays nothing.\n */\nexport class HistoryController<T extends object> implements ReactiveController {\n #undo: EditCommand[] = [];\n #redo: EditCommand[] = [];\n #batch: CellChange<T>[] | null = null;\n\n constructor(protected host: GridHost<T>) {\n this.host.addController(this);\n }\n\n public hostConnected(): void {}\n\n /** Whether undo / redo tracking is enabled. */\n public get enabled(): boolean {\n return Boolean(this.host.editing?.history?.enabled);\n }\n\n /** Maximum number of commands retained on the undo stack. */\n public get stackSize(): number {\n const size = this.host.editing?.history?.stackSize;\n return typeof size === 'number' && size > 0 ? size : DEFAULT_STACK_SIZE;\n }\n\n /** Whether there is at least one command to undo. */\n public get canUndo(): boolean {\n return this.#undo.length > 0;\n }\n\n /** Whether there is at least one command to redo. */\n public get canRedo(): boolean {\n return this.#redo.length > 0;\n }\n\n /**\n * Records a single cell mutation. Routed to the open batch when one is active,\n * otherwise pushed immediately as a standalone command. No-op when history is\n * disabled.\n */\n public record(change: CellChange<T>): void {\n if (!this.enabled) return;\n if (this.#batch) {\n this.#batch.push(change);\n return;\n }\n this.#push(new CellEditCommand(this.host, [change]));\n }\n\n /**\n * Opens a batch so subsequent {@link record} calls coalesce into one command.\n * Nestable calls are flattened (the outermost {@link endBatch} commits).\n */\n public beginBatch(): void {\n if (!this.enabled) return;\n this.#batch ??= [];\n }\n\n /**\n * Closes the open batch, pushing it as a single command when it captured any\n * changes.\n */\n public endBatch(): void {\n const batch = this.#batch;\n this.#batch = null;\n if (batch && batch.length > 0) {\n this.#push(new CellEditCommand(this.host, batch));\n }\n }\n\n /** Reverts the most recent command. No-op when the undo stack is empty. */\n public undo(): void {\n const command = this.#undo.pop();\n if (!command) return;\n command.revert();\n this.#redo.push(command);\n this.#afterReplay(command, 'undo');\n }\n\n /** Re-applies the most recently undone command. No-op when nothing to redo. */\n public redo(): void {\n const command = this.#redo.pop();\n if (!command) return;\n command.apply();\n this.#undo.push(command);\n this.#afterReplay(command, 'redo');\n }\n\n /** Clears both stacks (e.g. after a data reset). */\n public clear(): void {\n if (this.#undo.length === 0 && this.#redo.length === 0) return;\n this.#undo = [];\n this.#redo = [];\n this.#batch = null;\n this.#emitChanged();\n }\n\n #push(command: EditCommand): void {\n this.#undo.push(command);\n // A fresh edit invalidates the redo branch.\n this.#redo = [];\n // Evict the oldest commands once the stack exceeds its cap.\n while (this.#undo.length > this.stackSize) {\n this.#undo.shift();\n }\n this.#emitChanged();\n }\n\n #afterReplay(command: EditCommand, kind: 'undo' | 'redo'): void {\n this.host.requestUpdate(PIPELINE);\n const count = command instanceof CellEditCommand ? command.changes.length : 1;\n const key =\n kind === 'undo'\n ? count === 1\n ? 'announce.undoOne'\n : 'announce.undoMany'\n : count === 1\n ? 'announce.redoOne'\n : 'announce.redoMany';\n this.host.announce(this.host.localize(key, { count }));\n this.#emitChanged();\n }\n\n #emitChanged(): void {\n this.host.emitEvent('historyChanged', {\n detail: { canUndo: this.canUndo, canRedo: this.canRedo },\n });\n }\n}\n"]}