apex-grid 3.4.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/CHANGELOG.md +439 -0
  2. package/LICENSE.md +125 -0
  3. package/README.md +107 -14
  4. package/components/cell.d.ts +30 -4
  5. package/components/cell.js +199 -53
  6. package/components/cell.js.map +1 -1
  7. package/components/filter-row.d.ts +2 -0
  8. package/components/filter-row.js +82 -28
  9. package/components/filter-row.js.map +1 -1
  10. package/components/grid.d.ts +257 -20
  11. package/components/grid.js +463 -99
  12. package/components/grid.js.map +1 -1
  13. package/components/group-header-row.js +28 -25
  14. package/components/group-header-row.js.map +1 -1
  15. package/components/header-row.d.ts +7 -5
  16. package/components/header-row.js +19 -9
  17. package/components/header-row.js.map +1 -1
  18. package/components/header.js +36 -22
  19. package/components/header.js.map +1 -1
  20. package/components/row.d.ts +19 -3
  21. package/components/row.js +96 -22
  22. package/components/row.js.map +1 -1
  23. package/components/toolbar.js +2 -1
  24. package/components/toolbar.js.map +1 -1
  25. package/components/virtualizer.d.ts +45 -1
  26. package/components/virtualizer.js +184 -3
  27. package/components/virtualizer.js.map +1 -1
  28. package/controllers/dom.d.ts +4 -4
  29. package/controllers/dom.js +40 -22
  30. package/controllers/dom.js.map +1 -1
  31. package/controllers/editing.d.ts +104 -37
  32. package/controllers/editing.js +230 -78
  33. package/controllers/editing.js.map +1 -1
  34. package/controllers/expansion.d.ts +14 -5
  35. package/controllers/expansion.js +24 -1
  36. package/controllers/expansion.js.map +1 -1
  37. package/controllers/filter.d.ts +10 -2
  38. package/controllers/filter.js +62 -17
  39. package/controllers/filter.js.map +1 -1
  40. package/controllers/history.d.ts +45 -8
  41. package/controllers/history.js +58 -20
  42. package/controllers/history.js.map +1 -1
  43. package/controllers/navigation.d.ts +68 -13
  44. package/controllers/navigation.js +196 -51
  45. package/controllers/navigation.js.map +1 -1
  46. package/controllers/pagination.d.ts +6 -0
  47. package/controllers/pagination.js +10 -2
  48. package/controllers/pagination.js.map +1 -1
  49. package/controllers/reorder.js +42 -24
  50. package/controllers/reorder.js.map +1 -1
  51. package/controllers/resize.d.ts +17 -2
  52. package/controllers/resize.js +16 -6
  53. package/controllers/resize.js.map +1 -1
  54. package/controllers/row-pin.d.ts +20 -2
  55. package/controllers/row-pin.js +16 -9
  56. package/controllers/row-pin.js.map +1 -1
  57. package/controllers/row-reorder.d.ts +31 -11
  58. package/controllers/row-reorder.js +117 -37
  59. package/controllers/row-reorder.js.map +1 -1
  60. package/controllers/selection.d.ts +11 -4
  61. package/controllers/selection.js +26 -11
  62. package/controllers/selection.js.map +1 -1
  63. package/controllers/sort.d.ts +22 -2
  64. package/controllers/sort.js +29 -23
  65. package/controllers/sort.js.map +1 -1
  66. package/controllers/state.d.ts +65 -10
  67. package/controllers/state.js +86 -6
  68. package/controllers/state.js.map +1 -1
  69. package/controllers/tree.d.ts +15 -10
  70. package/controllers/tree.js +0 -0
  71. package/controllers/tree.js.map +1 -1
  72. package/custom-elements.json +3589 -452
  73. package/docs/assets/hierarchy.js +1 -1
  74. package/docs/assets/highlight.css +15 -8
  75. package/docs/assets/navigation.js +1 -1
  76. package/docs/assets/search.js +1 -1
  77. package/docs/classes/ApexGrid.html +660 -0
  78. package/docs/classes/ApexGridPaginator.html +36 -0
  79. package/docs/classes/ApexGridToolbar.html +67 -0
  80. package/docs/functions/localize.html +2 -2
  81. package/docs/functions/setup.html +1 -2
  82. package/docs/hierarchy.html +1 -1
  83. package/docs/index.html +105 -15
  84. package/docs/interfaces/ApexCellValidationFailedEvent.html +3 -2
  85. package/docs/interfaces/ApexCellValueChangedEvent.html +4 -2
  86. package/docs/interfaces/ApexCellValueChangingEvent.html +3 -2
  87. package/docs/interfaces/ApexColumnMovedEvent.html +2 -2
  88. package/docs/interfaces/ApexColumnMovingEvent.html +1 -1
  89. package/docs/interfaces/ApexDetailContext.html +2 -2
  90. package/docs/interfaces/ApexGridEventMap.html +9 -6
  91. package/docs/interfaces/ApexRowEditEndedEvent.html +2 -1
  92. package/docs/interfaces/ApexRowEditStartedEvent.html +2 -1
  93. package/docs/interfaces/ApexRowMovingEvent.html +9 -3
  94. package/docs/interfaces/ApexRowPinningEvent.html +1 -1
  95. package/docs/interfaces/ApexTreeRowExpandedEvent.html +4 -1
  96. package/docs/interfaces/ApexTreeRowExpandingEvent.html +4 -1
  97. package/docs/interfaces/BaseApexCellContext.html +1 -1
  98. package/docs/interfaces/BaseApexEditorContext.html +1 -1
  99. package/docs/interfaces/BaseColumnConfiguration.html +3 -3
  100. package/docs/interfaces/BaseFilterExpression.html +2 -2
  101. package/docs/interfaces/CSVExportOptions.html +13 -5
  102. package/docs/interfaces/DataPipelineConfiguration.html +2 -2
  103. package/docs/interfaces/EditingHistoryConfiguration.html +1 -1
  104. package/docs/interfaces/ExportFormat.html +2 -2
  105. package/docs/interfaces/ExportOptions.html +5 -3
  106. package/docs/interfaces/GridExpansionConfiguration.html +6 -2
  107. package/docs/interfaces/GridRowPinningConfiguration.html +1 -1
  108. package/docs/interfaces/GridRowReorderingConfiguration.html +2 -2
  109. package/docs/interfaces/PaginationConfiguration.html +1 -1
  110. package/docs/interfaces/PaginationState.html +1 -1
  111. package/docs/interfaces/SetStateResult.html +3 -2
  112. package/docs/interfaces/ValidatorContext.html +7 -2
  113. package/docs/media/_token-values.scss +118 -0
  114. package/docs/media/package.json +5 -5
  115. package/docs/media/state-persistence.html +0 -10
  116. package/docs/modules.html +1 -1
  117. package/docs/types/DataPipelineParams.html +2 -2
  118. package/docs/types/EditMode.html +2 -2
  119. package/docs/types/ExportSource.html +6 -4
  120. package/docs/types/GridLocaleText.html +1 -1
  121. package/docs/types/OperandKeys.html +9 -0
  122. package/docs/types/PaginationMode.html +1 -1
  123. package/docs/variables/EN_LOCALE.html +3 -3
  124. package/docs/variables/esLocale.html +1 -1
  125. package/i18n/en.d.ts +19 -0
  126. package/i18n/en.js +19 -0
  127. package/i18n/en.js.map +1 -1
  128. package/i18n/es.js +19 -0
  129. package/i18n/es.js.map +1 -1
  130. package/index.d.ts +2 -0
  131. package/index.js +2 -0
  132. package/index.js.map +1 -1
  133. package/internal/column-types.d.ts +27 -1
  134. package/internal/column-types.js +75 -49
  135. package/internal/column-types.js.map +1 -1
  136. package/internal/constants.d.ts +1 -2
  137. package/internal/constants.js +0 -4
  138. package/internal/constants.js.map +1 -1
  139. package/internal/document-styles.d.ts +30 -0
  140. package/internal/document-styles.js +48 -0
  141. package/internal/document-styles.js.map +1 -0
  142. package/internal/export.d.ts +39 -7
  143. package/internal/export.js +35 -34
  144. package/internal/export.js.map +1 -1
  145. package/internal/feature-module.d.ts +82 -3
  146. package/internal/feature-module.js +11 -0
  147. package/internal/feature-module.js.map +1 -1
  148. package/internal/index.d.ts +6 -4
  149. package/internal/index.js +5 -3
  150. package/internal/index.js.map +1 -1
  151. package/internal/normalize-case.d.ts +8 -1
  152. package/internal/normalize-case.js +3 -2
  153. package/internal/normalize-case.js.map +1 -1
  154. package/internal/state-snapshot.d.ts +60 -8
  155. package/internal/state-snapshot.js +87 -19
  156. package/internal/state-snapshot.js.map +1 -1
  157. package/internal/types.d.ts +17 -2
  158. package/internal/types.js.map +1 -1
  159. package/internal/utils.d.ts +48 -6
  160. package/internal/utils.js +35 -21
  161. package/internal/utils.js.map +1 -1
  162. package/internal/validators.js +1 -0
  163. package/internal/validators.js.map +1 -1
  164. package/operations/filter/operands/number.js +9 -6
  165. package/operations/filter/operands/number.js.map +1 -1
  166. package/operations/filter/operands/string.js +2 -3
  167. package/operations/filter/operands/string.js.map +1 -1
  168. package/operations/filter/types.d.ts +13 -1
  169. package/operations/filter/types.js.map +1 -1
  170. package/operations/filter.d.ts +6 -0
  171. package/operations/filter.js +23 -7
  172. package/operations/filter.js.map +1 -1
  173. package/operations/quick-filter.js +2 -5
  174. package/operations/quick-filter.js.map +1 -1
  175. package/operations/sort.d.ts +21 -2
  176. package/operations/sort.js +41 -14
  177. package/operations/sort.js.map +1 -1
  178. package/package.json +8 -4
  179. package/setup.d.ts +23 -11
  180. package/setup.js +19 -21
  181. package/setup.js.map +1 -1
  182. package/styles/_common.css.js +1 -1
  183. package/styles/_common.css.js.map +1 -1
  184. package/styles/_token-values.css.d.ts +1 -0
  185. package/styles/_token-values.css.js +3 -0
  186. package/styles/_token-values.css.js.map +1 -0
  187. package/styles/_tokens.css.js +1 -1
  188. package/styles/_tokens.css.js.map +1 -1
  189. package/styles/body-cell/body-cell.css.js +1 -1
  190. package/styles/body-cell/body-cell.css.js.map +1 -1
  191. package/styles/body-cell/cell-renderers.css.js +1 -1
  192. package/styles/body-cell/cell-renderers.css.js.map +1 -1
  193. package/styles/body-row/body-row.css.js +1 -1
  194. package/styles/body-row/body-row.css.js.map +1 -1
  195. package/styles/document-tokens.css.d.ts +1 -0
  196. package/styles/document-tokens.css.js +3 -0
  197. package/styles/document-tokens.css.js.map +1 -0
  198. package/styles/filter-row/filter-row.css.js +1 -1
  199. package/styles/filter-row/filter-row.css.js.map +1 -1
  200. package/styles/grid/_grid.base.css.js +1 -1
  201. package/styles/grid/_grid.base.css.js.map +1 -1
  202. package/styles/grid/grid.css.js +1 -1
  203. package/styles/grid/grid.css.js.map +1 -1
  204. package/styles/group-header-row/group-header-row.css.js +1 -1
  205. package/styles/group-header-row/group-header-row.css.js.map +1 -1
  206. package/styles/header-cell/header-cell.css.js +1 -1
  207. package/styles/header-cell/header-cell.css.js.map +1 -1
  208. package/styles/header-row/header-row.base.css.js +1 -1
  209. package/styles/header-row/header-row.base.css.js.map +1 -1
  210. package/styles/paginator/paginator.css.js +1 -1
  211. package/styles/paginator/paginator.css.js.map +1 -1
  212. package/styles/toolbar/toolbar.css.js +1 -1
  213. package/styles/toolbar/toolbar.css.js.map +1 -1
  214. package/tokens.css +78 -0
  215. package/LICENSE +0 -80
  216. package/docs/media/LICENSE +0 -80
  217. package/docs/media/_tokens.scss +0 -193
@@ -1 +1 @@
1
- {"version":3,"file":"toolbar.js","sourceRoot":"","sources":["../../src/components/toolbar.ts"],"names":[],"mappings":"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","sourcesContent":["import { consume } from '@lit/context';\nimport { html, LitElement, nothing } from 'lit';\nimport { property, query, state } from 'lit/decorators.js';\nimport { gridStateContext, type StateController } from '../controllers/state.js';\nimport { renderIcon } from '../internal/icons.js';\nimport { registerComponent } from '../internal/register.js';\nimport { GRID_TOOLBAR_TAG } from '../internal/tags.js';\nimport { styles } from '../styles/toolbar/toolbar.css.js';\n\n/**\n * Toolbar shown above the grid's header row. Hosts opt-in toolbar features\n * such as the quick-filter (global search) input and the export menu.\n *\n * @element apex-grid-toolbar\n *\n * @csspart toolbar - The toolbar's container element.\n * @csspart toolbar-search - The quick-filter input wrapper.\n * @csspart search-field - The bordered field containing the icon + input.\n * @csspart search-icon - The leading search icon.\n * @csspart search-input - The text input itself.\n * @csspart toolbar-actions - The trailing actions area (right side).\n * @csspart toolbar-action - A custom host-contributed action button (e.g. \"Create chart\").\n * @csspart export-trigger - The export menu trigger button.\n * @csspart export-menu - The dropdown menu containing export options.\n * @csspart export-menu-item - A single menu item inside the export dropdown.\n */\nexport default class ApexGridToolbar<T extends object> extends LitElement {\n public static get tagName() {\n return GRID_TOOLBAR_TAG;\n }\n\n public static override styles = styles;\n\n /**\n * Registers the `<apex-grid-toolbar>` element. Idempotent.\n */\n public static register() {\n registerComponent(ApexGridToolbar);\n }\n\n @consume({ context: gridStateContext, subscribe: true })\n @property({ attribute: false })\n public state!: StateController<T>;\n\n /**\n * The current quick-filter value, mirrored from {@link ApexGrid.quickFilter}.\n */\n @property({ type: String })\n public value = '';\n\n /**\n * The placeholder rendered in the search input.\n *\n * @remarks\n * When unset, falls back to the grid's localized `toolbar.searchPlaceholder`\n * text (see {@link ApexGrid.localeText}).\n */\n @property({ type: String })\n public placeholder = '';\n\n protected get searchPlaceholder() {\n return this.placeholder || this.state?.localize('toolbar.searchPlaceholder') || 'Search…';\n }\n\n /**\n * Debounce window (ms) before {@link ApexGrid.quickFilter} updates.\n */\n @property({ type: Number, attribute: 'debounce' })\n public debounce = 200;\n\n /** Whether to render the quick-filter input. Driven by the parent grid. */\n @property({ attribute: false, type: Boolean })\n public showQuickFilter = true;\n\n /** Whether to render the export menu trigger. Driven by the parent grid. */\n @property({ attribute: false, type: Boolean })\n public showExport = false;\n\n /** Optional override filename (without extension) for exports. */\n @property({ attribute: false, type: String })\n public exportFilename = 'data';\n\n @query('input')\n protected input!: HTMLInputElement;\n\n @query('[part=\"export-trigger\"]')\n protected exportTrigger!: HTMLButtonElement;\n\n @query('[part=\"export-menu\"]')\n protected exportMenuEl!: HTMLElement;\n\n @state()\n protected exportMenuOpen = false;\n\n #debounceHandle: ReturnType<typeof setTimeout> | null = null;\n\n public override connectedCallback(): void {\n super.connectedCallback();\n document.addEventListener('pointerdown', this.#handleOutsidePointer, true);\n document.addEventListener('keydown', this.#handleDocumentKey, true);\n }\n\n public override disconnectedCallback(): void {\n document.removeEventListener('pointerdown', this.#handleOutsidePointer, true);\n document.removeEventListener('keydown', this.#handleDocumentKey, true);\n if (this.#debounceHandle) {\n clearTimeout(this.#debounceHandle);\n this.#debounceHandle = null;\n }\n super.disconnectedCallback();\n }\n\n #emit(value: string) {\n this.dispatchEvent(\n new CustomEvent<string>('apex-quick-filter', {\n detail: value,\n bubbles: true,\n composed: true,\n })\n );\n }\n\n #handleInput = (event: Event) => {\n event.stopPropagation();\n const next = (event.target as HTMLInputElement).value ?? '';\n if (this.#debounceHandle) clearTimeout(this.#debounceHandle);\n\n if (this.debounce <= 0) {\n this.#emit(next);\n return;\n }\n\n this.#debounceHandle = setTimeout(() => {\n this.#debounceHandle = null;\n this.#emit(next);\n }, this.debounce);\n };\n\n #handleKeydown = (event: KeyboardEvent) => {\n // Don't let arrow keys bubble into the grid navigation controller.\n event.stopPropagation();\n if (event.key === 'Escape' && this.input.value) {\n event.preventDefault();\n this.input.value = '';\n if (this.#debounceHandle) {\n clearTimeout(this.#debounceHandle);\n this.#debounceHandle = null;\n }\n this.#emit('');\n }\n };\n\n #toggleExportMenu = (event?: Event) => {\n event?.stopPropagation();\n this.exportMenuOpen = !this.exportMenuOpen;\n };\n\n #closeExportMenu(returnFocus = false) {\n if (!this.exportMenuOpen) return;\n this.exportMenuOpen = false;\n if (returnFocus) {\n // Return focus to the trigger so keyboard users don't lose their place.\n requestAnimationFrame(() => this.exportTrigger?.focus());\n }\n }\n\n #handleOutsidePointer = (event: PointerEvent) => {\n if (!this.exportMenuOpen) return;\n const path = event.composedPath();\n if (path.includes(this.exportTrigger) || path.includes(this.exportMenuEl)) return;\n this.#closeExportMenu();\n };\n\n #handleDocumentKey = (event: KeyboardEvent) => {\n if (!this.exportMenuOpen) return;\n if (event.key === 'Escape') {\n event.stopPropagation();\n this.#closeExportMenu(true);\n }\n };\n\n #handleExport = (formatId: string) => {\n this.#closeExportMenu();\n const grid = this.state?.host;\n if (!grid) return;\n grid.exportAs(formatId, { filename: this.exportFilename });\n };\n\n /** Lets keyboard users land in the menu when it opens via arrow-down. */\n #handleTriggerKeydown = (event: KeyboardEvent) => {\n if (event.key === 'ArrowDown' || event.key === 'Down') {\n event.preventDefault();\n if (!this.exportMenuOpen) this.exportMenuOpen = true;\n requestAnimationFrame(() => {\n const first = this.exportMenuEl?.querySelector<HTMLElement>('[role=\"menuitem\"]');\n first?.focus();\n });\n }\n };\n\n #handleMenuKeydown = (event: KeyboardEvent) => {\n const items = Array.from(\n this.exportMenuEl?.querySelectorAll<HTMLElement>('[role=\"menuitem\"]') ?? []\n );\n const current = items.indexOf(document.activeElement as HTMLElement);\n if (event.key === 'ArrowDown' || event.key === 'Down') {\n event.preventDefault();\n items[(current + 1) % items.length]?.focus();\n } else if (event.key === 'ArrowUp' || event.key === 'Up') {\n event.preventDefault();\n items[(current - 1 + items.length) % items.length]?.focus();\n } else if (event.key === 'Home') {\n event.preventDefault();\n items[0]?.focus();\n } else if (event.key === 'End') {\n event.preventDefault();\n items[items.length - 1]?.focus();\n } else if (event.key === 'Tab') {\n // Tab closes the menu so focus moves naturally to the next focusable.\n this.#closeExportMenu();\n }\n };\n\n protected renderQuickFilter() {\n if (!this.showQuickFilter) return nothing;\n return html`<div part=\"toolbar-search\">\n <label part=\"search-field\">\n ${renderIcon('search', { part: 'search-icon' })}\n <input\n part=\"search-input\"\n type=\"search\"\n role=\"searchbox\"\n aria-label=${this.searchPlaceholder}\n placeholder=${this.searchPlaceholder}\n .value=${this.value}\n @input=${this.#handleInput}\n @keydown=${this.#handleKeydown}\n />\n </label>\n </div>`;\n }\n\n protected renderExportMenu() {\n if (!this.showExport) return nothing;\n const open = this.exportMenuOpen;\n return html`<div part=\"export\">\n <button\n type=\"button\"\n part=\"export-trigger\"\n aria-label=${this.state.localize('toolbar.export')}\n aria-haspopup=\"menu\"\n aria-expanded=${open ? 'true' : 'false'}\n @click=${this.#toggleExportMenu}\n @keydown=${this.#handleTriggerKeydown}\n >\n ${renderIcon('download', { part: 'export-icon' })}\n ${renderIcon('chevron-down', { part: 'export-caret' })}\n </button>\n <ul\n part=\"export-menu\"\n role=\"menu\"\n aria-label=${this.state.localize('toolbar.exportOptions')}\n ?hidden=${!open}\n @keydown=${this.#handleMenuKeydown}\n >\n ${(this.state?.host?.exportFormats ?? []).map(\n (format) => html`<li role=\"none\">\n <button\n type=\"button\"\n role=\"menuitem\"\n part=\"export-menu-item\"\n tabindex=\"-1\"\n @click=${() => this.#handleExport(format.id)}\n >\n ${format.label}\n </button>\n </li>`\n )}\n </ul>\n </div>`;\n }\n\n protected renderToolbarActions() {\n const actions = this.state?.host?.toolbarActions ?? [];\n if (!actions.length) return nothing;\n return html`${actions.map(\n (action) => html`<button\n type=\"button\"\n part=\"toolbar-action\"\n data-action-id=${action.id ?? ''}\n @click=${() => action.run()}\n >\n ${action.label}\n </button>`\n )}`;\n }\n\n protected override render() {\n if (!this.state) return nothing;\n return html`\n <div part=\"toolbar\" role=\"toolbar\" aria-label=${this.state.localize('toolbar.label')}>\n ${this.renderQuickFilter()}\n <div part=\"toolbar-actions\">${this.renderToolbarActions()}${this.renderExportMenu()}</div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n [ApexGridToolbar.tagName]: ApexGridToolbar<object>;\n }\n}\n"]}
1
+ {"version":3,"file":"toolbar.js","sourceRoot":"","sources":["../../src/components/toolbar.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAwB,MAAM,yBAAyB,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,kCAAkC,CAAC;AAmB1D,MAAM,CAAC,OAAO,OAAO,eAAkC,SAAQ,UAAU;IAAzE;;QAsBS,UAAK,GAAG,EAAE,CAAC;QAUX,gBAAW,GAAG,EAAE,CAAC;QAUjB,aAAQ,GAAG,GAAG,CAAC;QAIf,oBAAe,GAAG,IAAI,CAAC;QAIvB,eAAU,GAAG,KAAK,CAAC;QAInB,mBAAc,GAAG,MAAM,CAAC;QAYrB,mBAAc,GAAG,KAAK,CAAC;QAEjC,oBAAe,GAAyC,IAAI,CAAC;QA4B7D,iBAAY,GAAG,CAAC,KAAY,EAAE,EAAE;YAC9B,KAAK,CAAC,eAAe,EAAE,CAAC;YACxB,MAAM,IAAI,GAAI,KAAK,CAAC,MAA2B,CAAC,KAAK,IAAI,EAAE,CAAC;YAC5D,IAAI,IAAI,CAAC,eAAe;gBAAE,YAAY,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YAE7D,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC;gBACvB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACjB,OAAO;YACT,CAAC;YAED,IAAI,CAAC,eAAe,GAAG,UAAU,CAAC,GAAG,EAAE;gBACrC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;gBAC5B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpB,CAAC,CAAC;QAEF,mBAAc,GAAG,CAAC,KAAoB,EAAE,EAAE;YAExC,KAAK,CAAC,eAAe,EAAE,CAAC;YACxB,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;gBAC/C,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC;gBACtB,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;oBACzB,YAAY,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;oBACnC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;gBAC9B,CAAC;gBACD,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACjB,CAAC;QACH,CAAC,CAAC;QAEF,sBAAiB,GAAG,CAAC,KAAa,EAAE,EAAE;YACpC,KAAK,EAAE,eAAe,EAAE,CAAC;YACzB,IAAI,CAAC,cAAc,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC;QAC7C,CAAC,CAAC;QAWF,0BAAqB,GAAG,CAAC,KAAmB,EAAE,EAAE;YAC9C,IAAI,CAAC,IAAI,CAAC,cAAc;gBAAE,OAAO;YACjC,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC;YAClC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC;gBAAE,OAAO;YAClF,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC1B,CAAC,CAAC;QAEF,uBAAkB,GAAG,CAAC,KAAoB,EAAE,EAAE;YAC5C,IAAI,CAAC,IAAI,CAAC,cAAc;gBAAE,OAAO;YACjC,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;gBAC3B,KAAK,CAAC,eAAe,EAAE,CAAC;gBACxB,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC,CAAC;QAEF,kBAAa,GAAG,CAAC,QAAgB,EAAE,EAAE;YACnC,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACxB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC;YAC9B,IAAI,CAAC,IAAI;gBAAE,OAAO;YAClB,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;QAC7D,CAAC,CAAC;QAGF,0BAAqB,GAAG,CAAC,KAAoB,EAAE,EAAE;YAC/C,IAAI,KAAK,CAAC,GAAG,KAAK,WAAW,IAAI,KAAK,CAAC,GAAG,KAAK,MAAM,EAAE,CAAC;gBACtD,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,IAAI,CAAC,IAAI,CAAC,cAAc;oBAAE,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;gBACrD,qBAAqB,CAAC,GAAG,EAAE;oBACzB,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,EAAE,aAAa,CAAc,mBAAmB,CAAC,CAAC;oBACjF,KAAK,EAAE,KAAK,EAAE,CAAC;gBACjB,CAAC,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC;QAEF,uBAAkB,GAAG,CAAC,KAAoB,EAAE,EAAE;YAC5C,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CACtB,IAAI,CAAC,YAAY,EAAE,gBAAgB,CAAc,mBAAmB,CAAC,IAAI,EAAE,CAC5E,CAAC;YAGF,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE,WAAW,EAAuC,CAAC;YACnF,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,aAA4B,CAAC,CAAC;YAClE,IAAI,KAAK,CAAC,GAAG,KAAK,WAAW,IAAI,KAAK,CAAC,GAAG,KAAK,MAAM,EAAE,CAAC;gBACtD,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,KAAK,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;YAC/C,CAAC;iBAAM,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,IAAI,KAAK,CAAC,GAAG,KAAK,IAAI,EAAE,CAAC;gBACzD,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,KAAK,CAAC,CAAC,OAAO,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;YAC9D,CAAC;iBAAM,IAAI,KAAK,CAAC,GAAG,KAAK,MAAM,EAAE,CAAC;gBAChC,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;YACpB,CAAC;iBAAM,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;gBAC/B,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;YACnC,CAAC;iBAAM,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;gBAE/B,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,CAAC;QACH,CAAC,CAAC;IAqFJ,CAAC;IA1RQ,MAAM,KAAK,OAAO;QACvB,OAAO,gBAAgB,CAAC;IAC1B,CAAC;aAEsB,WAAM,GAAG,MAAM,AAAT,CAAU;IAKhC,MAAM,CAAC,QAAQ;QACpB,iBAAiB,CAAC,eAAe,CAAC,CAAC;IACrC,CAAC;IAsBD,IAAc,iBAAiB;QAC7B,OAAO,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,2BAA2B,CAAC,IAAI,SAAS,CAAC;IAC5F,CAAC;IAgCD,eAAe,CAA8C;IAE7C,iBAAiB;QAC/B,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAC1B,QAAQ,CAAC,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC;QAC3E,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;IACtE,CAAC;IAEe,oBAAoB;QAClC,QAAQ,CAAC,mBAAmB,CAAC,aAAa,EAAE,IAAI,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC;QAC9E,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;QACvE,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,YAAY,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YACnC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC9B,CAAC;QACD,KAAK,CAAC,oBAAoB,EAAE,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,KAAa;QACjB,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAS,mBAAmB,EAAE;YAC3C,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,IAAI;SACf,CAAC,CACH,CAAC;IACJ,CAAC;IAED,YAAY,CAcV;IAEF,cAAc,CAYZ;IAEF,iBAAiB,CAGf;IAEF,gBAAgB,CAAC,WAAW,GAAG,KAAK;QAClC,IAAI,CAAC,IAAI,CAAC,cAAc;YAAE,OAAO;QACjC,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC;QAC5B,IAAI,WAAW,EAAE,CAAC;YAEhB,qBAAqB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAED,qBAAqB,CAKnB;IAEF,kBAAkB,CAMhB;IAEF,aAAa,CAKX;IAGF,qBAAqB,CASnB;IAEF,kBAAkB,CAwBhB;IAEQ,iBAAiB;QACzB,IAAI,CAAC,IAAI,CAAC,eAAe;YAAE,OAAO,OAAO,CAAC;QAC1C,OAAO,IAAI,CAAA;;UAEL,UAAU,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;;;;;uBAKhC,IAAI,CAAC,iBAAiB;wBACrB,IAAI,CAAC,iBAAiB;mBAC3B,IAAI,CAAC,KAAK;mBACV,IAAI,CAAC,YAAY;qBACf,IAAI,CAAC,cAAc;;;WAG7B,CAAC;IACV,CAAC;IAES,gBAAgB;QACxB,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAO,OAAO,CAAC;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,CAAC;QACjC,OAAO,IAAI,CAAA;;;;qBAIM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,gBAAgB,CAAC;;wBAElC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;iBAC9B,IAAI,CAAC,iBAAiB;mBACpB,IAAI,CAAC,qBAAqB;;UAEnC,UAAU,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;UAC/C,UAAU,CAAC,cAAc,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;;;;;qBAKzC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,uBAAuB,CAAC;kBAC/C,CAAC,IAAI;mBACJ,IAAI,CAAC,kBAAkB;;UAEhC,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,aAAa,IAAI,EAAE,CAAC,CAAC,GAAG,CAC3C,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAA;;;;;;uBAMH,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;;gBAE1C,MAAM,CAAC,KAAK;;gBAEZ,CACP;;WAEE,CAAC;IACV,CAAC;IAES,oBAAoB;QAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,cAAc,IAAI,EAAE,CAAC;QACvD,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO,OAAO,CAAC;QACpC,OAAO,IAAI,CAAA,GAAG,OAAO,CAAC,GAAG,CACvB,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAA;;;yBAGG,MAAM,CAAC,EAAE,IAAI,EAAE;iBACvB,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE;;UAEzB,MAAM,CAAC,KAAK;gBACN,CACX,EAAE,CAAC;IACN,CAAC;IAEkB,MAAM;QACvB,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAO,OAAO,CAAC;QAChC,OAAO,IAAI,CAAA;sDACuC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,eAAe,CAAC;UAChF,IAAI,CAAC,iBAAiB,EAAE;sCACI,IAAI,CAAC,oBAAoB,EAAE,GAAG,IAAI,CAAC,gBAAgB,EAAE;;KAEtF,CAAC;IACJ,CAAC;;AA1QM;IAFN,OAAO,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IACvD,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;8CACG;AAM3B;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;8CACT;AAUX;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;oDACH;AAUjB;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;iDAC5B;AAIf;IADN,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;wDAChB;AAIvB;IADN,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;mDACpB;AAInB;IADN,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;uDACd;AAGrB;IADT,KAAK,CAAC,OAAO,CAAC;8CACoB;AAGzB;IADT,KAAK,CAAC,yBAAyB,CAAC;sDACW;AAGlC;IADT,KAAK,CAAC,sBAAsB,CAAC;qDACO;AAG3B;IADT,KAAK,EAAE;uDACyB","sourcesContent":["import { consume } from '@lit/context';\nimport { html, LitElement, nothing } from 'lit';\nimport { property, query, state } from 'lit/decorators.js';\nimport { gridStateContext, type StateController } from '../controllers/state.js';\nimport { renderIcon } from '../internal/icons.js';\nimport { registerComponent } from '../internal/register.js';\nimport { GRID_TOOLBAR_TAG } from '../internal/tags.js';\nimport { styles } from '../styles/toolbar/toolbar.css.js';\n\n/**\n * Toolbar shown above the grid's header row. Hosts opt-in toolbar features\n * such as the quick-filter (global search) input and the export menu.\n *\n * @element apex-grid-toolbar\n *\n * @csspart toolbar - The toolbar's container element.\n * @csspart toolbar-search - The quick-filter input wrapper.\n * @csspart search-field - The bordered field containing the icon + input.\n * @csspart search-icon - The leading search icon.\n * @csspart search-input - The text input itself.\n * @csspart toolbar-actions - The trailing actions area (right side).\n * @csspart toolbar-action - A custom host-contributed action button (e.g. \"Create chart\").\n * @csspart export-trigger - The export menu trigger button.\n * @csspart export-menu - The dropdown menu containing export options.\n * @csspart export-menu-item - A single menu item inside the export dropdown.\n */\nexport default class ApexGridToolbar<T extends object> extends LitElement {\n public static get tagName() {\n return GRID_TOOLBAR_TAG;\n }\n\n public static override styles = styles;\n\n /**\n * Registers the `<apex-grid-toolbar>` element. Idempotent.\n */\n public static register() {\n registerComponent(ApexGridToolbar);\n }\n\n @consume({ context: gridStateContext, subscribe: true })\n @property({ attribute: false })\n public state!: StateController<T>;\n\n /**\n * The current quick-filter value, mirrored from {@link ApexGrid.quickFilter}.\n */\n @property({ type: String })\n public value = '';\n\n /**\n * The placeholder rendered in the search input.\n *\n * @remarks\n * When unset, falls back to the grid's localized `toolbar.searchPlaceholder`\n * text (see {@link ApexGrid.localeText}).\n */\n @property({ type: String })\n public placeholder = '';\n\n protected get searchPlaceholder() {\n return this.placeholder || this.state?.localize('toolbar.searchPlaceholder') || 'Search…';\n }\n\n /**\n * Debounce window (ms) before {@link ApexGrid.quickFilter} updates.\n */\n @property({ type: Number, attribute: 'debounce' })\n public debounce = 200;\n\n /** Whether to render the quick-filter input. Driven by the parent grid. */\n @property({ attribute: false, type: Boolean })\n public showQuickFilter = true;\n\n /** Whether to render the export menu trigger. Driven by the parent grid. */\n @property({ attribute: false, type: Boolean })\n public showExport = false;\n\n /** Optional override filename (without extension) for exports. */\n @property({ attribute: false, type: String })\n public exportFilename = 'data';\n\n @query('input')\n protected input!: HTMLInputElement;\n\n @query('[part=\"export-trigger\"]')\n protected exportTrigger!: HTMLButtonElement;\n\n @query('[part=\"export-menu\"]')\n protected exportMenuEl!: HTMLElement;\n\n @state()\n protected exportMenuOpen = false;\n\n #debounceHandle: ReturnType<typeof setTimeout> | null = null;\n\n public override connectedCallback(): void {\n super.connectedCallback();\n document.addEventListener('pointerdown', this.#handleOutsidePointer, true);\n document.addEventListener('keydown', this.#handleDocumentKey, true);\n }\n\n public override disconnectedCallback(): void {\n document.removeEventListener('pointerdown', this.#handleOutsidePointer, true);\n document.removeEventListener('keydown', this.#handleDocumentKey, true);\n if (this.#debounceHandle) {\n clearTimeout(this.#debounceHandle);\n this.#debounceHandle = null;\n }\n super.disconnectedCallback();\n }\n\n #emit(value: string) {\n this.dispatchEvent(\n new CustomEvent<string>('apex-quick-filter', {\n detail: value,\n bubbles: true,\n composed: true,\n })\n );\n }\n\n #handleInput = (event: Event) => {\n event.stopPropagation();\n const next = (event.target as HTMLInputElement).value ?? '';\n if (this.#debounceHandle) clearTimeout(this.#debounceHandle);\n\n if (this.debounce <= 0) {\n this.#emit(next);\n return;\n }\n\n this.#debounceHandle = setTimeout(() => {\n this.#debounceHandle = null;\n this.#emit(next);\n }, this.debounce);\n };\n\n #handleKeydown = (event: KeyboardEvent) => {\n // Don't let arrow keys bubble into the grid navigation controller.\n event.stopPropagation();\n if (event.key === 'Escape' && this.input.value) {\n event.preventDefault();\n this.input.value = '';\n if (this.#debounceHandle) {\n clearTimeout(this.#debounceHandle);\n this.#debounceHandle = null;\n }\n this.#emit('');\n }\n };\n\n #toggleExportMenu = (event?: Event) => {\n event?.stopPropagation();\n this.exportMenuOpen = !this.exportMenuOpen;\n };\n\n #closeExportMenu(returnFocus = false) {\n if (!this.exportMenuOpen) return;\n this.exportMenuOpen = false;\n if (returnFocus) {\n // Return focus to the trigger so keyboard users don't lose their place.\n requestAnimationFrame(() => this.exportTrigger?.focus());\n }\n }\n\n #handleOutsidePointer = (event: PointerEvent) => {\n if (!this.exportMenuOpen) return;\n const path = event.composedPath();\n if (path.includes(this.exportTrigger) || path.includes(this.exportMenuEl)) return;\n this.#closeExportMenu();\n };\n\n #handleDocumentKey = (event: KeyboardEvent) => {\n if (!this.exportMenuOpen) return;\n if (event.key === 'Escape') {\n event.stopPropagation();\n this.#closeExportMenu(true);\n }\n };\n\n #handleExport = (formatId: string) => {\n this.#closeExportMenu();\n const grid = this.state?.host;\n if (!grid) return;\n grid.exportAs(formatId, { filename: this.exportFilename });\n };\n\n /** Lets keyboard users land in the menu when it opens via arrow-down. */\n #handleTriggerKeydown = (event: KeyboardEvent) => {\n if (event.key === 'ArrowDown' || event.key === 'Down') {\n event.preventDefault();\n if (!this.exportMenuOpen) this.exportMenuOpen = true;\n requestAnimationFrame(() => {\n const first = this.exportMenuEl?.querySelector<HTMLElement>('[role=\"menuitem\"]');\n first?.focus();\n });\n }\n };\n\n #handleMenuKeydown = (event: KeyboardEvent) => {\n const items = Array.from(\n this.exportMenuEl?.querySelectorAll<HTMLElement>('[role=\"menuitem\"]') ?? []\n );\n // The menu lives in this shadow root, so `document.activeElement` is only\n // the outermost host; ask the menu's own root for the focused item.\n const root = this.exportMenuEl?.getRootNode() as Document | ShadowRoot | undefined;\n const current = items.indexOf(root?.activeElement as HTMLElement);\n if (event.key === 'ArrowDown' || event.key === 'Down') {\n event.preventDefault();\n items[(current + 1) % items.length]?.focus();\n } else if (event.key === 'ArrowUp' || event.key === 'Up') {\n event.preventDefault();\n items[(current - 1 + items.length) % items.length]?.focus();\n } else if (event.key === 'Home') {\n event.preventDefault();\n items[0]?.focus();\n } else if (event.key === 'End') {\n event.preventDefault();\n items[items.length - 1]?.focus();\n } else if (event.key === 'Tab') {\n // Tab closes the menu so focus moves naturally to the next focusable.\n this.#closeExportMenu();\n }\n };\n\n protected renderQuickFilter() {\n if (!this.showQuickFilter) return nothing;\n return html`<div part=\"toolbar-search\">\n <label part=\"search-field\">\n ${renderIcon('search', { part: 'search-icon' })}\n <input\n part=\"search-input\"\n type=\"search\"\n role=\"searchbox\"\n aria-label=${this.searchPlaceholder}\n placeholder=${this.searchPlaceholder}\n .value=${this.value}\n @input=${this.#handleInput}\n @keydown=${this.#handleKeydown}\n />\n </label>\n </div>`;\n }\n\n protected renderExportMenu() {\n if (!this.showExport) return nothing;\n const open = this.exportMenuOpen;\n return html`<div part=\"export\">\n <button\n type=\"button\"\n part=\"export-trigger\"\n aria-label=${this.state.localize('toolbar.export')}\n aria-haspopup=\"menu\"\n aria-expanded=${open ? 'true' : 'false'}\n @click=${this.#toggleExportMenu}\n @keydown=${this.#handleTriggerKeydown}\n >\n ${renderIcon('download', { part: 'export-icon' })}\n ${renderIcon('chevron-down', { part: 'export-caret' })}\n </button>\n <ul\n part=\"export-menu\"\n role=\"menu\"\n aria-label=${this.state.localize('toolbar.exportOptions')}\n ?hidden=${!open}\n @keydown=${this.#handleMenuKeydown}\n >\n ${(this.state?.host?.exportFormats ?? []).map(\n (format) => html`<li role=\"none\">\n <button\n type=\"button\"\n role=\"menuitem\"\n part=\"export-menu-item\"\n tabindex=\"-1\"\n @click=${() => this.#handleExport(format.id)}\n >\n ${format.label}\n </button>\n </li>`\n )}\n </ul>\n </div>`;\n }\n\n protected renderToolbarActions() {\n const actions = this.state?.host?.toolbarActions ?? [];\n if (!actions.length) return nothing;\n return html`${actions.map(\n (action) => html`<button\n type=\"button\"\n part=\"toolbar-action\"\n data-action-id=${action.id ?? ''}\n @click=${() => action.run()}\n >\n ${action.label}\n </button>`\n )}`;\n }\n\n protected override render() {\n if (!this.state) return nothing;\n return html`\n <div part=\"toolbar\" role=\"toolbar\" aria-label=${this.state.localize('toolbar.label')}>\n ${this.renderQuickFilter()}\n <div part=\"toolbar-actions\">${this.renderToolbarActions()}${this.renderExportMenu()}</div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n [ApexGridToolbar.tagName]: ApexGridToolbar<object>;\n }\n}\n"]}
@@ -1,9 +1,53 @@
1
1
  import { LitVirtualizer } from '@lit-labs/virtualizer/LitVirtualizer.js';
2
+ import type { PropertyValues } from 'lit';
3
+ /** Space, in px, that sticky chrome covers on each side of the scrollport. */
4
+ export interface ScrollInsets {
5
+ top: number;
6
+ right: number;
7
+ bottom: number;
8
+ left: number;
9
+ }
10
+ /** `scrollIntoView` options plus the sticky chrome to keep the target clear of. */
11
+ export type RevealOptions = ScrollIntoViewOptions & {
12
+ insets?: ScrollInsets;
13
+ };
14
+ /**
15
+ * Scrolls `el` into view as if `scroller` had `insets` as its scroll-padding.
16
+ * The padding is set only for the duration of the call. Left in place, the
17
+ * browser would apply it to every later scroll into view, including focus
18
+ * moving onto the sticky chrome itself (a header button, the paginator),
19
+ * which sits inside the padding and would drag the body along.
20
+ */
21
+ export declare function revealWithin(scroller: HTMLElement | null, el: Element, { insets, ...options }: RevealOptions): void;
2
22
  export default class ApexVirtualizer extends LitVirtualizer {
23
+ #private;
24
+ constructor();
25
+ /**
26
+ * Resolves once the layout has settled, or after a bounded wait when no
27
+ * layout is pending (upstream `layoutComplete` only settles on a layout).
28
+ */
29
+ settled(): Promise<void>;
30
+ /** Whether the item at `index` is in the rendered range. */
31
+ isRendered(index: number): boolean;
32
+ /**
33
+ * Scroll handle for item `index`, rendered or not.
34
+ *
35
+ * Upstream scrolls an unrendered item into view by pinning the layout to it,
36
+ * and that pin path assumes the window is the scroller: with the grid host
37
+ * scrolling (`scroller = false`) it overshoots every item once the host has
38
+ * scrolled, leaving the body empty at the maximum scroll offset. An
39
+ * unrendered item is instead reached with a real scroll of the host, to its
40
+ * estimated offset, which the layout then follows like any user scroll.
41
+ */
42
+ element(index: number): {
43
+ scrollIntoView: (options?: RevealOptions) => void;
44
+ } | undefined;
3
45
  static get tagName(): "apex-virtualizer";
4
46
  static register(): void;
5
47
  scroller: boolean;
6
- connectedCallback(): Promise<void>;
48
+ connectedCallback(): void;
49
+ disconnectedCallback(): void;
50
+ protected updated(changed: PropertyValues): void;
7
51
  }
8
52
  declare global {
9
53
  interface HTMLElementTagNameMap {
@@ -1,10 +1,131 @@
1
1
  import { LitVirtualizer } from '@lit-labs/virtualizer/LitVirtualizer.js';
2
2
  import { registerComponent } from '../internal/register.js';
3
3
  import { GRID_BODY } from '../internal/tags.js';
4
+ const SIZER_ATTRIBUTE = 'virtualizer-sizer';
5
+ const MAX_RECOVERY_ATTEMPTS = 3;
6
+ const SETTLE_TIMEOUT_MS = 250;
7
+ export function revealWithin(scroller, el, { insets, ...options }) {
8
+ const style = insets && scroller?.style;
9
+ const previous = style ? style.scrollPadding : '';
10
+ if (style) {
11
+ const { top, right, bottom, left } = insets;
12
+ style.scrollPadding = `${top}px ${right}px ${bottom}px ${left}px`;
13
+ }
14
+ el.scrollIntoView(options);
15
+ if (style)
16
+ style.scrollPadding = previous;
17
+ }
4
18
  export default class ApexVirtualizer extends LitVirtualizer {
19
+ #viewportObserver;
20
+ #visibleHeight;
21
+ #pendingCheck;
22
+ #recoveryAttempts;
23
+ #first;
24
+ #last;
5
25
  constructor() {
6
- super(...arguments);
26
+ super();
27
+ this.#viewportObserver = null;
28
+ this.#visibleHeight = 0;
29
+ this.#pendingCheck = 0;
30
+ this.#recoveryAttempts = 0;
31
+ this.#first = -1;
32
+ this.#last = -1;
7
33
  this.scroller = false;
34
+ this.addEventListener('rangeChanged', (event) => {
35
+ const { first, last } = event;
36
+ this.#first = first;
37
+ this.#last = last;
38
+ });
39
+ }
40
+ settled() {
41
+ const layout = this.layoutComplete;
42
+ const timeout = new Promise((resolve) => setTimeout(resolve, SETTLE_TIMEOUT_MS));
43
+ return layout ? Promise.race([layout.catch(() => undefined), timeout]) : timeout;
44
+ }
45
+ isRendered(index) {
46
+ return index >= 0 && index >= this.#first && index <= this.#last;
47
+ }
48
+ element(index) {
49
+ const target = index === Number.POSITIVE_INFINITY ? this.items.length - 1 : index;
50
+ if (!super.element(target))
51
+ return undefined;
52
+ return {
53
+ scrollIntoView: (options = {}) => {
54
+ const row = this.#renderedChild(target);
55
+ if (row)
56
+ revealWithin(this.#scroller(), row, options);
57
+ else
58
+ this.#scrollToUnrendered(target, options);
59
+ },
60
+ };
61
+ }
62
+ #renderedChild(index) {
63
+ if (!this.isRendered(index))
64
+ return null;
65
+ for (let child = this.firstElementChild; child; child = child.nextElementSibling) {
66
+ if (child.index === index)
67
+ return child;
68
+ }
69
+ return null;
70
+ }
71
+ #scroller() {
72
+ let node = this.parentNode;
73
+ while (node) {
74
+ if (node instanceof ShadowRoot) {
75
+ node = node.host;
76
+ }
77
+ else {
78
+ if (node instanceof HTMLElement && getComputedStyle(node).overflowY !== 'visible') {
79
+ return node;
80
+ }
81
+ node = node.parentNode;
82
+ }
83
+ }
84
+ return null;
85
+ }
86
+ #scrollToUnrendered(index, options) {
87
+ const scroller = this.#scroller();
88
+ const count = this.items.length;
89
+ if (!scroller || !count)
90
+ return;
91
+ const size = this.offsetHeight / count;
92
+ if (!size)
93
+ return;
94
+ const padStart = options.insets?.top ?? 0;
95
+ const padEnd = options.insets?.bottom ?? 0;
96
+ const viewport = scroller.clientHeight - padStart - padEnd;
97
+ const origin = this.getBoundingClientRect().top -
98
+ scroller.getBoundingClientRect().top -
99
+ scroller.clientTop +
100
+ scroller.scrollTop;
101
+ const start = origin + index * size;
102
+ const end = start + size;
103
+ const current = scroller.scrollTop;
104
+ let top;
105
+ switch (options.block ?? 'start') {
106
+ case 'start':
107
+ top = start - padStart;
108
+ break;
109
+ case 'end':
110
+ top = end - padStart - viewport;
111
+ break;
112
+ case 'center':
113
+ top = (start + end) / 2 - padStart - viewport / 2;
114
+ break;
115
+ default:
116
+ if (start < current + padStart)
117
+ top = start - padStart;
118
+ else if (end > current + padStart + viewport)
119
+ top = end - padStart - viewport;
120
+ else
121
+ return;
122
+ }
123
+ scroller.scrollTo({ top, behavior: options.behavior });
124
+ void this.settled().then(() => {
125
+ const row = this.#renderedChild(index);
126
+ if (row)
127
+ revealWithin(scroller, row, options);
128
+ });
8
129
  }
9
130
  static get tagName() {
10
131
  return GRID_BODY;
@@ -12,10 +133,70 @@ export default class ApexVirtualizer extends LitVirtualizer {
12
133
  static register() {
13
134
  registerComponent(ApexVirtualizer);
14
135
  }
15
- async connectedCallback() {
16
- await super.layoutComplete;
136
+ connectedCallback() {
17
137
  super.connectedCallback();
18
138
  this.setAttribute('tabindex', '0');
139
+ this.#observeViewport();
140
+ }
141
+ disconnectedCallback() {
142
+ this.#viewportObserver?.disconnect();
143
+ this.#viewportObserver = null;
144
+ this.#visibleHeight = 0;
145
+ if (this.#pendingCheck) {
146
+ cancelAnimationFrame(this.#pendingCheck);
147
+ this.#pendingCheck = 0;
148
+ }
149
+ super.disconnectedCallback();
150
+ }
151
+ updated(changed) {
152
+ super.updated(changed);
153
+ this.#scheduleViewportCheck();
154
+ }
155
+ #observeViewport() {
156
+ if (this.#viewportObserver || typeof IntersectionObserver === 'undefined')
157
+ return;
158
+ this.#viewportObserver = new IntersectionObserver((entries) => {
159
+ this.#visibleHeight = entries.at(-1)?.intersectionRect.height ?? 0;
160
+ this.#recoveryAttempts = 0;
161
+ this.#scheduleViewportCheck();
162
+ });
163
+ this.#viewportObserver.observe(this);
164
+ }
165
+ #scheduleViewportCheck() {
166
+ if (this.#pendingCheck || !this.isConnected)
167
+ return;
168
+ if (!this.items?.length || this.#hasRenderedRows())
169
+ return;
170
+ this.#pendingCheck = requestAnimationFrame(() => {
171
+ this.#pendingCheck = requestAnimationFrame(() => {
172
+ this.#pendingCheck = 0;
173
+ this.#recoverIfStale();
174
+ });
175
+ });
176
+ }
177
+ #recoverIfStale() {
178
+ if (!this.isConnected)
179
+ return;
180
+ const items = this.items;
181
+ if (!Array.isArray(items) || items.length === 0)
182
+ return;
183
+ if (this.#hasRenderedRows()) {
184
+ this.#recoveryAttempts = 0;
185
+ return;
186
+ }
187
+ if (this.#visibleHeight <= 0)
188
+ return;
189
+ if (this.#recoveryAttempts >= MAX_RECOVERY_ATTEMPTS)
190
+ return;
191
+ this.#recoveryAttempts++;
192
+ this.items = items.slice();
193
+ }
194
+ #hasRenderedRows() {
195
+ for (let child = this.firstElementChild; child; child = child.nextElementSibling) {
196
+ if (!child.hasAttribute(SIZER_ATTRIBUTE))
197
+ return true;
198
+ }
199
+ return false;
19
200
  }
20
201
  }
21
202
  //# sourceMappingURL=virtualizer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"virtualizer.js","sourceRoot":"","sources":["../../src/components/virtualizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,cAAc;IAA3D;;QAckB,aAAQ,GAAG,KAAK,CAAC;IAQnC,CAAC;IArBQ,MAAM,KAAK,OAAO;QACvB,OAAO,SAAS,CAAC;IACnB,CAAC;IAEM,MAAM,CAAC,QAAQ;QACpB,iBAAiB,CAAC,eAAe,CAAC,CAAC;IACrC,CAAC;IASe,KAAK,CAAC,iBAAiB;QACrC,MAAM,KAAK,CAAC,cAAc,CAAC;QAE3B,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAC1B,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IACrC,CAAC;CACF","sourcesContent":["import { LitVirtualizer } from '@lit-labs/virtualizer/LitVirtualizer.js';\nimport { registerComponent } from '../internal/register.js';\nimport { GRID_BODY } from '../internal/tags.js';\n\nexport default class ApexVirtualizer extends LitVirtualizer {\n public static get tagName() {\n return GRID_BODY;\n }\n\n public static register(): void {\n registerComponent(ApexVirtualizer);\n }\n\n // The virtualizer used to own vertical scrolling, which made it a scroll container\n // and broke `position: sticky` for body cells in pinned columns (cells would anchor\n // to the virtualizer instead of the horizontally-scrolling grid host). With\n // `scroller = false` the grid host owns both axes, so headers and pinned body cells\n // share a single sticky containing block.\n public override scroller = false;\n\n public override async connectedCallback() {\n await super.layoutComplete;\n\n super.connectedCallback();\n this.setAttribute('tabindex', '0');\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n [ApexVirtualizer.tagName]: ApexVirtualizer;\n }\n}\n"]}
1
+ {"version":3,"file":"virtualizer.js","sourceRoot":"","sources":["../../src/components/virtualizer.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitVirtualizer } from '@lit-labs/virtualizer/LitVirtualizer.js';\nimport type { PropertyValues } from 'lit';\nimport { registerComponent } from '../internal/register.js';\nimport { GRID_BODY } from '../internal/tags.js';\n\n/**\n * Attribute the upstream virtualizer stamps on the spacer element it appends in\n * scroller mode. It is not a rendered row, so it is skipped when asking whether\n * the body rendered anything.\n */\nconst SIZER_ATTRIBUTE = 'virtualizer-sizer';\n\n/**\n * Re-measure attempts allowed per geometry change. A grid that still renders\n * nothing after this many tries has a cause we cannot fix by re-measuring, so\n * we stop rather than spin.\n */\nconst MAX_RECOVERY_ATTEMPTS = 3;\n\n/** Space, in px, that sticky chrome covers on each side of the scrollport. */\nexport interface ScrollInsets {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\n/** `scrollIntoView` options plus the sticky chrome to keep the target clear of. */\nexport type RevealOptions = ScrollIntoViewOptions & { insets?: ScrollInsets };\n\n/** Longest wait for the layout to settle after a scroll to an unrendered row. */\nconst SETTLE_TIMEOUT_MS = 250;\n\n/**\n * Scrolls `el` into view as if `scroller` had `insets` as its scroll-padding.\n * The padding is set only for the duration of the call. Left in place, the\n * browser would apply it to every later scroll into view, including focus\n * moving onto the sticky chrome itself (a header button, the paginator),\n * which sits inside the padding and would drag the body along.\n */\nexport function revealWithin(\n scroller: HTMLElement | null,\n el: Element,\n { insets, ...options }: RevealOptions\n): void {\n const style = insets && scroller?.style;\n const previous = style ? style.scrollPadding : '';\n if (style) {\n const { top, right, bottom, left } = insets;\n style.scrollPadding = `${top}px ${right}px ${bottom}px ${left}px`;\n }\n el.scrollIntoView(options);\n if (style) style.scrollPadding = previous;\n}\n\nexport default class ApexVirtualizer extends LitVirtualizer {\n /**\n * Watches the host's intersection with the window, which is the one input to\n * the upstream viewport measurement that has no invalidation signal of its\n * own. See the note on `#observeViewport`.\n */\n #viewportObserver: IntersectionObserver | null = null;\n\n /**\n * Height of the host's last observed intersection with the window: the\n * viewport height the layout should be working from. Zero means an empty\n * range is the *correct* render and there is nothing to recover.\n */\n #visibleHeight = 0;\n\n #pendingCheck = 0;\n\n #recoveryAttempts = 0;\n\n /** Item indexes currently rendered, as last reported by `rangeChanged`. */\n #first = -1;\n #last = -1;\n\n constructor() {\n super();\n // Registered before the upstream directive's own listener (added on first\n // render), so the range is current before the rows are re-rendered.\n this.addEventListener('rangeChanged', (event) => {\n const { first, last } = event as Event & { first: number; last: number };\n this.#first = first;\n this.#last = last;\n });\n }\n\n /**\n * Resolves once the layout has settled, or after a bounded wait when no\n * layout is pending (upstream `layoutComplete` only settles on a layout).\n */\n public settled(): Promise<void> {\n const layout = this.layoutComplete;\n const timeout = new Promise<void>((resolve) => setTimeout(resolve, SETTLE_TIMEOUT_MS));\n return layout ? Promise.race([layout.catch(() => undefined), timeout]) : timeout;\n }\n\n /** Whether the item at `index` is in the rendered range. */\n public isRendered(index: number): boolean {\n return index >= 0 && index >= this.#first && index <= this.#last;\n }\n\n /**\n * Scroll handle for item `index`, rendered or not.\n *\n * Upstream scrolls an unrendered item into view by pinning the layout to it,\n * and that pin path assumes the window is the scroller: with the grid host\n * scrolling (`scroller = false`) it overshoots every item once the host has\n * scrolled, leaving the body empty at the maximum scroll offset. An\n * unrendered item is instead reached with a real scroll of the host, to its\n * estimated offset, which the layout then follows like any user scroll.\n */\n public override element(\n index: number\n ): { scrollIntoView: (options?: RevealOptions) => void } | undefined {\n const target = index === Number.POSITIVE_INFINITY ? this.items.length - 1 : index;\n if (!super.element(target)) return undefined;\n return {\n scrollIntoView: (options: RevealOptions = {}) => {\n const row = this.#renderedChild(target);\n if (row) revealWithin(this.#scroller(), row, options);\n else this.#scrollToUnrendered(target, options);\n },\n };\n }\n\n #renderedChild(index: number): HTMLElement | null {\n if (!this.isRendered(index)) return null;\n for (let child = this.firstElementChild; child; child = child.nextElementSibling) {\n if ((child as Element & { index?: number }).index === index) return child as HTMLElement;\n }\n return null;\n }\n\n /** The nearest ancestor that scrolls this element (the grid host). */\n #scroller(): HTMLElement | null {\n let node: Node | null = this.parentNode;\n while (node) {\n if (node instanceof ShadowRoot) {\n node = node.host;\n } else {\n if (node instanceof HTMLElement && getComputedStyle(node).overflowY !== 'visible') {\n return node;\n }\n node = node.parentNode;\n }\n }\n return null;\n }\n\n #scrollToUnrendered(index: number, options: RevealOptions): void {\n const scroller = this.#scroller();\n const count = this.items.length;\n if (!scroller || !count) return;\n // The upstream layout sizes this element to its estimate of the total\n // extent, so the average item size here matches its own estimates for\n // items it has not measured.\n const size = this.offsetHeight / count;\n if (!size) return;\n\n const padStart = options.insets?.top ?? 0;\n const padEnd = options.insets?.bottom ?? 0;\n const viewport = scroller.clientHeight - padStart - padEnd;\n const origin =\n this.getBoundingClientRect().top -\n scroller.getBoundingClientRect().top -\n scroller.clientTop +\n scroller.scrollTop;\n const start = origin + index * size;\n const end = start + size;\n const current = scroller.scrollTop;\n\n let top: number;\n switch (options.block ?? 'start') {\n case 'start':\n top = start - padStart;\n break;\n case 'end':\n top = end - padStart - viewport;\n break;\n case 'center':\n top = (start + end) / 2 - padStart - viewport / 2;\n break;\n default:\n if (start < current + padStart) top = start - padStart;\n else if (end > current + padStart + viewport) top = end - padStart - viewport;\n else return;\n }\n scroller.scrollTo({ top, behavior: options.behavior });\n // The offset was an estimate; once the target is rendered, align the real row.\n void this.settled().then(() => {\n const row = this.#renderedChild(index);\n if (row) revealWithin(scroller, row, options);\n });\n }\n\n public static get tagName() {\n return GRID_BODY;\n }\n\n public static register(): void {\n registerComponent(ApexVirtualizer);\n }\n\n // The virtualizer used to own vertical scrolling, which made it a scroll container\n // and broke `position: sticky` for body cells in pinned columns (cells would anchor\n // to the virtualizer instead of the horizontally-scrolling grid host). With\n // `scroller = false` the grid host owns both axes, so headers and pinned body cells\n // share a single sticky containing block.\n public override scroller = false;\n\n public override connectedCallback() {\n // Nothing may be awaited before this call. The upstream virtualizer is only\n // created (and reconnected) from the element's own Lit update cycle, which\n // `super.connectedCallback()` is what starts. Deferring it past an await on\n // any virtualizer state deadlocks on a reconnect: the promise waits for a\n // layout that waits for the update that this call would have scheduled.\n super.connectedCallback();\n this.setAttribute('tabindex', '0');\n this.#observeViewport();\n }\n\n public override disconnectedCallback() {\n this.#viewportObserver?.disconnect();\n this.#viewportObserver = null;\n // Geometry is meaningless while detached; the observer re-reports on connect.\n this.#visibleHeight = 0;\n if (this.#pendingCheck) {\n cancelAnimationFrame(this.#pendingCheck);\n this.#pendingCheck = 0;\n }\n super.disconnectedCallback();\n }\n\n protected override updated(changed: PropertyValues) {\n super.updated(changed);\n this.#scheduleViewportCheck();\n }\n\n /**\n * The upstream virtualizer derives its viewport from the intersection of the\n * host, its clipping ancestors and the window, and clears the rendered range\n * outright when that intersection is zero-height, while still sizing the\n * scroll extent from the full item count. A grid that is laid out while it\n * sits outside the window (an iframe not yet sized by its host page, a panel\n * still settling, content above it that has not finished loading) therefore\n * ends up reporting a correct row count and scroll extent with no rows in it.\n *\n * That state is not self-correcting, because the only invalidation signals\n * upstream listens for are `ResizeObserver` size changes and scroll events:\n * an element that *moves* into view, or a window that resizes around a\n * fixed-size host, fires neither. The grid then stays empty until something\n * unrelated forces a re-render, which is why the failure reads as a race and\n * heals on the first interaction.\n *\n * `IntersectionObserver` fires on exactly the missing signal, so it is what\n * arms the recovery check.\n *\n * @see https://github.com/apexcharts/apex-grid/issues/31\n */\n #observeViewport(): void {\n if (this.#viewportObserver || typeof IntersectionObserver === 'undefined') return;\n this.#viewportObserver = new IntersectionObserver((entries) => {\n this.#visibleHeight = entries.at(-1)?.intersectionRect.height ?? 0;\n // Fresh geometry, so the attempt budget starts over.\n this.#recoveryAttempts = 0;\n this.#scheduleViewportCheck();\n });\n this.#viewportObserver.observe(this);\n }\n\n #scheduleViewportCheck(): void {\n // Healthy path: one element lookup and out, no frame scheduled.\n if (this.#pendingCheck || !this.isConnected) return;\n if (!this.items?.length || this.#hasRenderedRows()) return;\n // Two frames, matching what the virtualizer itself waits before calling a\n // layout settled. Checking sooner would flag a first paint still in flight.\n this.#pendingCheck = requestAnimationFrame(() => {\n this.#pendingCheck = requestAnimationFrame(() => {\n this.#pendingCheck = 0;\n this.#recoverIfStale();\n });\n });\n }\n\n #recoverIfStale(): void {\n if (!this.isConnected) return;\n\n const items = this.items;\n if (!Array.isArray(items) || items.length === 0) return;\n\n if (this.#hasRenderedRows()) {\n this.#recoveryAttempts = 0;\n return;\n }\n\n // No visible slice means rendering nothing is correct: the grid is scrolled\n // out of view, inside a collapsed panel, or hidden. Leave it alone.\n if (this.#visibleHeight <= 0) return;\n if (this.#recoveryAttempts >= MAX_RECOVERY_ATTEMPTS) return;\n\n this.#recoveryAttempts++;\n // Re-assigning `items` with a new array identity is the public lever that\n // makes the virtualizer re-run its layout, and so re-measure the viewport.\n // Unlike a synthetic scroll event it cannot disturb a pending\n // `scrollIntoView`, which the layout holds as a pin until a real scroll.\n this.items = items.slice();\n }\n\n #hasRenderedRows(): boolean {\n for (let child = this.firstElementChild; child; child = child.nextElementSibling) {\n if (!child.hasAttribute(SIZER_ATTRIBUTE)) return true;\n }\n return false;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n [ApexVirtualizer.tagName]: ApexVirtualizer;\n }\n}\n"]}
@@ -30,9 +30,10 @@ export declare class GridDOMController<T extends object> implements ReactiveCont
30
30
  rowRenderer: RenderItemFunction<T>;
31
31
  /**
32
32
  * Renders a single pinned row for a sticky band. Reuses {@link ApexGridRow}
33
- * so selection / expansion / decoration all work by data reference, but pins a
34
- * sentinel {@link ActiveNode} (pinned rows are presentation + selection
35
- * surfaces in v1, never the active/edited cell) and renders no editor.
33
+ * so selection / expansion / decoration / editing all work by data
34
+ * reference, but pins a sentinel {@link ActiveNode} (pinned rows are
35
+ * presentation, selection and pointer-edit surfaces in v1, never the
36
+ * keyboard-active cell).
36
37
  *
37
38
  * @param localIndex - position within the band (drives the active sentinel id).
38
39
  * @param ariaRowOffset - so `aria-rowindex` continues the unified sequence.
@@ -48,7 +49,6 @@ export declare class GridDOMController<T extends object> implements ReactiveCont
48
49
  * horizontal scrolling doesn't drag them off-screen.
49
50
  */
50
51
  recomputeHostWidth(): void;
51
- setScrollOffset(): void;
52
52
  protected setGridColumnSizes(): void;
53
53
  getActiveRowStyles(index: number): StyleInfo;
54
54
  /**
@@ -1,14 +1,13 @@
1
1
  import { html } from 'lit';
2
2
  import { styleMap } from 'lit/directives/style-map.js';
3
3
  import { SENTINEL_NODE } from '../internal/constants.js';
4
- import { applyColumnWidths, getDisplayColumns } from '../internal/utils.js';
4
+ import { applyColumnWidths, getDisplayColumns, leadingChromeTracks, } from '../internal/utils.js';
5
5
  export class GridDOMController {
6
6
  constructor(host, state) {
7
7
  this.host = host;
8
8
  this.state = state;
9
9
  this.#initialSize = () => {
10
10
  setTimeout(() => {
11
- this.setScrollOffset();
12
11
  this.recomputePinOffsets();
13
12
  this.recomputeStickyRowOffsets();
14
13
  });
@@ -20,8 +19,7 @@ export class GridDOMController {
20
19
  this.columnSizes = {};
21
20
  this.pinOffsets = new Map();
22
21
  this.rowRenderer = (data, index) => {
23
- const editingCell = this.state.editing.activeCell;
24
- const editingKey = editingCell?.rowIndex === index ? editingCell.columnKey : null;
22
+ const editingKey = this.#editingKey(data);
25
23
  const ariaRowOffset = this.headerRowCount + this.state.rowPin.pinnedRows.top.length;
26
24
  return html `
27
25
  <apex-grid-row
@@ -35,6 +33,7 @@ export class GridDOMController {
35
33
  .editingKey=${editingKey}
36
34
  .ariaRowOffset=${ariaRowOffset}
37
35
  .decorationVersion=${this.state.decorationVersion}
36
+ .dataVersion=${this.state.dataVersion}
38
37
  .validationVersion=${this.state.editing.validationVersion}
39
38
  .dragging=${this.state.rowReorder.dragging === data}
40
39
  .grabbed=${this.state.rowReorder.grabbed === data}
@@ -43,6 +42,7 @@ export class GridDOMController {
43
42
  </apex-grid-row>
44
43
  `;
45
44
  };
45
+ this.#chromeWidth = 0;
46
46
  this.#lastGroupTop = -1;
47
47
  this.#lastHeaderTop = -1;
48
48
  this.#lastFilterTop = -1;
@@ -66,6 +66,10 @@ export class GridDOMController {
66
66
  get headerRowCount() {
67
67
  return this.host.columnGroupDepth + 1;
68
68
  }
69
+ #editingKey(data) {
70
+ const editingCell = this.state.editing.activeCell;
71
+ return editingCell && editingCell.data === data ? editingCell.columnKey : null;
72
+ }
69
73
  pinnedRowRenderer(data, localIndex, ariaRowOffset) {
70
74
  return html `
71
75
  <apex-grid-row
@@ -76,9 +80,10 @@ export class GridDOMController {
76
80
  .data=${data}
77
81
  .columns=${this.displayColumns}
78
82
  .pinOffsets=${this.pinOffsets}
79
- .editingKey=${null}
83
+ .editingKey=${this.#editingKey(data)}
80
84
  .ariaRowOffset=${ariaRowOffset}
81
85
  .decorationVersion=${this.state.decorationVersion}
86
+ .dataVersion=${this.state.dataVersion}
82
87
  .validationVersion=${this.state.editing.validationVersion}
83
88
  .coordinateHints=${Boolean(this.host.coordinateHints)}
84
89
  >
@@ -98,7 +103,6 @@ export class GridDOMController {
98
103
  this.recomputeHostWidth();
99
104
  }
100
105
  hostUpdate() {
101
- this.setScrollOffset();
102
106
  this.setGridColumnSizes();
103
107
  }
104
108
  hostUpdated() {
@@ -125,16 +129,28 @@ export class GridDOMController {
125
129
  host.style.setProperty('--apex-host-width', `${width}px`);
126
130
  this.#lastHostWidth = width;
127
131
  }
128
- setScrollOffset() {
129
- this.host.style.setProperty('--scrollbar-offset', '0px');
130
- }
131
132
  setGridColumnSizes() {
132
- this.columnSizes = applyColumnWidths(this.displayColumns, {
133
- showSelectionColumn: this.state.selection.showCheckboxColumn,
134
- showExpansionColumn: this.state.expansion.showToggleColumn,
135
- showReorderHandle: this.state.rowReorder.showHandleColumn,
136
- showRowNumbers: Boolean(this.host.coordinateHints),
137
- });
133
+ const chrome = leadingChromeTracks(this.state);
134
+ this.columnSizes = applyColumnWidths(this.displayColumns, chrome);
135
+ this.#publishChromeOffsets(chrome);
136
+ }
137
+ #chromeWidth;
138
+ #publishChromeOffsets(chrome) {
139
+ const offsets = {
140
+ 'row-number': 0,
141
+ reorder: 0,
142
+ selection: 0,
143
+ expansion: 0,
144
+ };
145
+ let accum = 0;
146
+ for (const [track, width] of chrome) {
147
+ offsets[track] = accum;
148
+ accum += width;
149
+ }
150
+ this.#chromeWidth = accum;
151
+ for (const [track, offset] of Object.entries(offsets)) {
152
+ this.host.style.setProperty(`--apex-chrome-offset-${track}`, `${offset}px`);
153
+ }
138
154
  }
139
155
  getActiveRowStyles(index) {
140
156
  const { row } = this.state.active;
@@ -145,19 +161,22 @@ export class GridDOMController {
145
161
  if (!headerRow?.headers)
146
162
  return;
147
163
  const next = new Map();
148
- const headersByKey = new Map();
164
+ const widths = new Map();
165
+ const host = this.host;
166
+ const visual = host.getBoundingClientRect().width;
167
+ const layout = host.offsetWidth;
168
+ const scale = visual && layout && Math.abs(visual - layout) >= 1 ? visual / layout : 1;
149
169
  for (const header of headerRow.headers) {
150
- headersByKey.set(header.column.key, header.getBoundingClientRect());
170
+ widths.set(header.column.key, header.getBoundingClientRect().width / scale);
151
171
  }
152
- let leftAccum = 0;
172
+ let leftAccum = this.#chromeWidth;
153
173
  for (const column of this.displayColumns) {
154
174
  if (column.hidden)
155
175
  continue;
156
176
  if (column.pinned !== 'start')
157
177
  break;
158
178
  next.set(column.key, leftAccum);
159
- const rect = headersByKey.get(column.key);
160
- leftAccum += rect?.width ?? 0;
179
+ leftAccum += widths.get(column.key) ?? 0;
161
180
  }
162
181
  let rightAccum = 0;
163
182
  for (let i = this.displayColumns.length - 1; i >= 0; i--) {
@@ -167,8 +186,7 @@ export class GridDOMController {
167
186
  if (column.pinned !== 'end')
168
187
  break;
169
188
  next.set(column.key, rightAccum);
170
- const rect = headersByKey.get(column.key);
171
- rightAccum += rect?.width ?? 0;
189
+ rightAccum += widths.get(column.key) ?? 0;
172
190
  }
173
191
  if (!offsetsEqual(this.pinOffsets, next)) {
174
192
  this.pinOffsets = next;