@jimka/typescript-ui 0.1.0 → 0.2.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 (216) hide show
  1. package/README.md +5 -18
  2. package/dist/lib/{AbstractBooleanInput-C3rj7sje.js → AbstractBooleanInput-BGesvIZy.js} +2 -2
  3. package/dist/lib/{AbstractBooleanInput-C3rj7sje.js.map → AbstractBooleanInput-BGesvIZy.js.map} +1 -1
  4. package/dist/lib/{AbstractInput-DD-NpicS.js → AbstractInput-BxOtFrER.js} +2 -2
  5. package/dist/lib/{AbstractInput-DD-NpicS.js.map → AbstractInput-BxOtFrER.js.map} +1 -1
  6. package/dist/lib/AnimatedDropdown-CZQEqQi-.js +2 -0
  7. package/dist/lib/AnimatedDropdown-CZQEqQi-.js.map +1 -0
  8. package/dist/lib/Border-BgaZkc1R.js +2 -0
  9. package/dist/lib/Border-BgaZkc1R.js.map +1 -0
  10. package/dist/lib/{BoxLayout-DgZukB5_.js → BoxLayout-DrKGfsup.js} +2 -2
  11. package/dist/lib/{BoxLayout-DgZukB5_.js.map → BoxLayout-DrKGfsup.js.map} +1 -1
  12. package/dist/lib/Button-U1rsP4v8.js +2 -0
  13. package/dist/lib/Button-U1rsP4v8.js.map +1 -0
  14. package/dist/lib/{Card-UF0fq8cQ.js → Card-BVwOS8AG.js} +2 -2
  15. package/dist/lib/{Card-UF0fq8cQ.js.map → Card-BVwOS8AG.js.map} +1 -1
  16. package/dist/lib/ComboBox-B9EDPu6T.js +2 -0
  17. package/dist/lib/ComboBox-B9EDPu6T.js.map +1 -0
  18. package/dist/lib/Component-DXf5a3g0.js +2 -0
  19. package/dist/lib/Component-DXf5a3g0.js.map +1 -0
  20. package/dist/lib/Container-D5H8VxJK.js +2 -0
  21. package/dist/lib/{Container-3KsvsJ6S.js.map → Container-D5H8VxJK.js.map} +1 -1
  22. package/dist/lib/DOM-CGn7xyTw.js +2 -0
  23. package/dist/lib/DOM-CGn7xyTw.js.map +1 -0
  24. package/dist/lib/DragManager-CI4mZQFy.js +2 -0
  25. package/dist/lib/{DragManager-tPMnbjeO.js.map → DragManager-CI4mZQFy.js.map} +1 -1
  26. package/dist/lib/FieldDecorator-CJs9gwqH.js +2 -0
  27. package/dist/lib/{FieldDecorator-E7WzutjO.js.map → FieldDecorator-CJs9gwqH.js.map} +1 -1
  28. package/dist/lib/{Fit-CFIr4P9R.js → Fit-DjRsiwlg.js} +2 -2
  29. package/dist/lib/{Fit-CFIr4P9R.js.map → Fit-DjRsiwlg.js.map} +1 -1
  30. package/dist/lib/Glyph-KEf4nIv2.js +2 -0
  31. package/dist/lib/Glyph-KEf4nIv2.js.map +1 -0
  32. package/dist/lib/{Grid-Czux--Mr.js → Grid-DdL9TVeJ.js} +2 -2
  33. package/dist/lib/{Grid-Czux--Mr.js.map → Grid-DdL9TVeJ.js.map} +1 -1
  34. package/dist/lib/GridConstraints-DkLueyrf.js +2 -0
  35. package/dist/lib/GridConstraints-DkLueyrf.js.map +1 -0
  36. package/dist/lib/{HBox-Q8y1PHlk.js → HBox-Ce0GTCJP.js} +2 -2
  37. package/dist/lib/{HBox-Q8y1PHlk.js.map → HBox-Ce0GTCJP.js.map} +1 -1
  38. package/dist/lib/{Header-zPdvC2AX.js → Header-BQIVpBte.js} +2 -2
  39. package/dist/lib/{Header-zPdvC2AX.js.map → Header-BQIVpBte.js.map} +1 -1
  40. package/dist/lib/{IconText-BvGfu7eq.js → IconText-Bo4Qcnot.js} +2 -2
  41. package/dist/lib/{IconText-BvGfu7eq.js.map → IconText-Bo4Qcnot.js.map} +1 -1
  42. package/dist/lib/LayoutConstraints-DkctJZXB.js +2 -0
  43. package/dist/lib/LayoutConstraints-DkctJZXB.js.map +1 -0
  44. package/dist/lib/LayoutSerialization-BT66TY83.js +2 -0
  45. package/dist/lib/LayoutSerialization-BT66TY83.js.map +1 -0
  46. package/dist/lib/LayoutSizes-DU3qSq0w.js +2 -0
  47. package/dist/lib/LayoutSizes-DU3qSq0w.js.map +1 -0
  48. package/dist/lib/List-DtTVTyWW.js +2 -0
  49. package/dist/lib/List-DtTVTyWW.js.map +1 -0
  50. package/dist/lib/Menu-Dz4L7EKJ.js +2 -0
  51. package/dist/lib/Menu-Dz4L7EKJ.js.map +1 -0
  52. package/dist/lib/MenuButton-VJ5uC3c1.js +2 -0
  53. package/dist/lib/{MenuButton-JQQaKThJ.js.map → MenuButton-VJ5uC3c1.js.map} +1 -1
  54. package/dist/lib/Panel-DzWdYwq1.js +2 -0
  55. package/dist/lib/Panel-DzWdYwq1.js.map +1 -0
  56. package/dist/lib/Position-A39T7-_1.js +2 -0
  57. package/dist/lib/Position-A39T7-_1.js.map +1 -0
  58. package/dist/lib/ProgressSpinner-zJKtGyNJ.js +2 -0
  59. package/dist/lib/ProgressSpinner-zJKtGyNJ.js.map +1 -0
  60. package/dist/lib/RadioButton-CYSSv4nV.js +2 -0
  61. package/dist/lib/RadioButton-CYSSv4nV.js.map +1 -0
  62. package/dist/lib/{RovingTabIndex-CFIzIjjU.js → RovingTabIndex-BSZ31Vp5.js} +2 -2
  63. package/dist/lib/{RovingTabIndex-CFIzIjjU.js.map → RovingTabIndex-BSZ31Vp5.js.map} +1 -1
  64. package/dist/lib/Scrollbar-BhtOjqkG.js +2 -0
  65. package/dist/lib/Scrollbar-BhtOjqkG.js.map +1 -0
  66. package/dist/lib/Slider-CdXm7E64.js +2 -0
  67. package/dist/lib/Slider-CdXm7E64.js.map +1 -0
  68. package/dist/lib/{Spacer-BVQncB5B.js → Spacer-D9NGW4QG.js} +2 -2
  69. package/dist/lib/{Spacer-BVQncB5B.js.map → Spacer-D9NGW4QG.js.map} +1 -1
  70. package/dist/lib/TabButton-CThevdY_.js +2 -0
  71. package/dist/lib/{TabButton-pZKFYg7b.js.map → TabButton-CThevdY_.js.map} +1 -1
  72. package/dist/lib/Text-gz6qjFfP.js +2 -0
  73. package/dist/lib/Text-gz6qjFfP.js.map +1 -0
  74. package/dist/lib/Tooltip-tkXDk5Bo.js +3 -0
  75. package/dist/lib/Tooltip-tkXDk5Bo.js.map +1 -0
  76. package/dist/lib/{VBox-D5B9ecw0.js → VBox-EIqw85Rh.js} +2 -2
  77. package/dist/lib/{VBox-D5B9ecw0.js.map → VBox-EIqw85Rh.js.map} +1 -1
  78. package/dist/lib/VirtualRowView-BxpHQ_u8.js +2 -0
  79. package/dist/lib/{VirtualRowView-Con9SWiU.js.map → VirtualRowView-BxpHQ_u8.js.map} +1 -1
  80. package/dist/lib/VirtualScroller-DwEEJE1_.js +2 -0
  81. package/dist/lib/{VirtualScroller-BPDmO1ir.js.map → VirtualScroller-DwEEJE1_.js.map} +1 -1
  82. package/dist/lib/component/button.es.js +1 -1
  83. package/dist/lib/component/button.es.js.map +1 -1
  84. package/dist/lib/component/chart.es.js +1 -1
  85. package/dist/lib/component/chart.es.js.map +1 -1
  86. package/dist/lib/component/container.es.js +1 -1
  87. package/dist/lib/component/container.es.js.map +1 -1
  88. package/dist/lib/component/diagram.es.js +1 -1
  89. package/dist/lib/component/diagram.es.js.map +1 -1
  90. package/dist/lib/component/display.es.js +1 -1
  91. package/dist/lib/component/display.es.js.map +1 -1
  92. package/dist/lib/component/editor.es.js +3 -1
  93. package/dist/lib/component/editor.es.js.map +1 -1
  94. package/dist/lib/component/input.es.js +1 -1
  95. package/dist/lib/component/input.es.js.map +1 -1
  96. package/dist/lib/component/list.es.js +1 -1
  97. package/dist/lib/component/list.es.js.map +1 -1
  98. package/dist/lib/component/menubar.es.js +1 -1
  99. package/dist/lib/component/menubar.es.js.map +1 -1
  100. package/dist/lib/component/table.es.js +2 -2
  101. package/dist/lib/component/table.es.js.map +1 -1
  102. package/dist/lib/component/tree.es.js +1 -1
  103. package/dist/lib/component/tree.es.js.map +1 -1
  104. package/dist/lib/core.es.js +1 -1
  105. package/dist/lib/core.es.js.map +1 -1
  106. package/dist/lib/layout.es.js +1 -1
  107. package/dist/lib/overlay.es.js +1 -1
  108. package/dist/lib/overlay.es.js.map +1 -1
  109. package/dist/lib/primitive.es.js +1 -1
  110. package/dist/lib/router.es.js +2 -0
  111. package/dist/lib/router.es.js.map +1 -0
  112. package/dist/lib/types/component/button/Button.d.ts +4 -3
  113. package/dist/lib/types/component/chart/AbstractChart.d.ts +1 -1
  114. package/dist/lib/types/component/chart/ChartLegend.d.ts +1 -1
  115. package/dist/lib/types/component/container/DialogBackdrop.d.ts +2 -1
  116. package/dist/lib/types/component/container/MenuItem.d.ts +1 -1
  117. package/dist/lib/types/component/container/ScrollStrip.d.ts +1 -0
  118. package/dist/lib/types/component/container/SplitGutter.d.ts +3 -2
  119. package/dist/lib/types/component/container/TabBar.d.ts +1 -2
  120. package/dist/lib/types/component/container/TabPanel.d.ts +6 -3
  121. package/dist/lib/types/component/container/WindowBorder.d.ts +2 -1
  122. package/dist/lib/types/component/container/WindowHeader.d.ts +2 -1
  123. package/dist/lib/types/component/display/Glyph.d.ts +2 -1
  124. package/dist/lib/types/component/display/Markdown.d.ts +11 -1
  125. package/dist/lib/types/component/display/PaginationBar.d.ts +1 -1
  126. package/dist/lib/types/component/display/SpinnerWrap.d.ts +2 -0
  127. package/dist/lib/types/component/display/Video.d.ts +1 -1
  128. package/dist/lib/types/component/display/VideoPlayer.d.ts +1 -1
  129. package/dist/lib/types/component/display/index.d.ts +1 -1
  130. package/dist/lib/types/component/editor/CodeEditor.d.ts +2 -1
  131. package/dist/lib/types/component/editor/MarkdownEditor.d.ts +7 -1
  132. package/dist/lib/types/component/editor/markdownTableTransformer.d.ts +2 -0
  133. package/dist/lib/types/component/input/Checkbox.d.ts +2 -1
  134. package/dist/lib/types/component/input/Link.d.ts +1 -1
  135. package/dist/lib/types/component/input/RadioButton.d.ts +2 -1
  136. package/dist/lib/types/component/input/Slider.d.ts +2 -1
  137. package/dist/lib/types/component/input/Text.d.ts +1 -3
  138. package/dist/lib/types/component/input/TextInput.d.ts +3 -2
  139. package/dist/lib/types/component/list/AbstractSelectableList.d.ts +2 -1
  140. package/dist/lib/types/component/menubar/MenuBarButton.d.ts +1 -1
  141. package/dist/lib/types/core/AnimatedDropdown.d.ts +6 -2
  142. package/dist/lib/types/core/Animation.d.ts +9 -6
  143. package/dist/lib/types/core/Component.d.ts +16 -11
  144. package/dist/lib/types/core/DOM.d.ts +14 -0
  145. package/dist/lib/types/core/Event.d.ts +12 -6
  146. package/dist/lib/types/core/index.d.ts +2 -2
  147. package/dist/lib/types/layout/Accordion.d.ts +2 -0
  148. package/dist/lib/types/layout/Border.d.ts +1 -0
  149. package/dist/lib/types/layout/CollapseSupport.d.ts +5 -1
  150. package/dist/lib/types/layout/LayoutConstraints.d.ts +1 -0
  151. package/dist/lib/types/layout/LayoutManager.d.ts +2 -0
  152. package/dist/lib/types/layout/Split.d.ts +1 -0
  153. package/dist/lib/types/layout/Tab.d.ts +10 -3
  154. package/dist/lib/types/overlay/AbstractWindow.d.ts +9 -2
  155. package/dist/lib/types/overlay/Dialog.d.ts +6 -0
  156. package/dist/lib/types/overlay/Dock.d.ts +12 -2
  157. package/dist/lib/types/overlay/Drawer.d.ts +5 -0
  158. package/dist/lib/types/overlay/Menu.d.ts +3 -1
  159. package/dist/lib/types/overlay/Notification.d.ts +3 -0
  160. package/dist/lib/types/overlay/Popover.d.ts +3 -1
  161. package/dist/lib/types/overlay/Rail.d.ts +7 -0
  162. package/dist/lib/types/overlay/Tooltip.d.ts +3 -0
  163. package/dist/lib/types/overlay/index.d.ts +1 -1
  164. package/dist/lib/types/router/RoutePattern.d.ts +18 -0
  165. package/dist/lib/types/router/Router.d.ts +36 -0
  166. package/dist/lib/types/router/index.d.ts +2 -0
  167. package/dist/lib/validation.es.js +1 -1
  168. package/llms.txt +9 -5
  169. package/package.json +6 -1
  170. package/dist/lib/AnimatedDropdown-BbGCs70E.js +0 -2
  171. package/dist/lib/AnimatedDropdown-BbGCs70E.js.map +0 -1
  172. package/dist/lib/Border-oZC_SDy8.js +0 -2
  173. package/dist/lib/Border-oZC_SDy8.js.map +0 -1
  174. package/dist/lib/Button-DEUtBqK4.js +0 -2
  175. package/dist/lib/Button-DEUtBqK4.js.map +0 -1
  176. package/dist/lib/ComboBox-GOBOAJ1N.js +0 -2
  177. package/dist/lib/ComboBox-GOBOAJ1N.js.map +0 -1
  178. package/dist/lib/Component-BR2f0Itb.js +0 -2
  179. package/dist/lib/Component-BR2f0Itb.js.map +0 -1
  180. package/dist/lib/Container-3KsvsJ6S.js +0 -2
  181. package/dist/lib/DragManager-tPMnbjeO.js +0 -2
  182. package/dist/lib/FieldDecorator-E7WzutjO.js +0 -2
  183. package/dist/lib/Glyph-WUqTF_Z9.js +0 -2
  184. package/dist/lib/Glyph-WUqTF_Z9.js.map +0 -1
  185. package/dist/lib/GridConstraints-Q3wvuovN.js +0 -2
  186. package/dist/lib/GridConstraints-Q3wvuovN.js.map +0 -1
  187. package/dist/lib/LayoutConstraints-CHI2lhPd.js +0 -2
  188. package/dist/lib/LayoutConstraints-CHI2lhPd.js.map +0 -1
  189. package/dist/lib/LayoutSerialization-CWU0iEuV.js +0 -2
  190. package/dist/lib/LayoutSerialization-CWU0iEuV.js.map +0 -1
  191. package/dist/lib/LayoutSizes-BW4wyGDI.js +0 -2
  192. package/dist/lib/LayoutSizes-BW4wyGDI.js.map +0 -1
  193. package/dist/lib/List-D-GhoVgB.js +0 -2
  194. package/dist/lib/List-D-GhoVgB.js.map +0 -1
  195. package/dist/lib/Menu-Wz4MK6sA.js +0 -2
  196. package/dist/lib/Menu-Wz4MK6sA.js.map +0 -1
  197. package/dist/lib/MenuButton-JQQaKThJ.js +0 -2
  198. package/dist/lib/Panel-DIsdaqc2.js +0 -2
  199. package/dist/lib/Panel-DIsdaqc2.js.map +0 -1
  200. package/dist/lib/Position-B-WfZYAJ.js +0 -2
  201. package/dist/lib/Position-B-WfZYAJ.js.map +0 -1
  202. package/dist/lib/ProgressSpinner-Dzaka7_c.js +0 -2
  203. package/dist/lib/ProgressSpinner-Dzaka7_c.js.map +0 -1
  204. package/dist/lib/RadioButton-eI3-vXn3.js +0 -2
  205. package/dist/lib/RadioButton-eI3-vXn3.js.map +0 -1
  206. package/dist/lib/Scrollbar-DchfiFKC.js +0 -2
  207. package/dist/lib/Scrollbar-DchfiFKC.js.map +0 -1
  208. package/dist/lib/Slider-xRxNSjFN.js +0 -2
  209. package/dist/lib/Slider-xRxNSjFN.js.map +0 -1
  210. package/dist/lib/TabButton-pZKFYg7b.js +0 -2
  211. package/dist/lib/Text-D1RrBY52.js +0 -2
  212. package/dist/lib/Text-D1RrBY52.js.map +0 -1
  213. package/dist/lib/Tooltip-4lU_tf6r.js +0 -3
  214. package/dist/lib/Tooltip-4lU_tf6r.js.map +0 -1
  215. package/dist/lib/VirtualRowView-Con9SWiU.js +0 -2
  216. package/dist/lib/VirtualScroller-BPDmO1ir.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"table.es.js","names":[],"sources":["../../../src/typescript/lib/layout/Table.ts","../../../src/typescript/lib/component/table/cell/editor/CellEditor.ts","../../../src/typescript/lib/component/table/cell/Cell.ts","../../../src/typescript/lib/component/table/cell/editor/Boolean.ts","../../../src/typescript/lib/component/table/cell/renderer/CellRenderer.ts","../../../src/typescript/lib/component/table/cell/renderer/String.ts","../../../src/typescript/lib/component/table/cell/renderer/Number.ts","../../../src/typescript/lib/component/table/cell/renderer/Date.ts","../../../src/typescript/lib/component/table/cell/renderer/Time.ts","../../../src/typescript/lib/component/table/cell/renderer/DateTime.ts","../../../src/typescript/lib/component/table/cell/renderer/Glyph.ts","../../../src/typescript/lib/component/table/ColumnConfig.ts","../../../src/typescript/lib/component/table/cell/renderer/Combo.ts","../../../src/typescript/lib/component/table/cell/Dynamic.ts","../../../src/typescript/lib/component/table/cell/Default.ts","../../../src/typescript/lib/component/table/cell/String.ts","../../../src/typescript/lib/component/table/cell/Boolean.ts","../../../src/typescript/lib/component/table/cell/Number.ts","../../../src/typescript/lib/component/table/cell/Date.ts","../../../src/typescript/lib/component/table/cell/Time.ts","../../../src/typescript/lib/component/table/cell/DateTime.ts","../../../src/typescript/lib/component/table/cell/Glyph.ts","../../../src/typescript/lib/component/table/cell/Combo.ts","../../../src/typescript/lib/component/table/cell/renderer/TreeCell.ts","../../../src/typescript/lib/component/table/Row.ts","../../../src/typescript/lib/component/table/cell/ResizeHandle.ts","../../../src/typescript/lib/component/table/cell/SortPriorityBadge.ts","../../../src/typescript/lib/component/table/cell/Header.ts","../../../src/typescript/lib/component/table/cell/ParentHeader.ts","../../../src/typescript/lib/component/table/Header.ts","../../../src/typescript/lib/component/table/cell/editor/String.ts","../../../src/typescript/lib/component/table/cell/editor/Number.ts","../../../src/typescript/lib/component/table/cell/editor/TextInputCellEditor.ts","../../../src/typescript/lib/component/table/cell/editor/Date.ts","../../../src/typescript/lib/component/table/cell/editor/Time.ts","../../../src/typescript/lib/component/table/cell/editor/DateTime.ts","../../../src/typescript/lib/component/table/cell/editor/CellEditorPool.ts","../../../src/typescript/lib/component/table/cell/editor/Combo.ts","../../../src/typescript/lib/component/table/Body.ts","../../../src/typescript/lib/component/table/Footer.ts","../../../src/typescript/lib/component/table/Column.ts","../../../src/typescript/lib/component/table/TableExporter.ts","../../../src/typescript/lib/component/table/Table.ts","../../../src/typescript/lib/component/table/TablePanel.ts","../../../src/typescript/lib/component/table/TreeBody.ts","../../../src/typescript/lib/component/table/TreeTable.ts","../../../src/typescript/lib/component/table/TreeTablePanel.ts","../../../src/typescript/lib/component/table/cell/renderer/Link.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { LayoutManager, LayoutManagerOptions } from \"~/layout/LayoutManager.js\";\nimport { Table as TableComponent } from \"~/component/table/Table.js\";\nimport { Column } from \"~/component/table/Column.js\";\nimport { Component } from \"~/core/Component.js\";\nimport { Util } from \"~/core/Util.js\";\nimport { UNBOUNDED } from \"~/primitive/Size.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport { ThemeManager } from \"~/core/Theme.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for the {@link Table} layout manager.\n *\n * @category Layouts\n */\ninterface TableLayoutOptions extends LayoutManagerOptions {\n}\n\nconst BOOLEAN_WIDTH = 60;\nconst NUMBER_WIDTH = 90;\nconst DATE_WIDTH = 110;\nconst CHAR_WIDTH = 8;\nconst HEADER_PAD = 16;\n\n/**\n * A layout manager dedicated to the [`Table`](/api/component/table/classes/Table) component.\n * Positions the header, body, and footer sections within the container and\n * triggers virtual-scroll rendering on the body after each layout pass.\n *\n * Per-column widths are stored on the [`Table`](/api/component/table/classes/Table) component. On first render (or after\n * a model swap) widths are initialized based on field type and any `minWidth` /\n * `maxWidth` declared in the column spec: compact types (`boolean`, `number`, `date`)\n * receive a fixed base width floored at the header text width; string and auto columns\n * share the remaining space equally, clamped to their constraints. On container resize\n * compact-type columns keep their width unchanged; only flexible columns scale\n * proportionally, again clamped to their per-column constraints.\n */\nclass Table extends LayoutManager {\n\n /**\n * Constructs a Table layout manager.\n *\n * @param options - Optional construction-time options.\n */\n constructor(options?: TableLayoutOptions) {\n // LayoutManager's constructor takes no options; applied via applyOptions below.\n // eslint-disable-next-line local/forward-super-options\n super();\n\n if (options) {\n this.applyOptions(options);\n }\n }\n\n /**\n * Attaches to a container, throwing if it is not a [`Table`](/api/component/table/classes/Table)\n * or a [`TreeTable`](/api/component/table/classes/TreeTable) component.\n *\n * @param container - The container component to attach to.\n *\n * @remarks This layout manager is valid for containers whose class\n * name is `\"Table\"` or `\"TreeTable\"`. `TreeTable` extends `Table`\n * and reuses the entire header / body / footer geometry pipeline,\n * so it shares the same layout manager.\n */\n attach(container: Component) : this {\n const name = container.getClassName();\n\n if (name != \"Table\" && name != \"TreeTable\") {\n throw new Error(\"Container must be a Table or TreeTable.\");\n }\n\n super.attach(container);\n\n return this;\n }\n\n /**\n * Positions the header, body, and footer sections and triggers body virtual scroll rendering.\n *\n * @remarks Per-column widths are read from the Table component. If the stored widths do not\n * match the current column count (first render or model swap) they are re-initialized using\n * type-aware sizing clamped to each column's `minWidth` / `maxWidth`. On a container resize\n * the existing widths are scaled proportionally, again clamped to per-column constraints.\n */\n doLayout() {\n const container = <TableComponent>this.getContainer();\n\n if (!container) {\n return;\n }\n\n const containerSize = container.getInnerSize();\n\n if (!containerSize) {\n return;\n }\n\n const containerInsets = container.getContentInsets();\n const columns = container.getColumns();\n const columnCount = container.getHeader().getColumns().length;\n const availableWidth = containerSize.width - DOM.source.getScrollBarWidth();\n\n let columnWidths = container.getColumnWidths();\n\n if (columnWidths.length !== columnCount) {\n columnWidths = this.initializeWidths(columns, availableWidth);\n container.setColumnWidths(columnWidths);\n } else {\n columnWidths = this.rescaleWidths(columns, columnWidths, availableWidth);\n container.setColumnWidths(columnWidths);\n }\n\n const header = container.getHeader();\n const body = container.getBody();\n const footer = container.getFooter();\n\n // A section is laid out only when it is both visible (the Table's own\n // header/body/footer visibility flag) AND displayed (the core\n // `setDisplayed` flag). The two are reconciled, not substituted: a\n // `setDisplayed(false)` part drops out of the layout exactly like a\n // `setHeaderVisible(false)` one, and the body reflows to reclaim its band.\n if (container.isHeaderVisible() && header && header.isDisplayed()) {\n // Header cells render their text in the shared px line box, so the\n // row height is that line box plus the cell padding.\n const theme = ThemeManager.getTheme();\n // Cells render at the root font size, so the line box is the\n // additive `font-size + --ts-ui-line-padding` value `Util` derives\n // (the same line box the cell text is laid out at).\n const lineHeight = Util.lineHeightPx();\n const padding = theme.table.cell.padding ?? 2;\n const columnHeight = lineHeight + 2 * padding;\n\n // Parent header row uses the same arithmetic — same font, same\n // padding — so a theme swap re-runs `doLayout` and the two\n // rows stay aligned. Collapses to zero when no visible\n // column declares a `group`, so no-group tables are\n // byte-identical at runtime.\n const hasParentRow = header.hasParentRow();\n const parentRowHeight = hasParentRow ? columnHeight : 0;\n const headerBandHeight = parentRowHeight + columnHeight;\n\n // The header element stays pinned to the viewport width so the\n // gradient (and the scrollbar-cover band) covers the full\n // band, but the inner rows are sized to fit all cells —\n // including ones that overflow the viewport horizontally —\n // because `Row`'s default `overflow: hidden` would otherwise\n // clip cells at the row's right edge and prevent them from\n // coming into view when the inner rows translate left during\n // a horizontal scroll.\n const columnSum = columnWidths.reduce((s, w) => s + w, 0);\n const innerRowW = Math.max(containerSize.width, columnSum);\n\n header.setAutoCommitStyle(false);\n header.setX(containerInsets.getLeft());\n header.setY(containerInsets.getTop());\n header.setWidth(containerSize.width);\n header.setHeight(headerBandHeight);\n header.setAutoCommitStyle(true);\n\n // Parent row is sized + positioned first so its `spanFrom`\n // / `spanTo` constraints translate to x/width sums over the\n // column-width array beneath. When `hasParentRow` is false\n // the parent row stays collapsed at zero height and we\n // skip the per-cell layout pass entirely — no visible\n // column declares a group, so the parent-cell pool is\n // empty anyway.\n const parentRow = header.getParentRow();\n parentRow.setAutoCommitStyle(false);\n parentRow.setX(0);\n parentRow.setY(0);\n parentRow.setWidth(innerRowW);\n parentRow.setHeight(parentRowHeight);\n parentRow.setAutoCommitStyle(true);\n\n if (hasParentRow) {\n const parentCells = parentRow.getComponents();\n\n parentCells.forEach(cell => {\n const lc = parentRow.getLayoutConstraints(cell);\n const span = lc?.data as { spanFrom: number, spanTo: number } | undefined;\n const spanFrom = span?.spanFrom ?? 0;\n const spanTo = span?.spanTo ?? 0;\n\n let cellX = 0;\n for (let i = 0; i < spanFrom; i++) {\n cellX += columnWidths[i];\n }\n\n let cellW = 0;\n for (let i = spanFrom; i <= spanTo; i++) {\n cellW += columnWidths[i];\n }\n\n cell.setAutoCommitStyle(false);\n cell.setX(cellX);\n cell.setY(0);\n cell.setWidth(cellW);\n cell.setHeight(parentRowHeight);\n cell.setAutoCommitStyle(true);\n cell.doLayout();\n });\n }\n\n // Column row sits beneath the parent row. The cell y-coords\n // are relative to the column row's element, so they stay at\n // y=0 — only the row itself shifts down by `parentRowHeight`.\n const columnRow = header.getComponents()[1];\n columnRow.setAutoCommitStyle(false);\n columnRow.setX(0);\n columnRow.setY(parentRowHeight);\n columnRow.setWidth(innerRowW);\n columnRow.setHeight(columnHeight);\n columnRow.setAutoCommitStyle(true);\n\n const headerColumns = header.getColumns();\n let x = 0;\n\n headerColumns.forEach((col, i) => {\n col.setAutoCommitStyle(false);\n col.setX(x);\n col.setY(0);\n col.setWidth(columnWidths[i]);\n col.setHeight(columnHeight);\n col.setAutoCommitStyle(true);\n col.doLayout();\n\n x += columnWidths[i];\n });\n\n // Cover the vertical-scrollbar reservation at the header's\n // right edge so cells scrolled horizontally appear to clip at\n // the trackW boundary while the band stays continuous with the\n // rest of the header's gradient. Sits on top of the inner rows\n // by DOM order, beneath the scrollbar widget which lives in\n // the body.\n const trackW = DOM.source.getScrollBarWidth();\n const cover = header.getScrollbarCover();\n DOM.sink.apply(cover, {\n style: {\n left: (containerSize.width - trackW) + \"px\",\n width: trackW + \"px\",\n height: headerBandHeight + \"px\",\n },\n });\n }\n\n if (container.isFooterVisible() && footer && footer.isDisplayed()) {\n // Footer cells render their text in the shared px line box, so the\n // row height is that line box plus the cell padding.\n const theme = ThemeManager.getTheme();\n // Same additive line box as the header/body: the root font size plus\n // the `--ts-ui-line-padding` leading, via `Util`.\n const lineHeight = Util.lineHeightPx();\n const padding = theme.table.cell.padding ?? 2;\n const columnHeight = lineHeight + 2 * padding;\n const footerColumns = footer.getColumns();\n\n footer.setAutoCommitStyle(false);\n footer.setX(containerInsets.getLeft());\n footer.setY(containerInsets.getTop() + containerSize.height - columnHeight);\n footer.setWidth(containerSize.width);\n footer.setHeight(columnHeight);\n footer.setAutoCommitStyle(true);\n\n let x = 0;\n\n footerColumns.forEach((col, i) => {\n col.setAutoCommitStyle(false);\n col.setX(x);\n col.setY(0);\n col.setWidth(columnWidths[i]);\n col.setHeight(columnHeight);\n col.setAutoCommitStyle(true);\n col.doLayout();\n\n x += columnWidths[i];\n });\n }\n\n if (container.isBodyVisible() && body && body.isDisplayed()) {\n const headerHeight = container.isHeaderVisible() && header && header.isDisplayed() ? header.getHeight() : 0;\n const footerHeight = container.isFooterVisible() && footer && footer.isDisplayed() ? footer.getHeight() : 0;\n\n body.setAutoCommitStyle(false);\n body.setX(containerInsets.getLeft());\n body.setY(containerInsets.getTop() + headerHeight);\n body.setWidth(containerSize.width);\n body.setHeight(containerSize.height - headerHeight - footerHeight);\n body.setAutoCommitStyle(true);\n\n body.renderWindow(availableWidth, columnWidths);\n }\n }\n\n /**\n * Computes initial column widths from field types and column constraints.\n *\n * Fixed-type columns (boolean, number, date) get a type-based base width floored\n * at their header text width and clamped to any declared min/max. Flexible columns\n * (string, auto) share the remaining space equally, each clamped to its own min/max.\n *\n * @param columns - The visible resolved columns.\n * @param availableWidth - Total available pixel width for columns.\n * @returns The computed width for each column.\n */\n private initializeWidths(columns: Column[], availableWidth: number): number[] {\n const intrinsic: (number | null)[] = columns.map(col => {\n const f = col.getField();\n const headerMin = f.getDescription().length * CHAR_WIDTH + HEADER_PAD;\n\n switch (f.getType()) {\n case 'boolean': return this.clamp(Math.max(BOOLEAN_WIDTH, headerMin), col);\n case 'number': return this.clamp(Math.max(NUMBER_WIDTH, headerMin), col);\n case 'date': return this.clamp(Math.max(DATE_WIDTH, headerMin), col);\n default: return null;\n }\n });\n\n const fixedTotal = intrinsic.reduce((s: number, w) => s + (w ?? 0), 0);\n const flexCount = intrinsic.filter(w => w === null).length;\n const rawFlex = flexCount > 0\n ? (availableWidth - fixedTotal) / flexCount\n : 0;\n\n return intrinsic.map((w, i) => {\n if (w !== null) {\n return w;\n }\n\n const col = columns[i];\n const min = col.getMinWidth() ?? 30;\n const max = col.getMaxWidth() ?? UNBOUNDED;\n\n return Util.clamp(rawFlex, min, max);\n });\n }\n\n /**\n * Rescales existing flexible column widths to fit the new available width,\n * keeping fixed-type columns at their current size and clamping all columns\n * to their per-column constraints.\n *\n * @param columns - The visible resolved columns.\n * @param columnWidths - The existing width array from the previous layout.\n * @param availableWidth - Total available pixel width for columns.\n * @returns The updated width array, or the original if no rescaling was needed.\n */\n private rescaleWidths(columns: Column[], columnWidths: number[], availableWidth: number): number[] {\n const isFixed = columns.map(col => {\n const t = col.getField().getType();\n\n return t === 'boolean' || t === 'number' || t === 'date';\n });\n\n const fixedTotal = columnWidths.reduce((s: number, w, i) => s + (isFixed[i] ? w : 0), 0);\n const prevFlexTotal = columnWidths.reduce((s: number, w, i) => s + (isFixed[i] ? 0 : w), 0);\n const newFlexTotal = availableWidth - fixedTotal;\n\n if (prevFlexTotal <= 0 || Math.abs(prevFlexTotal - newFlexTotal) <= 0.5) {\n return columnWidths;\n }\n\n const ratio = newFlexTotal / prevFlexTotal;\n\n return columnWidths.map((w, i) => {\n if (isFixed[i]) {\n return w;\n }\n\n return this.clamp(w * ratio, columns[i]);\n });\n }\n\n /**\n * Clamps a width value to the `[minWidth, maxWidth]` range declared on a column,\n * using 30 px as the default minimum and no upper bound when unconstrained.\n *\n * @param width - The raw width to clamp.\n * @param column - The column whose constraints apply.\n * @returns The clamped width.\n */\n private clamp(width: number, column: Column): number {\n const min = column.getMinWidth() ?? 30;\n const max = column.getMaxWidth() ?? UNBOUNDED;\n\n return Util.clamp(width, min, max);\n }\n}\n\nconst TableCallable = callable(Table);\ntype TableCallable = Table;\nexport {\n Table as _Table,\n TableCallable as Table\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { Fit } from \"~/layout/Fit.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { ThemeManager } from \"~/core/Theme.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\n\n/**\n * Interns the `relatedTarget` of a blur event into a {@link Handle}, or returns\n * `null` when no node is receiving focus.\n *\n * A native `blur` carries `relatedTarget: Node | null`, but a cell editor that\n * re-fires its inner field's blur as a synthetic `CustomEvent(\"blur\")` (e.g.\n * {@link StringEditor}, {@link NumberEditor}) has no `relatedTarget` at all —\n * the property reads back `undefined`. Feeding that to\n * [`DOM.source.intern`](/api/core/namespaces/DOM/interfaces/Source#intern)\n * throws (`new WeakRef(undefined)` is a `TypeError`), which would abort the\n * blur listener *before* it commits the edit — leaving the cell stuck in edit\n * mode and the pooled editor un-released, so the whole column can no longer be\n * edited. Treating both `null` and `undefined` as \"focus left the editor\n * entirely\" keeps the commit path alive for the synthetic-blur editors while\n * preserving the real-node answer the {@link CellEditor.retainsFocus} overrides\n * depend on.\n *\n * @param e - The blur event, native or synthetically re-fired.\n * @returns The interned handle of the focus target, or `null`.\n *\n * @category Components\n */\nexport function blurRelatedTargetHandle(e: FocusEvent): Handle | null {\n return e.relatedTarget ? DOM.source.intern(e.relatedTarget) : null;\n}\n\n/**\n * The `detail` payload an editor forwards on its re-fired `\"keydown\"` custom\n * event. The editor wraps its inner field's native keydown so the parent cell's\n * commit/cancel logic can read `keyCode` (and the modifier flags) without the\n * editor naming the global `KeyboardEvent` constructor.\n *\n * @category Components\n */\nexport interface ForwardedKeyDetail {\n key: string;\n code: string;\n keyCode: number;\n shiftKey: boolean;\n ctrlKey: boolean;\n altKey: boolean;\n metaKey: boolean;\n}\n\n/**\n * Abstract base class for cell editors.\n *\n * Subclasses allow in-place editing of a typed value inside a table cell using a\n * Fit layout with theme-driven padding.\n *\n * @category Components\n */\nexport abstract class CellEditor<T> extends Component {\n\n /**\n * Installed by {@link CellEditorPool}: lets an editor that manages its own\n * dismissal (e.g. one that pops a floating picker dropdown) ask the active\n * cell to commit the edit. Null until wired.\n */\n private _onCommitRequested: (() => void) | null = null;\n\n constructor(tag: string = \"div\") {\n super({ tag });\n\n this.setLayoutManager(new Fit());\n\n // Inlined (rather than a private method) so CellRenderer and CellEditor stay\n // structurally compatible — BooleanCell relies on a CellEditor doubling as the\n // renderer, which fails if both classes declare a private member of the same name.\n const applyPadding = () => {\n const p = ThemeManager.getTheme().table.cell.padding;\n this.setInsets(new Insets(0, p, 0, p));\n };\n\n applyPadding();\n\n this.subscribeTheme(applyPadding);\n }\n\n /**\n * Returns the current editor value.\n *\n * @returns The edited value of type T.\n */\n abstract getValue(): T;\n\n /**\n * Sets the editor to an initial value before editing begins.\n *\n * @param t - The value to populate the editor with.\n */\n abstract setValue(t: T): void;\n\n /**\n * Returns whether the edit should stay alive when the editor's `<input>`\n * loses focus to `relatedTarget`. The default is `false` (any blur ends the\n * edit). Editors that own a floating sub-surface (e.g. a picker dropdown\n * with its own focusable field) override this to keep editing while focus\n * moves into that surface.\n *\n * @remarks Overrides delegate the cross-portal answer (\"did focus land\n * inside my dropdown or any descendant layer?\") to\n * [`LayerManager.containsAcrossLayers`](/api/core/namespaces/LayerManager/functions/containsAcrossLayers)\n * rather than walking a private layer stack themselves.\n *\n * @param _relatedTarget - The node handle receiving focus, or null.\n * @returns True to suppress the blur-driven commit/close.\n */\n retainsFocus(_relatedTarget: Handle | null): boolean {\n return false;\n }\n\n /**\n * Installs the handler the editor calls to ask the active cell to commit.\n * Wired once by {@link CellEditorPool}.\n *\n * @param handler - Callback that commits the active cell's edit.\n */\n setCommitRequestHandler(handler: () => void): void {\n this._onCommitRequested = handler;\n }\n\n /**\n * Asks the active cell to commit the edit. Used by editors that detect the\n * end of an edit themselves (e.g. an outside click while focus sits in a\n * popped picker rather than the editor's own input).\n */\n protected requestCommit(): void {\n this._onCommitRequested?.();\n }\n\n /**\n * Mirrors {@link CellRenderer.getContentX} so a `CellEditor` and a\n * `CellRenderer` stay structurally compatible — {@link BooleanCell}\n * uses a [`BooleanEditor`](/api/component/table/classes/BooleanEditor)\n * as both renderer and editor, and TS would reject that pass if\n * either base class declared a member the other didn't. Default\n * returns `0`; editors do not currently report indent offsets of\n * their own.\n *\n * @returns Always `0`.\n */\n getContentX(): number {\n return 0;\n }\n}","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { Card } from \"~/layout/Card.js\";\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { CellEditor, blurRelatedTargetHandle } from \"~/component/table/cell/editor/CellEditor.js\";\nimport type { ForwardedKeyDetail } from \"~/component/table/cell/editor/CellEditor.js\";\nimport { CellEditorPool } from \"~/component/table/cell/editor/CellEditorPool.js\";\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\n\n/**\n * String-literal union of the events emitted by {@link Cell}.\n *\n * @category Components\n */\nexport type CellEvent = \"commit\" | \"editend\";\n\n/**\n * Base class for table cells that support both a display renderer and an optional in-place editor.\n *\n * A {@link Card} layout toggles between the renderer and editor views. Double-clicking the\n * renderer starts an edit; blur or Enter commits it; Escape cancels it.\n *\n * Subclasses ({@link BooleanCell}, {@link NumberCell}, {@link StringCell}, {@link HeaderCell})\n * wire a typed renderer and editor pair. Custom cell types extend this class with your own\n * renderer / editor classes.\n *\n * @category Components\n */\nexport class Cell<T> extends Component {\n\n private _readOnly: boolean;\n private _requiredEmpty: boolean = false;\n private _baseBackground: string = 'var(--ts-ui-table-cell-bg, transparent)';\n private _renderer: CellRenderer<T>;\n private _editor: CellEditor<T> | undefined;\n private _editorPool: CellEditorPool | null = null;\n private _scrollIntoView: (() => void) | null = null;\n protected _activeEditor: CellEditor<T> | null = null;\n // Typed as `ListenerBag<string>` rather than `ListenerBag<CellEvent>` so\n // subclasses (e.g. HeaderCell) can re-declare the bag with a wider\n // event union without fighting TypeScript's invariant generic on\n // `ListenerBag.add`. The compile-time gate on event names lives on the\n // host's typed `on` / `off` / `emit` overloads, not on the bag field.\n protected _listeners: ListenerBag<string> = new ListenerBag<string>();\n\n constructor(tag: string, renderer: CellRenderer<T>, editor?: CellEditor<T>, rendererConstraints?: LayoutConstraints, editorContraints?: LayoutConstraints) {\n super({ tag: tag || \"td\" });\n\n this.setLayoutManager(new Card());\n\n this.getAria().setRole(\"gridcell\");\n\n this._readOnly = false;\n this._renderer = renderer;\n this._editor = editor;\n this.setInsets(new Insets(0, 0, 0, 0));\n\n this.setBackgroundColor('var(--ts-ui-table-cell-bg, transparent)');\n this.setForegroundColor('var(--ts-ui-table-cell-color, inherit)');\n this.setBorder('var(--ts-ui-table-cell-border, none)');\n\n this.subscribeTheme(() => this.setBorder('var(--ts-ui-table-cell-border, none)'));\n\n this.addComponent(renderer, rendererConstraints);\n\n if (editor) {\n this.addComponent(editor, editorContraints);\n\n editor.setCommitRequestHandler(() => this.commitEdit());\n\n // Internal cell-editor wiring: listens on a privately-owned child;\n // see the cell-editor carve-out in ARCHITECTURE.md.\n Event.addListener(editor, 'blur', (e: FocusEvent) => {\n if (editor.retainsFocus(blurRelatedTargetHandle(e))) {\n return;\n }\n\n this.commitEdit();\n });\n Event.addListener(editor, 'keydown', (e: CustomEvent<ForwardedKeyDetail>) => this.onKeyDown(e));\n }\n\n // Internal cell-editor wiring: listens on a privately-owned child;\n // see the cell-editor carve-out in ARCHITECTURE.md.\n Event.addListener(renderer, 'dblclick', () => this.startEdit());\n }\n\n /**\n * Opts out of content-size clamping: a cell fits the geometry its host\n * {@link Body} force-assigns (column width × row height), exactly like a\n * {@link Container} fits its parent's allocation. Without this, a cell\n * whose visible child carries a hard maximum — e.g. {@link BooleanCell}'s\n * 16×16 checkbox — would clamp {@link Component.setHeight} down to that\n * child max and sit shorter than the row, pinning the checkbox to the top\n * instead of letting the cell's anchored layout centre it. Only the cell's\n * own explicit min/max remain hard limits.\n *\n * @returns `false` — clamp to explicit constraints only, never to the\n * content-derived size.\n */\n protected clampsToContentSize(): boolean {\n return false;\n }\n\n /**\n * Returns the pool-key that selects which shared editor this cell should borrow on edit.\n *\n * @returns A string key registered on the {@link CellEditorPool}, or `null` to opt out of\n * the shared-editor mechanism.\n *\n * @remarks The default implementation returns `null` so user-authored cells that pass an\n * editor through the constructor (the legacy path) keep working unchanged. Built-in typed\n * cells override this to return their variant key (e.g. `\"string\"`, `\"time:seconds\"`).\n */\n getEditorKey(): string | null {\n return null;\n }\n\n /**\n * Attaches this cell to a {@link CellEditorPool} so that {@link Cell.startEdit} can borrow\n * a shared editor instead of allocating one.\n *\n * @param pool - The pool to borrow from, or `null` to disable shared-editor mode.\n * @returns This cell, for method chaining.\n */\n setEditorPool(pool: CellEditorPool | null): this {\n this._editorPool = pool;\n\n return this;\n }\n\n /**\n * Installs the callback {@link Cell.startEdit} runs before opening the\n * editor, used by the host {@link Body} to scroll this cell's column into\n * view through its scroll model. Running it first means the editor — and\n * any picker dropdown it anchors — opens at the cell's final position\n * rather than its pre-scroll one.\n *\n * @param handler - The scroll-into-view callback, or `null` to clear it.\n * @returns This cell, for method chaining.\n */\n setScrollIntoViewHandler(handler: (() => void) | null): this {\n this._scrollIntoView = handler;\n\n return this;\n }\n\n /**\n * Registers a listener for one of this cell's events.\n *\n * @param event - `\"commit\"` fires with the committed value whenever an\n * edit is committed; `\"editend\"` fires after the cell returns to\n * renderer view via keyboard action (Enter / Escape), not on blur.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This cell, for method chaining.\n */\n on(event: \"commit\", listener: (value: T) => void): this;\n on(event: \"editend\", listener: () => void): this;\n on(event: CellEvent, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered listener. The exact callback reference\n * must match.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This cell, for method chaining.\n */\n off(event: CellEvent, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every listener registered for `event` with `payload`, in\n * registration order.\n *\n * @param event - The event to emit.\n * @param payload - Forwarded to each listener.\n */\n protected emit(event: \"commit\", value: T): void;\n protected emit(event: \"editend\"): void;\n protected emit(event: CellEvent, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Returns true if the cell cannot be edited.\n *\n * @returns True if the cell is read-only.\n */\n isReadOnly() {\n return !!this._readOnly;\n }\n\n /**\n * Sets whether this cell is read-only. Read-only cells refuse\n * {@link Cell.startEdit}, render with the\n * `--ts-ui-table-cell-readonly-bg` tint, and present a default\n * cursor instead of any renderer-supplied edit affordance.\n * Idempotent — passing the current value short-circuits before any\n * style writes.\n *\n * Body rows call this from their cell-construction loop based on\n * the column's `ColumnConfig.readOnly` flag, and the body's\n * per-rebind read-only resolution forwards the OR of the static\n * column flag, the spec-level row predicate, and the per-cell\n * predicate. Application code should declare read-only through\n * those config-level surfaces rather than calling this setter\n * directly on a cell.\n *\n * Calling `setReadOnly(true)` while the cell is in edit mode\n * silently commits the active edit before flipping the flag, so the\n * borrowed editor releases back to the {@link CellEditorPool}\n * cleanly and the in-progress user input lands on the record. The\n * commit fires the cell's `onCommit` callback (and any cascading\n * store-event refresh) as if the user had blurred the editor.\n *\n * Read-only wins over the required-empty outline set via\n * {@link Cell.setRequiredEmpty} — a read-only cell cannot be\n * filled, so ringing it as required would be misleading.\n *\n * @param value - `true` to mark read-only, `false` to restore the\n * default editable appearance.\n * @returns This cell, for method chaining.\n */\n setReadOnly(value: boolean): this {\n if (this._readOnly === value) {\n return this;\n }\n\n // Flip the flag BEFORE running the mid-edit commit. The\n // commit's onCommit callback synchronously calls\n // `store.notifyRecordChanged`, which cascades back into\n // `Body.applyReadOnlyState` and re-invokes this same setter on\n // this same cell. Flipping first lets the re-entrant call\n // short-circuit on the idempotence guard above; otherwise it\n // would observe `_readOnly === false`, see `isEditing() ===\n // true`, and recurse into `commitEdit` indefinitely.\n // `commitEdit` itself checks `isReadOnly()` and would\n // short-circuit now that the flag is set, so we inline its\n // value/renderer/onCommit/detach sequence here instead.\n this._readOnly = value;\n\n if (value && this._activeEditor) {\n const committedValue = this._activeEditor.getValue();\n\n this._renderer.setValue(committedValue);\n this.emit(\"commit\", committedValue as T);\n this.detachEditor();\n }\n\n this._applyStateTint();\n\n return this;\n }\n\n /**\n * Sets whether this cell shows the required-empty outline — a visual\n * cue that a required column's cell currently holds an empty value.\n * Sourced from `--ts-ui-table-cell-required-outline`. Idempotent —\n * passing the current value short-circuits before any style writes.\n *\n * Body rows call this from their per-rebind resolution based on the\n * column's `ColumnConfig.required` flag and `requiredPredicate`.\n * Read-only wins over this outline — see {@link Cell.setReadOnly}'s\n * precedence note.\n *\n * @param value - `true` to show the required-empty outline, `false`\n * to hide it.\n * @returns This cell, for method chaining.\n */\n setRequiredEmpty(value: boolean): this {\n if (this._requiredEmpty === value) {\n return this;\n }\n\n this._requiredEmpty = value;\n this._applyStateTint();\n\n return this;\n }\n\n /**\n * Sets the background this cell falls back to when not read-only —\n * e.g. a column's `groupColor` tint. {@link Row} routes its\n * group-color write through this setter (instead of\n * `setBackgroundColor` directly) so a filled cell in a grouped,\n * required column restores its group tint rather than going\n * transparent (the required-empty outline is a separate overlay\n * and does not affect the background).\n *\n * @param color - The CSS color string to use as the base background.\n * @returns This cell, for method chaining.\n */\n setBaseBackground(color: string): this {\n this._baseBackground = color;\n this._applyStateTint();\n\n return this;\n }\n\n /**\n * Resolves this cell's background + cursor from precedence\n * read-only ▸ base background, and separately resolves the\n * required-empty outline (shown only when required-empty and NOT\n * read-only). The single owner of both so the read-only and\n * required-empty states cannot fight over either.\n */\n private _applyStateTint(): void {\n if (this._readOnly) {\n this.setBackgroundColor('var(--ts-ui-table-cell-readonly-bg, rgba(0, 0, 0, 0.04))');\n this.setCursor('default');\n } else {\n this.setBackgroundColor(this._baseBackground);\n this.clearCursor();\n }\n\n // Read-only wins: a read-only cell cannot be filled, so ringing it\n // as required would be misleading. Uses an inset box-shadow (the\n // same \"border via shadow\" idiom the cell editors use for their own\n // focus border) rather than the CSS `outline` property, which\n // `Body._updateFocusStyle` already owns for the keyboard-focus ring.\n if (this._requiredEmpty && !this._readOnly) {\n this.setShadow('inset 0 0 0 1px var(--ts-ui-table-cell-required-outline, rgba(220, 60, 60, 0.6))');\n } else {\n this.clearShadow();\n }\n }\n\n /**\n * Commits on Enter and cancels on Escape while editing, then fires `_onEditEnd` to return focus to the container.\n *\n * @param evnt - The forwarded keydown event. The editor re-fires its inner\n * field's keydown as a custom event whose `detail` carries `keyCode` and\n * the modifier flags, so this reads `detail` rather than the native\n * top-level properties.\n */\n onKeyDown(evnt: CustomEvent<ForwardedKeyDetail>): void {\n const keyCode = evnt.detail?.keyCode;\n\n if (keyCode == 13) { // Enter\n this.commitEdit();\n this.emit(\"editend\");\n } else if (keyCode == 27) { // Escape\n this.cancelEdit();\n this.emit(\"editend\");\n }\n }\n\n /**\n * Returns true if an editor is currently mounted on this cell.\n *\n * @returns True if the cell is in edit mode.\n */\n isEditing(): boolean {\n return this._activeEditor !== null;\n }\n\n /**\n * Returns the Card layout manager used to toggle between renderer and editor.\n *\n * @returns The {@link Card} layout manager for this cell.\n */\n getLayoutManager() {\n return <Card>super.getLayoutManager();\n }\n\n /**\n * Switches to the editor view, copies the renderer's value, and focuses the editor.\n *\n * @remarks When the cell was constructed with a per-cell editor (legacy mode), that editor\n * is used. Otherwise the cell asks its {@link CellEditorPool} for the shared editor matching\n * {@link Cell.getEditorKey} and parents it into this cell for the duration of the edit.\n */\n startEdit(): void {\n if (this.isReadOnly() || this.isEditing()) {\n return;\n }\n\n // Scroll this column fully into view before the editor opens, so the\n // editor and any picker dropdown it anchors land at the cell's final\n // position. The editor itself focuses with preventScroll, so the\n // browser never scrolls the body out from under the scroll model.\n this._scrollIntoView?.();\n\n if (this._editor) {\n this._activeEditor = this._editor;\n } else {\n const key = this.getEditorKey();\n if (!key || !this._editorPool) {\n return;\n }\n\n const shared = this._editorPool.acquire(key, this);\n if (!shared) {\n return;\n }\n\n this._activeEditor = shared as CellEditor<T>;\n this.addComponent(this._activeEditor);\n }\n\n const editor = this._activeEditor;\n const renderer = this._renderer;\n\n this.prepareEditor(editor);\n editor.setValue(renderer.getValue());\n\n this.getLayoutManager().setVisibleComponentId(editor.getId());\n this.doLayout();\n // preventScroll — the column was already revealed through the scroll\n // model above; a native focus-scroll would desync the header + scrollbar.\n editor.focus(true);\n }\n\n /**\n * Saves the editor value to the renderer, fires onCommit, and returns to renderer view.\n *\n * @returns This cell, for method chaining.\n */\n commitEdit(): this {\n if (this.isReadOnly() || !this.isEditing()) {\n return this;\n }\n\n const editor = this._activeEditor!;\n const value = editor.getValue();\n\n this._renderer.setValue(value);\n this.emit(\"commit\", value as T);\n\n this.detachEditor();\n\n return this;\n }\n\n /**\n * Discards the editor value and returns to renderer view.\n */\n cancelEdit(): void {\n if (this.isReadOnly() || !this.isEditing()) {\n return;\n }\n\n this.detachEditor();\n }\n\n /**\n * Restores the renderer as the visible card layer and, in shared-editor mode, detaches the\n * borrowed editor from this cell so the pool can lend it to the next cell.\n *\n * @remarks `activeEditor` is cleared **before** the Card swap because hiding the editor\n * pulls focus off its `<input>` and synchronously fires `blur`. The pool's blur listener\n * routes back into `commitEdit`, which would otherwise re-enter here and double-remove the\n * element — clearing the pointer first makes the re-entrant `isEditing()` check short-circuit.\n */\n private detachEditor(): void {\n const editor = this._activeEditor;\n if (!editor) {\n return;\n }\n\n this._activeEditor = null;\n\n this.getLayoutManager().setVisibleComponentId(this._renderer.getId());\n this.doLayout();\n\n if (this._editor !== editor) {\n this.removeComponent(editor);\n this._editorPool?.release();\n }\n }\n\n /**\n * Sets the renderer's displayed value.\n *\n * @param value - The value to pass to the renderer.\n */\n setValue(value: T) : this {\n this._renderer.setValue(value);\n\n return this;\n }\n\n /**\n * Returns the cell's renderer component.\n *\n * @returns The {@link CellRenderer} for this cell.\n */\n getRenderer() {\n return this._renderer;\n }\n\n /**\n * Runs the inherited (Card) layout, then re-indents the active\n * editor so it lines up with the renderer's content offset\n * (`renderer.getContentX()`). For a plain renderer the offset is\n * `0` and this is a no-op; for a\n * [`TreeCellRenderer`](/api/component/table/classes/TreeCellRenderer)\n * the editor shifts right by `depth * indentPx + TOGGLE_WIDTH` so\n * it stays aligned with the value the user double-clicked instead\n * of snapping to the cell's left edge.\n *\n * @returns This cell, for method chaining.\n */\n doLayout(): this {\n super.doLayout();\n\n this.alignEditorWithContent();\n\n return this;\n }\n\n /**\n * If an editor is currently active and the renderer reports a\n * non-zero content offset, override the Card layout's\n * fill-the-cell editor placement so the editor starts at that\n * offset and shrinks its width accordingly. Idempotent — when no\n * offset is reserved this is a single comparison and an early\n * return.\n */\n private alignEditorWithContent(): void {\n const editor = this._activeEditor;\n\n if (!editor) {\n return;\n }\n\n const contentX = this._renderer.getContentX();\n\n if (contentX <= 0) {\n return;\n }\n\n const cellWidth = this.getWidth() ?? 0;\n const editorWidth = Math.max(0, cellWidth - contentX);\n\n editor.setAutoCommitStyle(false);\n editor.setX(contentX);\n editor.setWidth(editorWidth);\n editor.setAutoCommitStyle(true);\n }\n\n /**\n * Replaces the cell's renderer with one returned by `factory`, after\n * passing the existing renderer to the factory so it can be adopted as\n * a delegate. The wrapping renderer becomes this cell's new\n * {@link CellRenderer.getValue} / {@link CellRenderer.setValue} target;\n * the Card layout's renderer-visible state is restored automatically.\n *\n * Used by tree-table rows to install a `TreeCellRenderer` that\n * prepends an indent + expand/collapse toggle in front of the typed\n * renderer appropriate for the column's field type.\n *\n * @param factory - Receives the detached old renderer; returns the\n * new wrapping renderer that must adopt it as a child.\n *\n * @returns This cell, for method chaining.\n */\n wrapRenderer(factory: (delegate: CellRenderer<T>) => CellRenderer<T>): this {\n const oldRenderer = this._renderer;\n\n this.removeComponent(oldRenderer);\n\n const newRenderer = factory(oldRenderer);\n\n this._renderer = newRenderer;\n this.addComponent(newRenderer);\n this.getLayoutManager().setVisibleComponentId(newRenderer.getId());\n\n return this;\n }\n\n /**\n * Returns the cell's editor component, or undefined if the cell is display-only.\n *\n * @returns The {@link CellEditor} for this cell, or undefined.\n */\n getEditor() {\n return this._editor;\n }\n\n /**\n * Swaps which renderer is this cell's active display + commit target.\n *\n * @param renderer - The renderer to make active.\n * @param isNewChild - `true` the first time `renderer` is shown on this\n * cell: it is parented via `addComponent` and wired for double-click\n * activation, mirroring the wiring the constructor does for the\n * initial renderer. `false` for a renderer already parented on an\n * earlier swap.\n */\n protected setActiveRenderer(renderer: CellRenderer<T>, isNewChild: boolean): void {\n if (isNewChild) {\n this.addComponent(renderer);\n\n // Internal cell-editor wiring: listens on a privately-owned child;\n // see the cell-editor carve-out in ARCHITECTURE.md.\n Event.addListener(renderer, 'dblclick', () => this.startEdit());\n }\n\n this._renderer = renderer;\n this.getLayoutManager().setVisibleComponentId(renderer.getId());\n }\n\n /**\n * Hook run immediately before the acquired editor receives the\n * renderer's value in {@link Cell.startEdit}. Default is a no-op.\n *\n * @param _editor - The editor about to be shown, not yet holding a value.\n */\n protected prepareEditor(_editor: CellEditor<T>): void {\n // no-op by default\n }\n}","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellEditor } from \"~/component/table/cell/editor/CellEditor.js\";\nimport { Checkbox } from \"~/component/input/Checkbox.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * String-literal union of the events emitted by {@link BooleanEditor}.\n *\n * @category Components\n */\nexport type BooleanEditorEvent = \"change\";\n\n/**\n * An always-visible checkbox editor for boolean cell values.\n *\n * Used directly as the renderer in {@link BooleanCell}; changes fire the onChange\n * callback immediately without a separate commit step. A record whose\n * boolean field is `null` or `undefined` renders as the checkbox's\n * indeterminate (mixed) state; the first user click follows the\n * Checkbox's WAI-ARIA mixed-state rule and lands at `true`, after\n * which the cell carries a concrete `true`/`false` value.\n *\n * @category Components\n */\nclass BooleanEditor extends CellEditor<Boolean | null> {\n\n private _checkBox: Checkbox = new Checkbox();\n private _value: Boolean | null = null;\n private _listeners: ListenerBag<BooleanEditorEvent> = new ListenerBag<BooleanEditorEvent>();\n private _suppressCommit: boolean = false;\n\n constructor() {\n super();\n\n this._checkBox.setIndeterminate(true);\n // Suppress the 120 ms fill/check transition — a virtualized table\n // can call `setValue` on dozens of pool slots per scroll frame, and\n // each transition would otherwise leave the checkbox visibly\n // animating mid-scroll.\n this._checkBox.setAnimated(false);\n this.addComponent(this._checkBox);\n\n this._checkBox.on(\"action\", () => {\n // `Checkbox.setSelected` dispatches a synthetic\n // `CustomEvent(\"click\")` on the root for backward-compat with\n // `on(\"action\", fn)` consumers, which fires for BOTH real user\n // toggles AND programmatic `setValue` calls. Without the\n // `_suppressCommit` guard, every scroll-driven `setValue` would\n // commit the bound record back to the store, fire\n // `'datachange'`, and re-render both bodies in a loop.\n if (this._suppressCommit) {\n return;\n }\n\n this._value = this._checkBox.isSelected();\n this.emit(\"change\", this._value);\n });\n }\n\n /**\n * Registers a listener for one of this editor's events.\n *\n * @param event - `\"change\"` fires when the checkbox value changes,\n * receiving the concrete `true`/`false` produced by the click. The\n * editor never emits `null` from user interaction.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This editor, for method chaining.\n */\n on(event: \"change\", listener: (value: Boolean | null) => void): this;\n on(event: BooleanEditorEvent, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered listener. The exact callback reference\n * must match.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This editor, for method chaining.\n */\n off(event: BooleanEditorEvent, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every listener registered for `event` with `payload`, in\n * registration order.\n *\n * @param event - The event to emit.\n * @param payload - Forwarded to each listener.\n */\n protected emit(event: \"change\", value: Boolean | null): void;\n protected emit(event: BooleanEditorEvent, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Returns the cached boolean value, or `null` when the checkbox is\n * still in its initial indeterminate state.\n *\n * @returns `true`/`false` once the user (or `setValue`) has set a\n * concrete value, otherwise `null`.\n */\n getValue(): Boolean | null {\n return this._value;\n }\n\n /**\n * Sets the checkbox state. `null` and `undefined` put the checkbox\n * into the indeterminate state and cache the value as `null`;\n * `true`/`false` clear indeterminate and select accordingly.\n *\n * @param value - The boolean value to reflect on the checkbox, or\n * `null`/`undefined` to render the indeterminate state.\n */\n setValue(value: Boolean | null): this {\n this._value = value ?? null;\n\n // Wrap the programmatic state updates in the suppress-commit guard\n // so the synthetic `click` events dispatched by `setIndeterminate`\n // and `setSelected` don't fire the cell's commit callback. The\n // guard is checked by the constructor's action listener.\n this._suppressCommit = true;\n try {\n if (this._value === null) {\n this._checkBox.setIndeterminate(true);\n } else {\n this._checkBox.setIndeterminate(false);\n this._checkBox.setSelected(this._value as boolean);\n }\n } finally {\n this._suppressCommit = false;\n }\n\n return this;\n }\n\n /**\n * Marks the checkbox read-only (or restores it). A read-only checkbox\n * ignores user clicks and keyboard toggles, so a read-only boolean cell\n * stops accepting edits; programmatic {@link setValue} is unaffected.\n *\n * @param value - `true` to make the checkbox read-only, `false` to restore.\n *\n * @returns This editor, for method chaining.\n */\n setReadOnly(value: boolean): this {\n this._checkBox.setReadOnly(value);\n\n return this;\n }\n\n /**\n * Returns whether the checkbox is currently read-only.\n *\n * @returns `true` when the checkbox rejects user interaction.\n */\n isReadOnly(): boolean {\n return this._checkBox.isReadOnly();\n }\n\n /**\n * Toggles the checkbox and fires the onChange callback. Clears the\n * indeterminate state if it was set, so the toggled value is always\n * a concrete boolean.\n */\n toggle(): this {\n const next = !this._checkBox.isSelected();\n\n this._checkBox.setIndeterminate(false);\n this._checkBox.setSelected(next);\n this._value = next;\n this.emit(\"change\", this._value);\n\n return this;\n }\n}\n\nconst BooleanEditorCallable = callable(BooleanEditor);\ntype BooleanEditorCallable = BooleanEditor;\nexport {\n BooleanEditor as _BooleanEditor,\n BooleanEditorCallable as BooleanEditor\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { Fit } from \"~/layout/Fit.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { ThemeManager } from \"~/core/Theme.js\";\n\n/**\n * Abstract base class for cell renderers.\n *\n * Subclasses display a typed value inside a table cell using a Fit layout with theme-driven padding.\n *\n * @category Components\n */\nexport abstract class CellRenderer<T> extends Component {\n\n constructor() {\n super();\n\n this.setLayoutManager(new Fit());\n\n // Inlined (rather than a private method) so CellRenderer and CellEditor stay\n // structurally compatible — BooleanCell relies on a CellEditor doubling as the\n // renderer, which fails if both classes declare a private member of the same name.\n const applyPadding = () => {\n const p = ThemeManager.getTheme().table.cell.padding;\n this.setInsets(new Insets(0, p, 0, p));\n };\n\n applyPadding();\n\n this.subscribeTheme(applyPadding);\n }\n\n /**\n * Returns the currently displayed value.\n *\n * @returns The rendered value of type T.\n */\n abstract getValue(): T;\n\n /**\n * Updates the displayed value.\n *\n * @param t - The value to render.\n */\n abstract setValue(t: T): void;\n\n /**\n * Returns the x offset (in this renderer's local space) at which\n * the cell's actual content begins. Defaults to `0` — the typed\n * renderer fills its box from the left.\n *\n * Overridden by [`TreeCellRenderer`](/api/component/table/classes/TreeCellRenderer)\n * to reserve space for the indent + toggle column. The owning\n * {@link Cell} consults this on every `doLayout` so the editor\n * stays visually aligned with where the renderer's content sat,\n * instead of snapping to the left cell edge on edit.\n *\n * @returns The pixels reserved at the left of the renderer's box\n * before its content begins.\n */\n getContentX(): number {\n return 0;\n }\n\n /**\n * Runs the Fit layout, then synchronises the Text child's `line-height`\n * to its own element height. A single-line `<span>` whose `line-height`\n * matches its block height renders the text glyphs vertically centered;\n * without this, the line-box sits at the top of the cell and leaves the\n * empty space below.\n *\n * Cell renderer Texts have `setAutoMeasure(false)`, so `setLineHeight`\n * skips its DOM measurement and only writes the CSS rule.\n */\n doLayout(): this {\n super.doLayout();\n\n const children = this.getComponents();\n if (children.length !== 1) {\n return this;\n }\n\n const child = children[0];\n if (!(child instanceof Text)) {\n return this;\n }\n\n const h = child.getHeight();\n if (h > 0) {\n child.setLineHeight(h);\n }\n\n return this;\n }\n}","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A read-only renderer for string cell values.\n *\n * Displays the value via a {@link Text}. Caches the last value passed\n * to {@link setValue} so {@link getValue} returns `null` for an empty\n * cell, distinct from the empty-string render the user sees.\n *\n * @category Components\n */\nclass StringRenderer extends CellRenderer<String | null> {\n\n private _text: Text = new Text();\n private _value: String | null = null;\n\n constructor() {\n super();\n\n this._text.setText(\"\");\n this._text.setPointerEvents(\"none\");\n this._text.setAutoMeasure(false);\n this.addComponent(this._text);\n }\n\n /**\n * Returns the text component used to display text.\n *\n * @returns The underlying {@link Text}.\n */\n getText(): Text {\n return this._text;\n }\n\n /**\n * Returns the cached string value, or `null` when the cell is empty.\n *\n * @returns The displayed string value, or `null`.\n */\n getValue(): String | null {\n return this._value;\n }\n\n /**\n * Caches the value and renders it as text. `null` and `undefined`\n * are both normalised to `null` and render as the empty string.\n *\n * @param value - The string value to display, or `null`/`undefined`\n * to clear the cell.\n */\n setValue(value: String | null): this {\n this._value = value ?? null;\n this._text.setText(this._value === null ? \"\" : String(this._value));\n\n return this;\n }\n}\n\nconst StringRendererCallable = callable(StringRenderer);\ntype StringRendererCallable = StringRenderer;\nexport {\n StringRenderer as _StringRenderer,\n StringRendererCallable as StringRenderer\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A read-only renderer for numeric cell values.\n *\n * Displays the value right-aligned via a {@link Text}. Caches the last\n * value passed to {@link setValue} so {@link getValue} returns the exact\n * `Number | null` that was rendered — never the result of re-parsing the\n * DOM text, which silently coerces an empty cell back to `0`.\n *\n * @category Components\n */\nclass NumberRenderer extends CellRenderer<Number | null> {\n\n private _text: Text = new Text();\n private _value: Number | null = null;\n\n constructor() {\n super();\n\n this._text.setPointerEvents(\"none\");\n this._text.setTextAlign(\"right\");\n this._text.setText(\"\");\n this._text.setAutoMeasure(false);\n\n this.addComponent(this._text);\n }\n\n /**\n * Returns the cached numeric value, or `null` when the cell is\n * empty. Reads the private cache rather than re-parsing the DOM\n * text so an empty cell round-trips as `null` instead of `0`.\n *\n * @returns The current numeric value, or `null`.\n */\n getValue(): Number | null {\n return this._value;\n }\n\n /**\n * Caches the value and renders it as text. `null` and `undefined`\n * are both normalised to `null` and render as the empty string;\n * every other value (including `0`, `-1`, `NaN`, `Infinity`) goes\n * through `String(value)` so the cell shows the actual literal —\n * never the words `\"undefined\"` or `\"null\"`.\n *\n * @param value - The numeric value to display, or `null`/`undefined`\n * to clear the cell.\n */\n setValue(value: Number | null): this {\n this._value = value ?? null;\n this._text.setText(this._value === null ? \"\" : String(this._value));\n\n return this;\n }\n}\n\nconst NumberRendererCallable = callable(NumberRenderer);\ntype NumberRendererCallable = NumberRenderer;\nexport {\n NumberRenderer as _NumberRenderer,\n NumberRendererCallable as NumberRenderer\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A read-only renderer for date cell values.\n *\n * Displays the value via a {@link Text} formatted with `Date.toLocaleDateString`.\n */\nclass DateRenderer extends CellRenderer<Date | null> {\n\n private _text: Text = new Text();\n private _value: Date | null = null;\n\n constructor() {\n super();\n\n this._text.setText(\"\");\n this._text.setPointerEvents(\"none\");\n this._text.setAutoMeasure(false);\n this.addComponent(this._text);\n }\n\n getValue(): Date | null {\n return this._value;\n }\n\n setValue(value: Date | null): this {\n this._value = value ?? null;\n this._text.setText(value ? value.toLocaleDateString() : \"\");\n\n return this;\n }\n}\n\nconst DateRendererCallable = callable(DateRenderer);\ntype DateRendererCallable = DateRenderer;\nexport {\n DateRenderer as _DateRenderer,\n DateRendererCallable as DateRenderer\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A read-only renderer for time cell values.\n *\n * Displays the time portion of a `Date` via a {@link Text} formatted with\n * `Date.toLocaleTimeString`.\n */\nclass TimeRenderer extends CellRenderer<Date | null> {\n\n private _text: Text = new Text();\n private _value: Date | null = null;\n private _showSeconds: boolean;\n\n constructor(showSeconds: boolean = false) {\n super();\n this._showSeconds = showSeconds;\n\n this._text.setText(\"\");\n this._text.setPointerEvents(\"none\");\n this._text.setAutoMeasure(false);\n this.addComponent(this._text);\n }\n\n getValue(): Date | null {\n return this._value;\n }\n\n setValue(value: Date | null): this {\n this._value = value ?? null;\n const opts: Intl.DateTimeFormatOptions = this._showSeconds\n ? { hour: '2-digit', minute: '2-digit', second: '2-digit' }\n : { hour: '2-digit', minute: '2-digit' };\n this._text.setText(value ? value.toLocaleTimeString(undefined, opts) : \"\");\n\n return this;\n }\n}\n\nconst TimeRendererCallable = callable(TimeRenderer);\ntype TimeRendererCallable = TimeRenderer;\nexport {\n TimeRenderer as _TimeRenderer,\n TimeRendererCallable as TimeRenderer\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A read-only renderer for date-time cell values.\n *\n * Displays a `Date` via a {@link Text} formatted with `Date.toLocaleString`.\n */\nclass DateTimeRenderer extends CellRenderer<Date | null> {\n\n private _text: Text = new Text();\n private _value: Date | null = null;\n private _showSeconds: boolean;\n\n constructor(showSeconds: boolean = false) {\n super();\n this._showSeconds = showSeconds;\n\n this._text.setText(\"\");\n this._text.setPointerEvents(\"none\");\n this._text.setAutoMeasure(false);\n this.addComponent(this._text);\n }\n\n getValue(): Date | null {\n return this._value;\n }\n\n setValue(value: Date | null): this {\n this._value = value ?? null;\n const opts: Intl.DateTimeFormatOptions = this._showSeconds\n ? { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' }\n : { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' };\n this._text.setText(value ? value.toLocaleString(undefined, opts) : \"\");\n\n return this;\n }\n}\n\nconst DateTimeRendererCallable = callable(DateTimeRenderer);\ntype DateTimeRendererCallable = DateTimeRenderer;\nexport {\n DateTimeRenderer as _DateTimeRenderer,\n DateTimeRendererCallable as DateTimeRenderer\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { Glyph as GlyphComponent } from \"~/component/display/Glyph.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A read-only renderer for cells whose value is a registry glyph name.\n *\n * Displays the value via a [`Glyph`](/api/component/display/classes/Glyph) instance.\n * The glyph follows `currentColor`, so the icon inherits the surrounding cell's\n * foreground colour for free. Caches the last value passed to\n * {@link setValue} so {@link getValue} returns `null` for an empty cell\n * instead of an empty string.\n *\n * @category Components\n */\nclass GlyphRenderer extends CellRenderer<String | null> {\n\n private _value: String | null = null;\n private _glyph: GlyphComponent | null = null;\n\n /**\n * Returns the cached glyph name, or `null` when the cell is empty.\n *\n * @returns The registry name passed to the most recent {@link setValue} call,\n * or `null` if the cell has no glyph.\n */\n getValue(): String | null {\n return this._value;\n }\n\n /**\n * Replaces the displayed glyph with one for the given registry name.\n * `null`, `undefined`, and any other falsy value remove the glyph\n * child entirely so the cell renders blank.\n *\n * @param value - Registry glyph name, or `null`/`undefined`/falsy to\n * clear the cell.\n *\n * @returns This renderer, for method chaining.\n */\n setValue(value: String | null): this {\n const next = value ? value : null;\n if (next === this._value && (next === null || this._glyph)) {\n return this;\n }\n\n if (this._glyph) {\n this.removeComponent(this._glyph);\n this._glyph = null;\n }\n\n this._value = next;\n\n if (next !== null) {\n this._glyph = new GlyphComponent(next as string);\n this._glyph.setPointerEvents(\"none\");\n this.addComponent(this._glyph);\n }\n\n return this;\n }\n}\n\nconst GlyphRendererCallable = callable(GlyphRenderer);\ntype GlyphRendererCallable = GlyphRenderer;\nexport {\n GlyphRenderer as _GlyphRenderer,\n GlyphRendererCallable as GlyphRenderer\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport type { ModelRecord } from \"~/data/ModelRecord.js\";\nimport type { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport type { FieldType } from \"~/data/Field.js\";\n\n/**\n * One selectable option for a constrained-choice (combo-box) column.\n *\n * A plain string in a {@link ColumnConfig.values} array is shorthand for\n * `{ value: s, label: s }`; an object form lets the value stored on the\n * record differ from the text shown to the user (e.g. value `\"AU\"`, label\n * `\"Australia\"`).\n *\n * @category Components\n */\nexport interface ComboOption {\n /** The value stored on the record and round-tripped by the cell. */\n value: string;\n /** Display text shown in the cell and dropdown; defaults to `value`. */\n label?: string;\n}\n\n/**\n * Expands a {@link ColumnConfig.values} array — whose entries are either\n * plain strings or {@link ComboOption} objects — into fully-populated\n * `{ value, label }` pairs, defaulting an omitted `label` to the `value`.\n *\n * Shared by the combo cell's renderer (which builds a value-to-label map)\n * and its editor (which builds the dropdown's item list) so the two never\n * disagree about what label a value renders.\n *\n * @param options - The raw `values` entries from a column config.\n * @returns One `{ value, label }` pair per entry, in the same order.\n *\n * @internal\n */\nexport function normalizeComboOptions(options: Array<ComboOption | string>): Array<Required<ComboOption>> {\n return options.map(option =>\n typeof option === \"string\"\n ? { value: option, label: option }\n : { value: option.value, label: option.label ?? option.value },\n );\n}\n\n/**\n * The cell variant a per-cell resolver may select. Extends {@link FieldType}\n * with `'combo'` for a constrained-choice (combo-box) cell.\n *\n * @category Components\n */\nexport type CellType = FieldType | 'combo';\n\n/**\n * Presentation configuration for a single table column.\n *\n * References a model field by name and carries optional display constraints.\n * Fields not mentioned in a {@link ColumnSpec} are auto-generated with default\n * sizing unless `appendUnlisted` is set to `false`.\n *\n * @category Components\n */\nexport interface ColumnConfig {\n /** The model field name this column presents. */\n field : string;\n /** Minimum width in pixels. The column cannot be dragged narrower than this value. */\n minWidth ?: number;\n /** Maximum width in pixels. The column cannot be dragged wider than this value. */\n maxWidth ?: number;\n /** When `true` the column starts hidden; the user can still reveal it via the context menu. */\n hidden ?: boolean;\n /**\n * When `true` the user cannot hide this column via the context menu;\n * the entry renders disabled with the visible checkmark, and\n * `Table.setColumnVisible(field, false)` is a no-op. Takes precedence\n * over `hidden: true` — an unhideable column never starts hidden.\n *\n * Defaults to `false`. The flag does not auto-include the column when\n * `appendUnlisted: false` excludes unlisted fields; list the column\n * explicitly in `spec.columns` to mark it unhideable.\n */\n unhideable ?: boolean;\n\n /**\n * When `true`, every cell in this column is read-only — the value\n * is displayed but the user cannot edit it. Read-only cells refuse\n * inline editing (double-click is a no-op), render with a subtle\n * grey tint sourced from `--ts-ui-table-cell-readonly-bg`, and\n * present a default cursor on hover instead of the edit affordance.\n *\n * Selection, keyboard navigation, sorting, resizing, drag-reorder,\n * and CSV / JSON export are unaffected — read-only means \"value is\n * fixed,\" not \"row is inert.\" In a {@link TreeTable}, toggling\n * expand / collapse on the tree column still works when that column\n * is read-only (expansion is not editing).\n *\n * Defaults to `false`.\n */\n readOnly ?: boolean;\n /**\n * Per-cell read-only predicate, evaluated per record on every\n * rebind. Returns `true` to mark this column's cell read-only for\n * the given record. Composes with {@link ColumnConfig.readOnly} and\n * {@link ColumnSpec.rowReadOnly} via OR — a cell is read-only when\n * ANY of the three signals says so.\n *\n * The predicate fires on every row rebind: when scrolling pulls\n * new records into the visible window, when the store emits\n * `'datachange'` (which\n * [`notifyRecordChanged`](/api/data/classes/AbstractStore#notifyRecordChanged)\n * does), or when columns are hidden / shown. It MUST be O(1) and\n * pure — read fields off `record`, return a boolean, do not call\n * back into the store, do not allocate, do not perform DOM work.\n * Memoise inside the predicate if your computation is non-trivial;\n * the table does not cache results.\n *\n * Mutating a store-owned record auto-refreshes the table; call\n * `store.notifyRecordChanged(record)` only for an unowned record or\n * to force a refresh — the predicate fires again on the next paint.\n */\n cellReadOnly ?: (record: ModelRecord) => boolean;\n /**\n * For `time` and `datetime` columns: when `true` the editor and renderer include seconds.\n * Defaults to `false` (hours and minutes only).\n */\n showSeconds ?: boolean;\n /**\n * When present, this column renders as a constrained-choice (combo-box)\n * cell regardless of the field's declared type. The inline editor offers\n * exactly these options; the cell displays each option's label for the\n * stored value, falling back to the raw value when it is not in the set.\n *\n * Each entry is either a plain string (shorthand for value === label) or\n * a {@link ComboOption} `{ value, label }` pair. The value stored on the\n * record is always the option's `value` string. An empty or absent array\n * leaves the column on its field-type-driven cell.\n */\n values ?: Array<ComboOption | string>;\n /**\n * Per-cell (per-record) variant resolver. Returns which built-in cell\n * type to render/edit for THIS row's cell, or `null` to fall back to\n * the column's field-type-driven cell. Its presence switches the\n * column onto a dynamic, per-record cell.\n *\n * The resolver fires on every row rebind — when scrolling pulls new\n * records into the visible window, when the store emits\n * `'datachange'`, or when columns are hidden / shown. It MUST be O(1)\n * and pure — read fields off `record`, return a variant, do not call\n * back into the store, do not allocate, do not perform DOM work.\n *\n * Heterogeneous columns (rows that commit different native types)\n * must declare the field `'auto'` so commits are not coerced to one\n * type.\n */\n cellType ?: (record: ModelRecord) => CellType | null;\n /**\n * Per-cell combo options, consulted only when {@link ColumnConfig.cellType}\n * returns `'combo'` for the record. Each entry is a plain string or a\n * {@link ComboOption}. Absent/empty yields an empty dropdown.\n */\n cellValues ?: (record: ModelRecord) => Array<ComboOption | string> | undefined;\n /**\n * Custom cell-renderer factory for this column. When present it overrides\n * both the `values` (combo) routing and the field-type-driven cell: every\n * cell in the column is built display-only (no inline editor) around a\n * fresh {@link CellRenderer} from this factory, so the column shows\n * computed or styled content — a link, a badge, an icon beside text —\n * instead of an editable value.\n *\n * The factory runs once per rendered cell (one per row in the visible\n * window; cells are reused as the window scrolls). Each cell's value is\n * pushed to the renderer through `setValue` on every rebind, exactly like\n * the built-in typed renderers, so keep the renderer O(1) and pure. A\n * custom-rendered cell never enters edit mode; wire the {@link Table}\n * `\"cellclick\"` event for click behaviour.\n *\n * The library ships\n * [`LinkCellRenderer`](/api/component/table/classes/LinkCellRenderer) for the\n * common \"clickable link cell\" case — pass `renderer: () => new\n * LinkCellRenderer()` and handle `\"cellclick\"`.\n */\n renderer ?: () => CellRenderer<any>;\n /**\n * Registry glyph name shown to the left of the header text.\n * Omit for no glyph; no left-side gap is reserved when absent.\n */\n headerGlyph ?: string;\n /**\n * Name of the parent-header group this column belongs to. Adjacent columns\n * sharing the same group name render under a single spanning parent header\n * cell. Non-adjacent same-named columns render as two separate parent cells.\n * Omit to leave the column ungrouped; the parent row then renders an empty\n * spanning cell above it.\n */\n group ?: string;\n /**\n * Optional background color (CSS color string) for the parent header cell.\n * All columns in a contiguous group should agree on this value; the first\n * encountered value in the run wins.\n */\n groupColor ?: string;\n\n /**\n * When `true`, marks this column required: the header renders a\n * trailing asterisk, and every row's cell in this column outlines\n * with `--ts-ui-table-cell-required-outline` while its bound value\n * is empty (`null`, `undefined`, or `''`). Composes with\n * {@link ColumnConfig.requiredPredicate} via OR for the empty-cell\n * outline, but drives the header asterisk alone — the header cell\n * has no bound record to evaluate a per-record predicate against.\n *\n * Defaults to `false`.\n */\n required ?: boolean;\n /**\n * Per-record required predicate. Returns `true` to mark this\n * column's cell required for the given record. Composes with\n * {@link ColumnConfig.required} via OR for the empty-cell outline;\n * does NOT drive the header asterisk (the header has no bound\n * record).\n *\n * Unlike {@link ColumnConfig.cellReadOnly}, this predicate fires on\n * every visible-window render pass, not just on row rebind —\n * scrolling pulls new records in, the store emitting `'datachange'`\n * (which\n * [`notifyRecordChanged`](/api/data/classes/AbstractStore#notifyRecordChanged)\n * does) triggers one, columns hiding / showing triggers one, AND a\n * plain in-place cell edit re-runs it too, since the empty-cell\n * outline must track the live value. It MUST be O(1) and pure —\n * read fields off `record`, return a boolean, do not call back\n * into the store, do not allocate, do not perform DOM work.\n *\n * This is a visual affordance only: it does not block commits or\n * integrate with store-level validation.\n */\n requiredPredicate ?: (record: ModelRecord) => boolean;\n}\n\n/**\n * Presentation specification passed to a {@link Table} to control which columns\n * are shown and how they behave.\n *\n * When omitted entirely the table auto-generates one column per model field\n * using default sizing — identical to the pre-spec behaviour.\n *\n * @example\n * ```typescript\n * new Table(store, {\n * columns: [\n * { field: 'name', minWidth: 120 },\n * { field: 'id', maxWidth: 80 },\n * ],\n * })\n * ```\n *\n * @category Components\n */\nexport interface ColumnSpec {\n /** Per-column presentation overrides, in preferred display order. */\n columns : ColumnConfig[];\n /**\n * When `true` (the default) model fields not mentioned in `columns` are\n * appended automatically after the listed columns with default sizing.\n * When `false` only the explicitly listed fields appear in the table.\n */\n appendUnlisted ?: boolean;\n /**\n * Per-row read-only predicate. Receives each visible record on every\n * rebind and returns `true` to mark every cell in that row read-only\n * for the next render pass.\n *\n * Composes with {@link ColumnConfig.readOnly} (column-level static\n * flag) and {@link ColumnConfig.cellReadOnly} (per-cell predicate):\n * a cell is read-only when ANY of the three signals says so.\n *\n * The predicate fires on every row rebind — when scrolling pulls\n * new records into the visible window, when the store emits\n * `'datachange'` (which\n * [`notifyRecordChanged`](/api/data/classes/AbstractStore#notifyRecordChanged)\n * does), or when columns are hidden / shown. It MUST be O(1) and\n * pure: read fields off `record`, return a boolean, do not call\n * back into the store, do not allocate, do not touch the DOM.\n * Memoise inside your predicate if the computation is non-trivial;\n * the framework does not cache results.\n *\n * Mutating a store-owned record auto-refreshes the table; call\n * `store.notifyRecordChanged(record)` only for an unowned record or\n * to force a refresh — the predicate fires again on the next paint.\n */\n rowReadOnly ?: (record: ModelRecord) => boolean;\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { normalizeComboOptions } from \"~/component/table/ColumnConfig.js\";\nimport type { ComboOption } from \"~/component/table/ColumnConfig.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A read-only renderer for constrained-choice (combo-box) cell values.\n *\n * Stores the option *value* (the code round-tripped to the record) but\n * displays the option *label*: a value of `\"AU\"` with an option set of\n * `[{ value: \"AU\", label: \"Australia\" }]` renders `\"Australia\"`. A value\n * not present in the option set falls back to rendering the raw value, so a\n * record holding an out-of-set code is still visible rather than blanked.\n * Caches the last value passed to {@link setValue} so {@link getValue}\n * returns `null` for an empty cell, distinct from the empty-string render.\n *\n * @category Components\n */\nclass ComboRenderer extends CellRenderer<String | null> {\n\n private _text: Text = new Text();\n private _map: Map<string, string> = new Map();\n private _value: String | null = null;\n\n /**\n * @param optionList - The column's option set; each entry is a plain\n * string (value === label) or a `{ value, label }` pair. Used to\n * build the value-to-label lookup the renderer displays.\n */\n constructor(optionList: Array<ComboOption | string>) {\n super();\n\n for (const option of normalizeComboOptions(optionList)) {\n this._map.set(option.value, option.label);\n }\n\n this._text.setText(\"\");\n this._text.setPointerEvents(\"none\");\n this._text.setAutoMeasure(false);\n this.addComponent(this._text);\n }\n\n /**\n * Returns the text component used to display the option label.\n *\n * @returns The underlying {@link Text}.\n */\n getText(): Text {\n return this._text;\n }\n\n /**\n * Returns the cached option value (the code stored on the record), or\n * `null` when the cell is empty. Returns the value, never the label, so\n * the round-trip back to the record stays honest.\n *\n * @returns The displayed option value, or `null`.\n */\n getValue(): String | null {\n return this._value;\n }\n\n /**\n * Caches the value and renders its label. `null` and `undefined` both\n * normalise to `null` and render the empty string; a value outside the\n * option set renders the raw value rather than blanking.\n *\n * @param value - The option value to display, or `null`/`undefined` to\n * clear the cell.\n */\n setValue(value: String | null): this {\n this._value = value ?? null;\n\n if (this._value === null) {\n this._text.setText(\"\");\n } else {\n const key = String(this._value);\n\n this._text.setText(this._map.get(key) ?? key);\n }\n\n return this;\n }\n\n /**\n * Rebuilds the value-to-label map from a new option set and re-renders\n * the cached value against it, so a shared renderer can be reused\n * across rows whose combo options differ.\n *\n * @param optionList - The new option set; same shape as the constructor\n * argument.\n * @returns This renderer, for method chaining.\n */\n setOptions(optionList: Array<ComboOption | string>): this {\n this._map.clear();\n\n for (const option of normalizeComboOptions(optionList)) {\n this._map.set(option.value, option.label);\n }\n\n this.setValue(this._value);\n\n return this;\n }\n}\n\nconst ComboRendererCallable = callable(ComboRenderer);\ntype ComboRendererCallable = ComboRenderer;\nexport {\n ComboRenderer as _ComboRenderer,\n ComboRendererCallable as ComboRenderer\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport type { CellEditor } from \"~/component/table/cell/editor/CellEditor.js\";\nimport type { ComboEditor } from \"~/component/table/cell/editor/Combo.js\";\nimport { BooleanEditor } from \"~/component/table/cell/editor/Boolean.js\";\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { StringRenderer } from \"~/component/table/cell/renderer/String.js\";\nimport { NumberRenderer } from \"~/component/table/cell/renderer/Number.js\";\nimport { DateRenderer } from \"~/component/table/cell/renderer/Date.js\";\nimport { TimeRenderer } from \"~/component/table/cell/renderer/Time.js\";\nimport { DateTimeRenderer } from \"~/component/table/cell/renderer/DateTime.js\";\nimport { GlyphRenderer } from \"~/component/table/cell/renderer/Glyph.js\";\nimport { ComboRenderer } from \"~/component/table/cell/renderer/Combo.js\";\nimport type { CellType, ColumnConfig, ComboOption } from \"~/component/table/ColumnConfig.js\";\nimport type { ModelRecord } from \"~/data/ModelRecord.js\";\nimport type { FieldType } from \"~/data/Field.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A table cell whose active renderer and editor vary per bound record,\n * driven by {@link ColumnConfig.cellType}.\n *\n * One `DynamicCell` instance lives for the life of its pool slot — every\n * built-in variant it can show (`string`, `number`, `date`, `time`,\n * `datetime`, `glyph`, `combo`, `boolean`) is built lazily and cached, then\n * swapped into view on {@link DynamicCell.bindRecord} by making it the\n * cell's active renderer. This keeps the slot's geometry cache,\n * editor-pool wiring, commit wiring, read-only state, and focus behaviour\n * valid across rebinds that change the resolved variant, unlike replacing\n * the cell instance itself.\n *\n * A `'boolean'` row reuses the checkbox-as-renderer pattern from\n * {@link BooleanCell}: a {@link BooleanEditor} is placed in the renderer\n * slot and commits immediately on toggle, with no separate edit cycle. A\n * `'combo'` row shares one pooled `combo:<field>` editor across every combo\n * row in the column; {@link ColumnConfig.cellValues} supplies that row's\n * option set, injected into the pooled editor just before it opens.\n *\n * @category Components\n */\nclass DynamicCell extends Cell<any> {\n\n private _field: string;\n private _columnType: FieldType;\n private _showSeconds: boolean;\n private _cellType: ((record: ModelRecord) => CellType | null) | undefined;\n private _cellValues: ((record: ModelRecord) => Array<ComboOption | string> | undefined) | undefined;\n private _renderers: Map<CellType, CellRenderer<any>> = new Map();\n private _activeType: CellType = 'string';\n private _currentOptions: Array<ComboOption | string> = [];\n private _checkbox: BooleanEditor | null = null;\n // Set by `ensureRenderer` / `ensureCheckbox` immediately before the call\n // to `setActiveRenderer` that consumes it, to say whether the returned\n // variant was just constructed (true) or already cached (false).\n private _justCreated: boolean = false;\n\n /**\n * @param field - The model field name this column presents.\n * @param columnType - The column's declared field type; the fallback\n * variant when {@link ColumnConfig.cellType} returns `null` for a record.\n * @param config - The column's config, supplying `cellType`, `cellValues`,\n * and `showSeconds`.\n */\n constructor(field: string, columnType: FieldType, config: ColumnConfig) {\n const placeholder = new StringRenderer();\n\n super(\"td\", placeholder);\n\n this._field = field;\n this._columnType = columnType;\n this._showSeconds = config.showSeconds ?? false;\n this._cellType = config.cellType;\n this._cellValues = config.cellValues;\n\n this._renderers.set('string', placeholder);\n }\n\n /**\n * Resolves the active variant for `record`, swaps to the matching\n * cached renderer (constructing it on first use), and pushes the\n * record's value into it.\n *\n * @param record - The record now bound to this cell's pool slot.\n */\n bindRecord(record: ModelRecord): void {\n const resolved = this._cellType?.(record) ?? this._columnType;\n\n this._activeType = resolved;\n\n if (resolved === 'boolean') {\n const checkbox = this.ensureCheckbox();\n\n this.setActiveRenderer(checkbox as unknown as CellRenderer<any>, this._justCreated);\n checkbox.setValue(record.get(this._field));\n\n return;\n }\n\n if (resolved === 'combo') {\n this._currentOptions = this._cellValues?.(record) ?? [];\n\n const renderer = this.ensureRenderer('combo') as ComboRenderer;\n renderer.setOptions(this._currentOptions);\n this.setActiveRenderer(renderer, this._justCreated);\n renderer.setValue(record.get(this._field));\n\n return;\n }\n\n const renderer = this.ensureRenderer(resolved);\n\n this.setActiveRenderer(renderer, this._justCreated);\n renderer.setValue(record.get(this._field));\n }\n\n /**\n * Returns the pool key for the active variant's shared editor.\n *\n * @returns A key registered on the {@link CellEditorPool}, or `null` for\n * variants with no pooled editor (`boolean`, `glyph`).\n */\n getEditorKey(): string | null {\n switch (this._activeType) {\n case 'number':\n return 'number';\n case 'date':\n return 'date';\n case 'time':\n return this._showSeconds ? 'time:seconds' : 'time';\n case 'datetime':\n return this._showSeconds ? 'datetime:seconds' : 'datetime';\n case 'combo':\n return `combo:${this._field}`;\n case 'boolean':\n case 'glyph':\n return null;\n default:\n return 'string'; // 'string' | 'auto'\n }\n }\n\n /**\n * Toggles the checkbox directly when the active variant is `boolean`\n * (mirroring {@link BooleanCell.startEdit}, which has no separate edit\n * cycle); otherwise defers to the base pooled-editor flow.\n */\n startEdit(): void {\n if (this._activeType === 'boolean') {\n if (this.isReadOnly()) {\n return;\n }\n\n this._checkbox?.toggle();\n\n return;\n }\n\n super.startEdit();\n }\n\n /**\n * Marks the cell read-only and, when the active variant is `boolean`,\n * also disables the checkbox so it rejects toggles.\n *\n * @param value - `true` to mark read-only, `false` to restore.\n * @returns This cell, for method chaining.\n */\n setReadOnly(value: boolean): this {\n super.setReadOnly(value);\n\n if (this._activeType === 'boolean') {\n this._checkbox?.setReadOnly(value);\n }\n\n return this;\n }\n\n /**\n * Pushes the current record's combo options into the pooled editor\n * before it receives a value, so a shared `combo:<field>` editor\n * reflects this row's option set rather than whichever row last opened it.\n *\n * @param editor - The editor about to be shown.\n */\n protected prepareEditor(editor: CellEditor<any>): void {\n if (this._activeType === 'combo') {\n (editor as ComboEditor).setOptions(this._currentOptions);\n }\n }\n\n /**\n * Returns the cached renderer for `type`, lazily constructing it on\n * first use and recording whether it was just constructed via\n * `_justCreated`.\n *\n * @param type - The built-in variant to fetch a renderer for.\n * @returns The cached (or freshly built) renderer for `type`.\n */\n private ensureRenderer(type: CellType): CellRenderer<any> {\n const cached = this._renderers.get(type);\n\n if (cached) {\n this._justCreated = false;\n\n return cached;\n }\n\n const renderer = DynamicCell.buildRenderer(type, this._showSeconds);\n\n this._renderers.set(type, renderer);\n this._justCreated = true;\n\n return renderer;\n }\n\n /**\n * Constructs the renderer for a built-in variant. `glyph` and any\n * unrecognised variant fall back to a {@link StringRenderer}.\n *\n * @param type - The variant to construct a renderer for.\n * @param showSeconds - Forwarded to `time`/`datetime` renderers.\n * @returns A freshly constructed renderer for `type`.\n */\n private static buildRenderer(type: CellType, showSeconds: boolean): CellRenderer<any> {\n switch (type) {\n case 'number':\n return new NumberRenderer();\n case 'date':\n return new DateRenderer();\n case 'time':\n return new TimeRenderer(showSeconds);\n case 'datetime':\n return new DateTimeRenderer(showSeconds);\n case 'combo':\n return new ComboRenderer([]);\n case 'glyph':\n return new GlyphRenderer();\n default:\n return new StringRenderer(); // 'string' | 'auto'\n }\n }\n\n /**\n * Returns the cached checkbox editor, lazily constructing it on first\n * use and wiring its `change` event to commit immediately (mirroring\n * {@link BooleanCell}'s immediate-commit contract). Records whether it\n * was just constructed via `_justCreated`.\n *\n * @returns The cached (or freshly built) checkbox editor.\n */\n private ensureCheckbox(): BooleanEditor {\n if (this._checkbox) {\n this._justCreated = false;\n\n return this._checkbox;\n }\n\n const checkbox = new BooleanEditor();\n\n checkbox.on(\"change\", (value) => this.emitCommit(value));\n\n this._checkbox = checkbox;\n this._justCreated = true;\n\n return checkbox;\n }\n\n /**\n * Fires the cell's `\"commit\"` event with `value`. Extracted so the\n * checkbox's `change` listener has a stable bound method to call.\n *\n * @param value - The value to commit.\n */\n private emitCommit(value: any): void {\n this.emit(\"commit\", value);\n }\n}\n\nconst DynamicCellCallable = callable(DynamicCell);\ntype DynamicCellCallable = DynamicCell;\nexport {\n DynamicCell as _DynamicCell,\n DynamicCellCallable as DynamicCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { StringRenderer } from \"~/component/table/cell/renderer/String.js\";\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A plain string-rendering cell with no editor.\n *\n * Used as the fallback cell type for fields whose type is not explicitly mapped,\n * and as the base for {@link HeaderCell}.\n *\n * @category Components\n */\nclass DefaultCell extends Cell<String | null> {\n\n constructor(tag?: string) {\n let renderer = new StringRenderer();\n\n super(tag || \"td\", renderer);\n }\n\n /**\n * Returns the renderer cast to StringRenderer.\n *\n * @returns The {@link StringRenderer} for this cell.\n */\n getRenderer() {\n return <StringRenderer>super.getRenderer();\n }\n\n /**\n * Sets the displayed text value on the string renderer. `null` and\n * `undefined` render the cell as blank.\n *\n * @param value - The string value to display, or `null`/`undefined`\n * to clear the cell.\n */\n setValue(value: String | null): this {\n this.getRenderer().setValue(value);\n\n return this;\n }\n}\n\nconst DefaultCellCallable = callable(DefaultCell);\ntype DefaultCellCallable = DefaultCell;\nexport {\n DefaultCell as _DefaultCell,\n DefaultCellCallable as DefaultCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport { StringRenderer } from \"~/component/table/cell/renderer/String.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A table cell for string values.\n *\n * Uses a [`StringRenderer`](/api/component/table/classes/StringRenderer) for display and borrows\n * a shared [`StringEditor`](/api/component/table/classes/StringEditor) from the body's\n * {@link CellEditorPool} on edit.\n *\n * @category Components\n */\nclass StringCell extends Cell<String | null> {\n\n constructor() {\n let renderer = new StringRenderer();\n\n super(\"td\", renderer);\n }\n\n /**\n * Returns the pool key for the shared {@link StringEditor}.\n *\n * @returns The string `\"string\"`.\n */\n getEditorKey(): string {\n return \"string\";\n }\n\n /**\n * Sets the displayed text value on the renderer. `null` and\n * `undefined` render the cell as blank.\n *\n * @param value - The string value to display, or `null`/`undefined`\n * to clear the cell.\n */\n setValue(value: String | null): this {\n this.getRenderer().setValue(value);\n\n return this;\n }\n}\n\nconst StringCellCallable = callable(StringCell);\ntype StringCellCallable = StringCell;\nexport {\n StringCell as _StringCell,\n StringCellCallable as StringCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { BooleanEditor } from \"~/component/table/cell/editor/Boolean.js\";\nimport { Cell, CellEvent } from \"~/component/table/cell/Cell.js\";\nimport { FillType } from \"~/layout/FillType.js\";\nimport { AnchorType } from \"~/layout/AnchorType.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A table cell for boolean values, rendered as a checkbox.\n *\n * Unlike text-based cells, the {@link BooleanEditor} doubles as the renderer; there is\n * no separate renderer or edit-mode toggle — the checkbox is always interactive.\n *\n * @category Components\n */\nclass BooleanCell extends Cell<Boolean | null> {\n\n private _checkbox: BooleanEditor;\n\n constructor() {\n let editor = new BooleanEditor();\n\n super(\"td\",\n editor,\n undefined, {\n fill: FillType.NONE,\n anchor: AnchorType.CENTER\n },\n undefined\n );\n\n this._checkbox = editor;\n }\n\n /**\n * Registers a listener for one of this cell's events. The `\"commit\"`\n * listener is wired directly to the checkbox's `\"change\"` event rather\n * than the base {@link Cell} listener bag.\n *\n * @param event - The event name.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This cell, for method chaining.\n *\n * @remarks Overrides the base class because BooleanCell has no separate\n * edit/commit cycle; changes are committed immediately on each checkbox\n * interaction. Routed through the cached editor reference rather than\n * `getRenderer()` so the wiring survives a\n * [`TreeCellRenderer`](/api/component/table/classes/TreeCellRenderer)\n * wrap when the column is the tree column. The `\"commit\"` listener only\n * fires for user interaction, which always lands on a concrete\n * `true`/`false`; the `null` branch is included for consistency with the\n * editor's signature but is never emitted by the editor.\n */\n on(event: \"commit\", listener: (value: Boolean | null) => void): this;\n on(event: \"editend\", listener: () => void): this;\n on(event: CellEvent, listener: Function): this {\n if (event === \"commit\") {\n this._checkbox.on(\"change\", listener as (value: Boolean | null) => void);\n\n return this;\n }\n\n return super.on(event, listener as () => void);\n }\n\n /**\n * Removes a previously registered listener. The `\"commit\"` listener is\n * detached from the checkbox's `\"change\"` event; other events delegate\n * to the base {@link Cell}.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This cell, for method chaining.\n */\n off(event: CellEvent, listener: Function): this {\n if (event === \"commit\") {\n this._checkbox.off(\"change\", listener);\n\n return this;\n }\n\n return super.off(event, listener);\n }\n\n /**\n * Marks the cell read-only and propagates the flag to the checkbox so it\n * rejects user toggles. The base {@link Cell.setReadOnly} only handles the\n * cell chrome and the pooled-editor lifecycle; a BooleanCell's checkbox is\n * its always-on renderer, so it must be disabled explicitly or it would\n * stay interactive in a read-only column.\n *\n * @param value - `true` to lock the checkbox, `false` to restore it.\n *\n * @returns This cell, for method chaining.\n */\n setReadOnly(value: boolean): this {\n super.setReadOnly(value);\n this._checkbox.setReadOnly(value);\n\n return this;\n }\n\n /**\n * Toggles the checkbox value and fires the commit callback. A no-op when\n * the cell is read-only — the base {@link Cell.startEdit} self-guards on\n * `isReadOnly`, and this override (reached via the renderer's dblclick and\n * the body's keyboard activation) must do the same so a locked column never\n * toggles.\n */\n startEdit() {\n if (this.isReadOnly()) {\n return;\n }\n\n this._checkbox.toggle();\n }\n\n /**\n * Sets the checkbox checked state. `null` and `undefined` put the\n * checkbox into the indeterminate (mixed) state.\n *\n * @param value - The boolean value to reflect on the checkbox, or\n * `null`/`undefined` to render the indeterminate state.\n */\n setValue(value: Boolean | null): this {\n this.getRenderer().setValue(value);\n\n return this;\n }\n}\n\nconst BooleanCellCallable = callable(BooleanCell);\ntype BooleanCellCallable = BooleanCell;\nexport {\n BooleanCell as _BooleanCell,\n BooleanCellCallable as BooleanCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport { NumberRenderer } from \"~/component/table/cell/renderer/Number.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A table cell for numeric values.\n *\n * Uses a [`NumberRenderer`](/api/component/table/classes/NumberRenderer) for display and borrows\n * a shared [`NumberEditor`](/api/component/table/classes/NumberEditor) from the body's\n * {@link CellEditorPool} on edit.\n *\n * @category Components\n */\nclass NumberCell extends Cell<Number | null> {\n\n constructor() {\n let renderer = new NumberRenderer();\n\n super(\"td\", renderer);\n }\n\n /**\n * Returns the pool key for the shared {@link NumberEditor}.\n *\n * @returns The string `\"number\"`.\n */\n getEditorKey(): string {\n return \"number\";\n }\n\n /**\n * Sets the displayed numeric value on the renderer. `null` and\n * `undefined` render the cell as blank.\n *\n * @param value - The numeric value to display, or `null`/`undefined`\n * to clear the cell.\n */\n setValue(value: Number | null): this {\n this.getRenderer().setValue(value);\n\n return this;\n }\n}\n\nconst NumberCellCallable = callable(NumberCell);\ntype NumberCellCallable = NumberCell;\nexport {\n NumberCell as _NumberCell,\n NumberCellCallable as NumberCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport { DateRenderer } from \"~/component/table/cell/renderer/Date.js\";\nimport { DateEditor } from \"~/component/table/cell/editor/Date.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A table cell for date values.\n *\n * Uses a [`DateRenderer`](/api/component/table/classes/DateRenderer) for display and borrows a\n * shared [`DateEditor`](/api/component/table/classes/DateEditor) from the body's\n * {@link CellEditorPool} on edit. Committing an empty field writes null; committing an\n * unparseable value reverts to the previous value instead of writing null.\n */\nclass DateCell extends Cell<Date | null> {\n\n constructor() {\n let renderer = new DateRenderer();\n\n super(\"td\", renderer);\n }\n\n /**\n * Returns the pool key for the shared {@link DateEditor}.\n *\n * @returns The string `\"date\"`.\n */\n getEditorKey(): string {\n return \"date\";\n }\n\n /**\n * Sets the displayed date value on the renderer.\n *\n * @param value - The date to display, or null to clear.\n */\n setValue(value: Date | null): this {\n this.getRenderer().setValue(value);\n\n return this;\n }\n\n /**\n * Commits the active edit, but reverts when the user typed something the editor could not\n * parse so the underlying record is not blanked.\n *\n * @returns This cell, for method chaining.\n */\n commitEdit(): this {\n const editor = this._activeEditor as DateEditor | null;\n\n if (editor && !editor.isEmpty() && editor.getValue() === null) {\n this.cancelEdit();\n\n return this;\n }\n\n super.commitEdit();\n\n return this;\n }\n}\n\nconst DateCellCallable = callable(DateCell);\ntype DateCellCallable = DateCell;\nexport {\n DateCell as _DateCell,\n DateCellCallable as DateCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport { TimeRenderer } from \"~/component/table/cell/renderer/Time.js\";\nimport { TimeEditor } from \"~/component/table/cell/editor/Time.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A table cell for time values.\n *\n * Uses a [`TimeRenderer`](/api/component/table/classes/TimeRenderer) for display and borrows a\n * shared [`TimeEditor`](/api/component/table/classes/TimeEditor) from the body's\n * {@link CellEditorPool} on edit. Committing an empty field writes null; committing an\n * unparseable value reverts to the previous value instead of writing null.\n */\nclass TimeCell extends Cell<Date | null> {\n\n private _showSeconds: boolean;\n\n constructor(showSeconds: boolean = false) {\n let renderer = new TimeRenderer(showSeconds);\n\n super(\"td\", renderer);\n\n this._showSeconds = showSeconds;\n }\n\n /**\n * Returns the pool key for the shared {@link TimeEditor}, differentiating the seconds variant.\n *\n * @returns `\"time:seconds\"` when the cell renders seconds, otherwise `\"time\"`.\n */\n getEditorKey(): string {\n return this._showSeconds ? \"time:seconds\" : \"time\";\n }\n\n /**\n * Sets the displayed time value on the renderer.\n *\n * @param value - The time to display, or null to clear.\n */\n setValue(value: Date | null): this {\n this.getRenderer().setValue(value);\n\n return this;\n }\n\n /**\n * Commits the active edit, but reverts when the user typed something the editor could not\n * parse so the underlying record is not blanked.\n *\n * @returns This cell, for method chaining.\n */\n commitEdit(): this {\n const editor = this._activeEditor as TimeEditor | null;\n\n if (editor && !editor.isEmpty() && editor.getValue() === null) {\n this.cancelEdit();\n\n return this;\n }\n\n super.commitEdit();\n\n return this;\n }\n}\n\nconst TimeCellCallable = callable(TimeCell);\ntype TimeCellCallable = TimeCell;\nexport {\n TimeCell as _TimeCell,\n TimeCellCallable as TimeCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport { DateTimeRenderer } from \"~/component/table/cell/renderer/DateTime.js\";\nimport { DateTimeEditor } from \"~/component/table/cell/editor/DateTime.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A table cell for date-time values.\n *\n * Uses a [`DateTimeRenderer`](/api/component/table/classes/DateTimeRenderer) for display and\n * borrows a shared [`DateTimeEditor`](/api/component/table/classes/DateTimeEditor) from the\n * body's {@link CellEditorPool} on edit. Committing an empty field writes null; committing an\n * unparseable value reverts to the previous value instead of writing null.\n */\nclass DateTimeCell extends Cell<Date | null> {\n\n private _showSeconds: boolean;\n\n constructor(showSeconds: boolean = false) {\n let renderer = new DateTimeRenderer(showSeconds);\n\n super(\"td\", renderer);\n\n this._showSeconds = showSeconds;\n }\n\n /**\n * Returns the pool key for the shared {@link DateTimeEditor}, differentiating the seconds variant.\n *\n * @returns `\"datetime:seconds\"` when the cell renders seconds, otherwise `\"datetime\"`.\n */\n getEditorKey(): string {\n return this._showSeconds ? \"datetime:seconds\" : \"datetime\";\n }\n\n /**\n * Sets the displayed date-time value on the renderer.\n *\n * @param value - The date-time to display, or null to clear.\n */\n setValue(value: Date | null): this {\n this.getRenderer().setValue(value);\n\n return this;\n }\n\n /**\n * Commits the active edit, but reverts when the user typed something the editor could not\n * parse so the underlying record is not blanked.\n *\n * @returns This cell, for method chaining.\n */\n commitEdit(): this {\n const editor = this._activeEditor as DateTimeEditor | null;\n\n if (editor && !editor.isEmpty() && editor.getValue() === null) {\n this.cancelEdit();\n\n return this;\n }\n\n super.commitEdit();\n\n return this;\n }\n}\n\nconst DateTimeCellCallable = callable(DateTimeCell);\ntype DateTimeCellCallable = DateTimeCell;\nexport {\n DateTimeCell as _DateTimeCell,\n DateTimeCellCallable as DateTimeCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport { GlyphRenderer } from \"~/component/table/cell/renderer/Glyph.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A read-only table cell whose value is a registry glyph name.\n *\n * Uses a [`GlyphRenderer`](/api/component/table/classes/GlyphRenderer) for display.\n * No editor counterpart — editing a glyph by typing into a cell is not supported.\n *\n * @category Components\n */\nclass GlyphCell extends Cell<String | null> {\n\n constructor() {\n let renderer = new GlyphRenderer();\n\n super(\"td\", renderer);\n }\n\n /**\n * Sets the displayed glyph name on the renderer. `null`, `undefined`,\n * and any falsy value remove the glyph child so the cell renders\n * blank.\n *\n * @param value - The registry glyph name to display, or\n * `null`/`undefined`/falsy to clear the cell.\n */\n setValue(value: String | null): this {\n this.getRenderer().setValue(value);\n\n return this;\n }\n}\n\nconst GlyphCellCallable = callable(GlyphCell);\ntype GlyphCellCallable = GlyphCell;\nexport {\n GlyphCell as _GlyphCell,\n GlyphCellCallable as GlyphCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport { ComboRenderer } from \"~/component/table/cell/renderer/Combo.js\";\nimport type { ComboOption } from \"~/component/table/ColumnConfig.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A table cell for constrained-choice (combo-box) columns.\n *\n * Activated when a column declares `ColumnConfig.values`, regardless of the\n * field's declared type. Displays the chosen option's label via a\n * [`ComboRenderer`](/api/component/table/classes/ComboRenderer) and borrows a\n * per-column [`ComboEditor`](/api/component/table/classes/ComboEditor) from the\n * body's {@link CellEditorPool} on edit. The editor key is namespaced by field\n * (`combo:<field>`) so each combo column keeps its own option set rather than\n * sharing one pooled editor.\n *\n * @category Components\n */\nclass ComboCell extends Cell<String | null> {\n\n private _field: string;\n\n /**\n * @param field - The model field name this column presents; used to\n * namespace the pooled editor key.\n * @param optionList - The column's option set, forwarded to the renderer\n * so it can map stored values to display labels.\n */\n constructor(field: string, optionList: Array<ComboOption | string>) {\n super(\"td\", new ComboRenderer(optionList));\n\n this._field = field;\n }\n\n /**\n * Returns the per-column pool key for this cell's shared\n * [`ComboEditor`](/api/component/table/classes/ComboEditor).\n *\n * @returns The string `` `combo:${field}` ``.\n */\n getEditorKey(): string {\n return `combo:${this._field}`;\n }\n\n /**\n * Sets the displayed value on the renderer, which maps it to the\n * matching option label. `null` and `undefined` render the cell blank.\n *\n * @param value - The option value to display, or `null`/`undefined` to\n * clear the cell.\n */\n setValue(value: String | null): this {\n this.getRenderer().setValue(value);\n\n return this;\n }\n}\n\nconst ComboCellCallable = callable(ComboCell);\ntype ComboCellCallable = ComboCell;\nexport {\n ComboCell as _ComboCell,\n ComboCellCallable as ComboCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { Absolute } from \"~/layout/Absolute.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { caret_down } from \"~/glyphs/solid/caret_down.js\";\nimport { caret_right } from \"~/glyphs/solid/caret_right.js\";\n\nGlyph.register(caret_down, caret_right);\n\n/** Width in pixels reserved for the expand/collapse toggle glyph. */\nexport const TOGGLE_WIDTH = 20;\n\n/** Default pixels of indentation added per depth level. */\nexport const DEFAULT_INDENT_PX = 16;\n\n/**\n * A {@link CellRenderer} wrapper that prepends an indent column and an\n * expand/collapse toggle in front of a delegate renderer.\n *\n * The delegate is the typed renderer that would have been used for the\n * column's field type ({@link StringRenderer},\n * [`NumberRenderer`](/api/component/table/classes/NumberRenderer), …). It is\n * adopted as the tree renderer's only data-bearing child and handles\n * `getValue` / `setValue` unchanged. The toggle is a {@link Glyph}\n * rebuilt on each {@link setTreeState} call, mirroring the swap pattern\n * used by [`TreeRow.setRowData`](/api/component/tree/classes/TreeRow#setrowdata).\n *\n * Layout is absolute: the toggle (when present) is placed at\n * `depth * indentPx`, and the delegate immediately to its right —\n * filling the remaining width. The renderer's own insets are zero; the\n * delegate carries the cell padding inherited from its own\n * {@link CellRenderer} base.\n *\n * Toggle click routing is intentionally absent here. The host\n * `TreeBody` owns one subtree-click listener for the whole body; it\n * inspects the click target and, when it matches a pool row's toggle\n * element, calls {@link TreeBody.setExpanded} directly. This keeps\n * tree-cell renderers free of click handlers and matches the way the\n * existing `Tree` component routes toggle clicks.\n *\n * @category Components\n */\nclass TreeCellRenderer<T> extends CellRenderer<T> {\n\n declare private _delegate: CellRenderer<T>;\n declare private _indentPx: number;\n private _toggle: Glyph | null = null;\n private _depth: number = 0;\n private _hasChildren: boolean = false;\n private _expanded: boolean = false;\n\n /**\n * Constructs a tree-cell renderer wrapping the given delegate.\n *\n * @param delegate - The typed renderer that draws the cell value to\n * the right of the toggle. Adopted as a child of this renderer; do\n * not also add it to another parent.\n * @param indentPx - Pixels of indentation per depth level. Defaults\n * to 16 px.\n */\n constructor(delegate: CellRenderer<T>, indentPx: number = DEFAULT_INDENT_PX) {\n super();\n\n this._delegate = delegate;\n this._indentPx = indentPx;\n\n // Override CellRenderer's default Fit layout — we place the\n // toggle and delegate at explicit x positions in `doLayout`.\n this.setLayoutManager(new Absolute());\n\n // Zero out the cell padding that CellRenderer's base set during\n // super(); the delegate is itself a CellRenderer and owns the\n // padding, so doubling it would over-indent the content by one\n // cell-padding width per row. Call the inherited setter through\n // `super` to bypass our own forwarding override.\n super.setInsets(new Insets(0, 0, 0, 0));\n\n this.addComponent(delegate);\n }\n\n /**\n * Returns the delegate renderer that draws the cell's value.\n *\n * @returns The wrapped {@link CellRenderer}.\n */\n getDelegate(): CellRenderer<T> {\n return this._delegate;\n }\n\n /**\n * Returns the toggle glyph currently rendered, or `null` for a leaf\n * row. The host `TreeBody` reads this to dispatch toggle clicks\n * from its subtree-click listener.\n *\n * @returns The toggle {@link Glyph}, or `null` when the row has no children.\n */\n getToggle(): Glyph | null {\n return this._toggle;\n }\n\n /**\n * Returns the current depth level (0 = root).\n *\n * @returns The zero-based depth.\n */\n getDepth(): number {\n return this._depth;\n }\n\n /**\n * Returns the x offset at which this renderer's actual content\n * (the wrapped delegate) begins — `depth * indentPx + TOGGLE_WIDTH`.\n * Used by the owning {@link Cell} on `doLayout` to keep the\n * editor visually aligned with the delegate when the cell enters\n * edit mode.\n *\n * @returns The pixels reserved on the left for the indent +\n * toggle column.\n */\n getContentX(): number {\n return this._depth * this._indentPx + TOGGLE_WIDTH;\n }\n\n /**\n * Returns the value rendered by the delegate.\n *\n * @returns The current cell value.\n */\n getValue(): T {\n return this._delegate.getValue();\n }\n\n /**\n * Sets the value rendered by the delegate.\n *\n * @param value - The cell value to display.\n */\n setValue(value: T): this {\n this._delegate.setValue(value);\n\n return this;\n }\n\n /**\n * Updates the tree state for this slot — depth, whether the bound\n * record has children, and whether it is currently expanded. The\n * toggle glyph is created or removed as needed, and the renderer's\n * own `doLayout` runs so the toggle and delegate snap to their new\n * positions.\n *\n * Idempotent — a call with the same triple is a no-op.\n *\n * @param depth - Zero-based nesting depth.\n * @param hasChildren - Whether the bound record has children\n * (controls whether a toggle is rendered).\n * @param expanded - Whether the bound record is currently expanded\n * (controls the toggle glyph: `caret-down` vs `caret-right`).\n *\n * @returns This renderer, for method chaining.\n */\n setTreeState(depth: number, hasChildren: boolean, expanded: boolean): this {\n if (this._depth === depth\n && this._hasChildren === hasChildren\n && this._expanded === expanded) {\n return this;\n }\n\n this._depth = depth;\n this._hasChildren = hasChildren;\n this._expanded = expanded;\n\n this.refreshToggle();\n this.doLayout();\n\n return this;\n }\n\n /**\n * Forwards insets writes to the delegate. The tree-cell wrapper\n * itself stays insets-zero — its only role is to inject indent +\n * toggle space; the delegate owns the cell-padding contract that\n * CellRenderer's base assumes.\n *\n * The first call lands during `super()` of this renderer's\n * constructor (`CellRenderer`'s `applyPadding` runs before our\n * `_delegate` field is assigned). That call is swallowed by the\n * optional chain; subsequent calls — including theme-change\n * re-applications and explicit consumer writes — propagate to\n * the delegate.\n *\n * @param insets - The insets to apply to the delegate.\n */\n setInsets(insets: Insets): this {\n this._delegate?.setInsets(insets);\n\n return this;\n }\n\n /**\n * Rebuilds the toggle glyph from `_hasChildren` and `_expanded`.\n * A leaf row has no toggle. A branch row swaps in a fresh\n * `caret-down` (expanded) or `caret-right` (collapsed) glyph —\n * matches [`TreeRow.setRowData`](/api/component/tree/classes/TreeRow#setrowdata)'s\n * swap pattern (no in-place name mutation API on `Glyph`).\n */\n private refreshToggle(): void {\n if (this._toggle) {\n this.removeComponent(this._toggle);\n this._toggle = null;\n }\n\n if (!this._hasChildren) {\n return;\n }\n\n const toggle = new Glyph(this._expanded ? \"caret-down\" : \"caret-right\");\n\n toggle.setCursor(\"pointer\");\n toggle.clearInsets();\n toggle.getAria().setHidden(true);\n\n this._toggle = toggle;\n this.addComponent(toggle);\n }\n\n /**\n * Positions the toggle (when present) at `depth * indentPx` and\n * places the delegate immediately to its right, filling the\n * remaining width. Runs the inherited `super.doLayout` so the\n * Absolute layout manager commits the bounds we set on the\n * children.\n *\n * @returns This renderer, for method chaining.\n */\n doLayout(): this {\n const width = this.getWidth() || 0;\n const height = this.getHeight() || 0;\n const indent = this._depth * this._indentPx;\n\n if (this._toggle) {\n // The caret is a rigid SVG Glyph (min/max pinned to its 16×16\n // preferredSize), so it cannot fill the taller row — centre it\n // geometrically instead of relying on line-height (a no-op for SVG).\n const glyphHeight = this._toggle.getPreferredSize()?.height ?? height;\n this._toggle.setAutoCommitStyle(false);\n this._toggle.setX(indent);\n this._toggle.setY(Math.max(0, (height - glyphHeight) / 2));\n this._toggle.setWidth(TOGGLE_WIDTH);\n this._toggle.setHeight(glyphHeight);\n this._toggle.setAutoCommitStyle(true);\n }\n\n const delegateX = indent + TOGGLE_WIDTH;\n const delegateW = Math.max(0, width - delegateX);\n\n this._delegate.setAutoCommitStyle(false);\n this._delegate.setX(delegateX);\n this._delegate.setY(0);\n this._delegate.setWidth(delegateW);\n this._delegate.setHeight(height);\n this._delegate.setAutoCommitStyle(true);\n\n // The inherited `super.doLayout` (CellRenderer → Component →\n // Absolute layout manager) commits each child's bounds via\n // `LayoutManager.commitBounds`, which already calls\n // `component.doLayout()` on every placed child. The delegate\n // and the toggle therefore receive their own `doLayout` once\n // during this call — no need to call `_delegate.doLayout()`\n // again here.\n super.doLayout();\n\n return this;\n }\n}\n\nconst TreeCellRendererCallable = callable(TreeCellRenderer);\ntype TreeCellRendererCallable<T> = TreeCellRenderer<T>;\nexport {\n TreeCellRenderer as _TreeCellRenderer,\n TreeCellRendererCallable as TreeCellRenderer\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport { AbstractModel } from \"~/data/AbstractModel.js\";\nimport { Field } from \"~/data/Field.js\";\nimport { ModelRecord } from \"~/data/ModelRecord.js\";\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport { DynamicCell } from \"~/component/table/cell/Dynamic.js\";\nimport { DefaultCell } from \"~/component/table/cell/Default.js\";\nimport { StringCell } from \"~/component/table/cell/String.js\";\nimport { BooleanCell } from \"~/component/table/cell/Boolean.js\";\nimport { NumberCell } from \"~/component/table/cell/Number.js\";\nimport { DateCell } from \"~/component/table/cell/Date.js\";\nimport { TimeCell } from \"~/component/table/cell/Time.js\";\nimport { DateTimeCell } from \"~/component/table/cell/DateTime.js\";\nimport { GlyphCell } from \"~/component/table/cell/Glyph.js\";\nimport { ComboCell } from \"~/component/table/cell/Combo.js\";\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { TreeCellRenderer } from \"~/component/table/cell/renderer/TreeCell.js\";\nimport type { ColumnConfig } from \"~/component/table/ColumnConfig.js\";\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A single data row in the table, rendered as a `<tr>` element.\n *\n * Creates one typed cell ({@link StringCell}, {@link NumberCell}, {@link BooleanCell},\n * or {@link DefaultCell}) per model field and binds each cell's commit callback to the\n * corresponding field on the bound {@link ModelRecord}.\n *\n * Re-exported as `TableRow` from the package barrel.\n *\n * @category Components\n */\nclass Row extends Component {\n\n private _model?: AbstractModel;\n private _data?: ModelRecord;\n private _onCellCommit?: (record: ModelRecord) => void;\n private _fieldNames: string[] = [];\n private _treeCell: Cell<any> | null = null;\n private _stripe: boolean = false;\n\n constructor(\n model?: AbstractModel,\n data?: ModelRecord,\n hiddenColumns: Set<string> = new Set(),\n columnConfigs: Map<string, ColumnConfig> = new Map(),\n onCellCommit?: (record: ModelRecord) => void,\n treeFieldName?: string,\n ) {\n super({ tag: \"tr\" });\n\n this.getAria().setRole(\"row\");\n\n this._model = model;\n this._data = data;\n this._onCellCommit = onCellCommit;\n\n if (this._model) {\n let fields = this._model.getFields()\n .filter(f => !hiddenColumns.has(f.getName()))\n .sort((f1, f2) => f1.getOrder() - f2.getOrder());\n\n this._fieldNames = fields.map(f => f.getName());\n\n for (let idx in fields) {\n let field = fields[idx];\n let cell = Row.createCellForField(field, columnConfigs);\n\n this.bindCell(cell, this._data, field.getName());\n cell.on(\"commit\", (newValue) => {\n if (this._data) {\n this._data.set(field.getName(), newValue);\n this._onCellCommit?.(this._data);\n }\n this.updateVisualState();\n });\n\n // Tint the cell with the column's `groupColor` so a body\n // cell visually belongs to the same group as the parent\n // header above it. `Cell` base only re-applies the\n // border on theme change, so this background sticks.\n const groupColor = columnConfigs.get(field.getName())?.groupColor;\n if (groupColor) {\n cell.setBaseBackground(groupColor);\n }\n\n // Read-only wins over groupColor: this write lands\n // after the groupColor block so the read-only tint\n // overrides any group tint and the cell refuses inline\n // editing.\n const readOnly = columnConfigs.get(field.getName())?.readOnly;\n if (readOnly) {\n cell.setReadOnly(true);\n }\n\n // For the tree column, wrap the typed renderer in a\n // `TreeCellRenderer` so the cell draws an indent + an\n // expand/collapse toggle to the left of the value. The\n // host `TreeBody` keeps a reference via `getTreeCell()`\n // and pushes per-row depth + expansion state through\n // `setTreeState` on each bind.\n if (treeFieldName !== undefined && field.getName() === treeFieldName) {\n cell.wrapRenderer((delegate: CellRenderer<any>) => new TreeCellRenderer(delegate));\n this._treeCell = cell;\n }\n\n this.addComponent(cell, {\n data: field\n });\n }\n }\n }\n\n /**\n * Returns the cell on the row's tree column, or `null` when the row\n * was constructed without a `treeFieldName`. The host `TreeBody`\n * reads this to find each row's `TreeCellRenderer` for depth /\n * toggle updates and toggle-click routing.\n *\n * @returns The tree-column {@link Cell}, or `null`.\n */\n getTreeCell(): Cell<any> | null {\n return this._treeCell;\n }\n\n /**\n * Returns the field names backing this row's cells, in the same\n * order as `getComponents()`. Hidden columns are excluded.\n *\n * @returns The field names, in cell order.\n *\n * @remarks Used by the host `Body` to align cell index → field name\n * → {@link ColumnConfig} lookup when resolving per-cell read-only\n * state on each rebind. Not for consumer use.\n */\n getFieldNames(): string[] {\n return this._fieldNames;\n }\n\n /**\n * Returns the ModelRecord currently bound to this row.\n *\n * @returns The bound {@link ModelRecord}, or undefined if none has been set.\n */\n getData() {\n return this._data;\n }\n\n /**\n * Rebinds all cells to a new record, updating their displayed values.\n *\n * @param record - The new record to bind to this row.\n */\n setData(record: ModelRecord) : this {\n this._data = record;\n\n const cells = this.getComponents() as Cell<any>[];\n const names = this._fieldNames;\n\n for (let i = 0; i < names.length; i++) {\n this.bindCell(cells[i], record, names[i]);\n }\n\n this.updateVisualState();\n\n return this;\n }\n\n /**\n * Marks whether this row sits on a striped (odd) logical index, so the\n * resting background paints the zebra stripe.\n *\n * @param striped - True when this row's logical data index is odd.\n *\n * @remarks Set by the host Body on each rebind from `dataIndex % 2`; it only\n * updates the backing flag, so call `updateVisualState` afterwards to repaint.\n * Not for consumer use.\n */\n setStripe(striped: boolean): void {\n this._stripe = striped;\n }\n\n /**\n * Applies a background color based on the record's new/dirty/clean state.\n *\n * @remarks New records get a green tint, dirty records an orange tint, and clean records the zebra stripe (odd rows) or no tint (even rows).\n */\n updateVisualState(): void {\n const el = this.getElement();\n if (!el) {\n return;\n }\n\n // Per-record ephemeral tint on a pooled row re-bound to a new record on\n // every render. Going through a cached Component setter (setBackgroundColor)\n // would persist this into _options and replay it onto the next record bound\n // to this reused row, so write/remove the inline style directly instead.\n if (this._data?.isNew()) {\n DOM.sink.apply(el, { style: { 'background-color': 'var(--ts-ui-table-row-new, rgba(70, 200, 70, 0.15))' } });\n } else if (this._data?.isDirty()) {\n DOM.sink.apply(el, { style: { 'background-color': 'var(--ts-ui-table-row-dirty, rgba(255, 165, 0, 0.15))' } });\n } else if (this._stripe) {\n DOM.sink.apply(el, { style: { 'background-color': 'var(--ts-ui-table-row-stripe, rgba(0, 0, 0, 0.035))' } });\n } else {\n DOM.sink.apply(el, { style: { 'background-color': null } });\n }\n }\n\n /**\n * Appends a cell component to this row.\n *\n * @param cell - The cell to append.\n * @param constraints - Optional. Layout constraints for the cell.\n */\n addColumn(cell: Cell<any>, constraints?: LayoutConstraints) : this {\n this.addComponent(cell, constraints);\n\n return this;\n }\n\n /**\n * Adds a cell as a child component of this row.\n *\n * @param cell - The cell component to add.\n * @param constraints - Optional. Layout constraints for the cell.\n *\n * @returns This component, for method chaining.\n */\n addComponent(cell: Cell<any>, constraints?: LayoutConstraints): this {\n super.addComponent(cell, constraints);\n\n return this;\n }\n\n /**\n * Rebuilds this row's cell set in place to match `model`'s currently-\n * visible fields. Cells whose field is still visible are preserved\n * (along with their renderer, editor, theme listener, sort state,\n * group tint, etc.); cells whose field is now hidden are committed\n * (if editing) and removed; cells for newly-visible fields are\n * constructed via the same typed switch as the constructor.\n *\n * The child order is re-sorted to match the visible-field display\n * order. The tree column's cell, if any, is wrapped in a\n * {@link TreeCellRenderer} only on first creation — surviving tree\n * cells keep the renderer they already have.\n *\n * @param model - The model whose visible fields drive the cell list.\n * @param hiddenColumns - The set of field names to exclude.\n * @param columnConfigs - Per-field configs (carries `showSeconds`,\n * `groupColor`, etc.).\n * @param treeFieldName - Optional. Field name of the column that\n * carries the tree-cell renderer; matches the constructor's\n * parameter of the same name.\n *\n * @returns This row, for method chaining.\n */\n syncCells(\n model: AbstractModel,\n hiddenColumns: Set<string>,\n columnConfigs: Map<string, ColumnConfig>,\n treeFieldName?: string,\n ): this {\n this._model = model;\n\n const targetFields = model.getFields()\n .filter(f => !hiddenColumns.has(f.getName()))\n .sort((f1, f2) => f1.getOrder() - f2.getOrder());\n\n const existing = this.getComponents().slice() as Cell<any>[];\n const byName = new Map<string, Cell<any>>();\n\n for (const cell of existing) {\n const lc = this.getLayoutConstraints(cell);\n const field = lc?.data as Field | undefined;\n\n if (field) {\n byName.set(field.getName(), cell);\n }\n }\n\n // Remove cells whose field is no longer visible. Commit any\n // in-flight edit before discarding so user keystrokes land on\n // the record (mirrors the blur-commits-edit contract).\n const targetNames = new Set(targetFields.map(f => f.getName()));\n\n for (const cell of existing) {\n const lc = this.getLayoutConstraints(cell);\n const field = lc?.data as Field | undefined;\n\n if (!field || !targetNames.has(field.getName())) {\n if (cell.isEditing()) {\n cell.commitEdit();\n }\n\n this.removeComponent(cell);\n }\n }\n\n // Walk target fields in display order. Build any missing cell;\n // re-apply the commit wire and group tint on every (new and\n // surviving) cell so a config swap that changed the tint also\n // takes effect.\n this._treeCell = null;\n\n for (const field of targetFields) {\n const fieldName = field.getName();\n let cell = byName.get(fieldName);\n\n // A config change can flip a column between its combo cell and\n // its field-type-driven cell. Discard a surviving cell whose\n // kind no longer matches the current config so it rebuilds with\n // the right renderer + editor; commit any in-flight edit first.\n const wantsCombo = (columnConfigs.get(fieldName)?.values?.length ?? 0) > 0;\n const wantsDynamic = !!columnConfigs.get(fieldName)?.cellType;\n\n if (cell && (wantsCombo !== (cell instanceof ComboCell) || wantsDynamic !== (cell instanceof DynamicCell))) {\n if (cell.isEditing()) {\n cell.commitEdit();\n }\n\n this.removeComponent(cell);\n cell = undefined;\n }\n\n const isNew = !cell;\n\n if (!cell) {\n cell = Row.createCellForField(field, columnConfigs);\n }\n\n cell.on(\"commit\", (newValue) => {\n if (this._data) {\n this._data.set(fieldName, newValue);\n this._onCellCommit?.(this._data);\n }\n this.updateVisualState();\n });\n\n const groupColor = columnConfigs.get(fieldName)?.groupColor;\n\n if (groupColor) {\n cell.setBaseBackground(groupColor);\n }\n\n // Wrap the tree-column cell only when it's newly created —\n // a surviving tree cell already has its `TreeCellRenderer`,\n // and re-wrapping would chain renderers and double-register\n // a theme listener per toggle.\n if (isNew && treeFieldName !== undefined && fieldName === treeFieldName) {\n cell.wrapRenderer((delegate: CellRenderer<any>) => new TreeCellRenderer(delegate));\n }\n\n if (treeFieldName !== undefined && fieldName === treeFieldName) {\n this._treeCell = cell;\n }\n\n if (isNew) {\n this.addComponent(cell, { data: field });\n this.bindCell(cell, this._data, fieldName);\n }\n }\n\n // Re-order children to the new visible-field order. Mirrors\n // `Header.sortColumns` — same `Field` payload from the layout\n // constraints drives both.\n this.sortComponents((c1, c2) => {\n const f1 = (this.getLayoutConstraints(c1)?.data as Field).getOrder();\n const f2 = (this.getLayoutConstraints(c2)?.data as Field).getOrder();\n\n return f1 - f2;\n });\n\n this._fieldNames = targetFields.map(f => f.getName());\n\n return this;\n }\n\n /**\n * Routes a cell's value-set for one field: a {@link DynamicCell} resolves\n * its per-record variant via `bindRecord`, while every other cell keeps\n * the plain `setValue` path.\n *\n * @param cell - The cell to bind.\n * @param record - The record to bind, or undefined to clear the cell.\n * @param fieldName - The field this cell presents.\n */\n private bindCell(cell: Cell<any>, record: ModelRecord | undefined, fieldName: string): void {\n if (cell instanceof DynamicCell && record) {\n cell.bindRecord(record);\n } else {\n cell.setValue(record ? record.get(fieldName) : undefined);\n }\n }\n\n /**\n * Builds the typed `Cell` for `field`, applying any field-specific\n * options from `columnConfigs` (e.g. `showSeconds` on time cells).\n *\n * @param field - The field whose typed cell to construct.\n * @param columnConfigs - Per-field configs keyed by field name.\n *\n * @returns A new typed cell matching `field.getType()`.\n */\n private static createCellForField(field: Field, columnConfigs: Map<string, ColumnConfig>): Cell<any> {\n const config = columnConfigs.get(field.getName());\n\n // A custom renderer wins over both the combo (`values`) routing and the\n // field-type switch. The cell is built with no editor, so it stays\n // display-only — a click never enters edit mode (Cell.startEdit bails\n // with no editor and no pool key). The base Cell.setValue forwards each\n // rebind to the renderer.\n if (config?.renderer) {\n return new Cell(\"td\", config.renderer());\n }\n\n if (config?.cellType) {\n return new DynamicCell(field.getName(), field.getType(), config);\n }\n\n const values = config?.values;\n\n if (values && values.length > 0) {\n return new ComboCell(field.getName(), values);\n }\n\n switch (field.getType()) {\n case \"string\":\n return new StringCell();\n case \"number\":\n return new NumberCell();\n case \"boolean\":\n return new BooleanCell();\n case \"date\":\n return new DateCell();\n case \"time\":\n return new TimeCell(columnConfigs.get(field.getName())?.showSeconds ?? false);\n case \"datetime\":\n return new DateTimeCell(columnConfigs.get(field.getName())?.showSeconds ?? false);\n case \"glyph\":\n return new GlyphCell();\n default:\n return new DefaultCell();\n }\n }\n\n /**\n * No-op; cell layout is driven by the Body's renderWindow.\n *\n * @returns This component, for method chaining.\n */\n doLayout(): this {\n return this;\n }\n}\n\nconst RowCallable = callable(Row);\ntype RowCallable = Row;\nexport {\n Row as _Row,\n RowCallable as Row\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { StyleRule } from \"~/core/StyleTarget.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * String-literal union of the events emitted by {@link ResizeHandle}.\n *\n * @category Components\n */\ntype ResizeHandleEvent = \"dragstart\" | \"dragmove\" | \"dragend\";\n\n/**\n * Construction-time options for {@link ResizeHandle}.\n *\n * @category Components\n */\ninterface ResizeHandleOptions extends ComponentOptions {\n /**\n * Multi-event listener bag dispatched to {@link ResizeHandle.on} at\n * construction time. Each entry is appended; calling `on(event, fn)`\n * later registers another listener for the same event.\n */\n listeners?: {\n dragstart?: (event: MouseEvent) => void;\n dragmove?: (clientX: number) => void;\n dragend?: () => void;\n };\n}\n\n/**\n * The handle's themeable resize cursor. Exported so the host header can hold the\n * same cursor for the duration of a column drag, when the handle itself is out\n * of hit-testing and can no longer resolve it.\n */\nconst RESIZE_HANDLE_CURSOR = \"var(--ts-ui-table-resize-handle-cursor, ew-resize)\";\n\nlet _classRule: StyleRule | null = null;\n\n/**\n * Registers the shared `.ResizeHandle` class rule once on first use. The rule\n * holds the absolute-position box geometry (position, top, right, width,\n * height, z-index) so per-instance setters only carry per-instance state.\n *\n * Idempotent and module-local; safe across hot reloads.\n */\nfunction ensureResizeHandleClassRule(): void {\n if (_classRule) {\n return;\n }\n\n _classRule = new StyleRule({\n scope: \"class\",\n name: \"ResizeHandle\",\n styles: {\n position: \"absolute\",\n top: \"0\",\n right: \"0\",\n width: \"var(--ts-ui-table-resize-handle-width,5px)\",\n height: \"100%\",\n zIndex: \"1\",\n },\n });\n}\n\n/**\n * A thin draggable handle anchored to the right edge of a table header cell.\n *\n * Lives as a side-loaded overlay on the `<th>` (its `position:absolute` plus\n * the host cell's [`Card`](/api/layout/classes/Card) layout keep it out of the\n * cell renderer's flow). Owns its own click + mousedown listeners via\n * {@link Event.addListener}. Drag-phase mousemove/mouseup listeners live with\n * the host so that the host can suppress the synthesized post-drag click.\n *\n * The static box geometry (position, top, right, width, height, z-index)\n * lives in a shared `.ResizeHandle` class rule registered on first use. The\n * cursor and the indicator gradient are per-instance values written through\n * typed Component setters in the constructor.\n *\n * @category Components\n */\nclass ResizeHandle extends Component<ResizeHandleOptions> {\n\n private _listeners: ListenerBag<ResizeHandleEvent> = new ListenerBag<ResizeHandleEvent>();\n\n /**\n * Constructs a resize handle. Callbacks default to none; consumers\n * register them via {@link on} or pass them through `options.listeners`.\n *\n * @param options - Optional configuration bag (drag listeners plus common\n * Component fields).\n */\n constructor(options?: ResizeHandleOptions) {\n ensureResizeHandleClassRule();\n\n super({ tag: \"div\", ...(options ?? {}) });\n\n this.setCursor(RESIZE_HANDLE_CURSOR);\n // 5 px-wide drag target with a 1 px colored stripe at the right\n // edge — `80%` of `5 px = 4 px` transparent, the remaining `20%`\n // is `1 px` of the resize-handle colour. Pairs with the 1 px\n // dividers on ParentHeaderCell so every visible cell separator\n // in the header band reads at the same thickness.\n this.setBackgroundImage(\n \"linear-gradient(to right,transparent 80%,\" +\n \"var(--ts-ui-table-resize-handle-color,rgba(0,0,0,0.2)) 80%)\");\n this.setZIndex(1);\n\n // Listener wiring must happen here, NOT inside `applyOptions` —\n // Component's constructor invokes `applyOptions` synchronously\n // from inside super(), at which point the class-field\n // `_listeners` initializer has not yet run (class-field\n // initialisers fire between super() returning and the next\n // statement here). Wiring after super() guarantees `_listeners`\n // is the live `ListenerBag` instance.\n this.applyListeners(options?.listeners);\n }\n\n /**\n * Wires the framework-routed mousedown + click listeners after the element\n * has rendered. Mousedown fires the registered drag-start listeners; click\n * is intercepted to prevent a sort from firing on the host header cell.\n *\n * @param element - Optional element passed from the framework init chain.\n * @returns This component, for method chaining.\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n Event.addListener(this, \"mousedown\", this._onMouseDown);\n Event.addListener(this, \"click\", this._onClick);\n\n return this;\n }\n\n /**\n * Registers a listener for one of this handle's drag events.\n *\n * @param event - `\"dragstart\"` fires on mousedown over the handle, carrying\n * the originating `MouseEvent` (whose `clientX` seeds the drag origin);\n * `\"dragmove\"` fires on each viewport mousemove during a drag with the\n * absolute pointer `clientX`; `\"dragend\"` fires when the viewport mouseup\n * observes the release.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This handle, for method chaining.\n */\n on(event: \"dragstart\", listener: (e: MouseEvent) => void): this;\n on(event: \"dragmove\", listener: (clientX: number) => void): this;\n on(event: \"dragend\", listener: () => void): this;\n on(event: ResizeHandleEvent, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered listener. The exact callback reference\n * must match.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This handle, for method chaining.\n */\n off(event: ResizeHandleEvent, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every listener registered for `event` with `payload`, in\n * registration order. Internal use only — outer hosts call the public\n * verbs {@link dragMove} / {@link dragEnd} which forward to `emit`.\n *\n * @param event - The event to emit.\n * @param payload - Forwarded to each listener.\n */\n protected emit(event: \"dragstart\", e: MouseEvent): void;\n protected emit(event: \"dragmove\", clientX: number): void;\n protected emit(event: \"dragend\"): void;\n protected emit(event: ResizeHandleEvent, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Drives the `\"dragmove\"` event from the host's viewport-mousemove\n * listener. The host extracts `clientX` and forwards it here; this\n * method fires `\"dragmove\"` listeners with that absolute coordinate.\n *\n * @param clientX - The absolute pointer `clientX` for this mousemove tick.\n */\n dragMove(clientX: number): void {\n this.emit(\"dragmove\", clientX);\n }\n\n /**\n * Drives the `\"dragend\"` event from the host's viewport-mouseup\n * listener.\n */\n dragEnd(): void {\n this.emit(\"dragend\");\n }\n\n /**\n * Bound mousedown handler — fires the `\"dragstart\"` event with the\n * originating MouseEvent.\n */\n private _onMouseDown = (e: MouseEvent): void => {\n this.emit(\"dragstart\", e);\n };\n\n /**\n * Bound click handler — swallows the click so the host header cell does\n * not interpret it as a sort.\n */\n private _onClick = (e: MouseEvent): void => {\n e.stopPropagation();\n };\n}\n\nconst ResizeHandleCallable = callable(ResizeHandle);\ntype ResizeHandleCallable = ResizeHandle;\nexport {\n ResizeHandle as _ResizeHandle,\n ResizeHandleCallable as ResizeHandle,\n RESIZE_HANDLE_CURSOR\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { StyleRule } from \"~/core/StyleTarget.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\n\n/**\n * Construction-time options for {@link SortPriorityBadge}.\n *\n * @category Components\n */\ninterface SortPriorityBadgeOptions extends ComponentOptions {\n priority?: number | null;\n}\n\nlet _classRule: StyleRule | null = null;\n\n/**\n * Registers the shared `.SortPriorityBadge` class rule once on first use. The\n * rule holds the absolute-position overlay geometry (position, top, right,\n * font-size, line-height, border-radius, padding, pointer-events) so\n * per-instance setters only carry per-instance state (background, color,\n * visibility, text).\n *\n * Idempotent and module-local; safe across hot reloads.\n */\nfunction ensureSortBadgeClassRule(): void {\n if (_classRule) {\n return;\n }\n\n _classRule = new StyleRule({\n scope: \"class\",\n name: \"SortPriorityBadge\",\n styles: {\n position: \"absolute\",\n top: \"2px\",\n right: \"8px\",\n fontSize: \"var(--ts-ui-sort-badge-font-size, var(--ts-ui-font-size))\",\n lineHeight: \"1\",\n borderRadius: \"3px\",\n padding: \"1px 3px\",\n pointerEvents: \"none\",\n },\n });\n}\n\n/**\n * A small numeric badge anchored to the top-right corner of a table header\n * cell, used to surface the multi-sort priority (1-based) of that column.\n *\n * Lives as a side-loaded overlay on the `<th>` (its `position:absolute` plus\n * the host cell's [`Card`](/api/layout/classes/Card) layout keep it out of\n * the cell renderer's flow). The badge stays hidden for priority `null`,\n * `0`, and `1` — the leading sort needs no number because the arrow on the\n * header label already conveys direction.\n *\n * The static overlay geometry lives in a shared `.SortPriorityBadge` class\n * rule registered on first use. The background and foreground colours are\n * per-instance values written through typed Component setters in the\n * constructor.\n *\n * @category Components\n */\nclass SortPriorityBadge extends Component<SortPriorityBadgeOptions> {\n\n declare private _priority: number | null;\n\n /**\n * Constructs a sort-priority badge. The badge starts hidden until\n * {@link setPriority} writes a priority of 2 or greater.\n *\n * @param options - Optional configuration bag (initial priority plus\n * common Component fields).\n */\n constructor(options?: SortPriorityBadgeOptions) {\n ensureSortBadgeClassRule();\n\n super({ tag: \"span\", ...(options ?? {}) });\n\n this._priority ??= null;\n\n this.setBackgroundColor(\"var(--ts-ui-sort-badge-bg, rgba(0,0,0,0.15))\");\n this.setForegroundColor(\"var(--ts-ui-sort-badge-color, inherit)\");\n this.setVisible(this._shouldShow(this._priority));\n }\n\n /**\n * Applies a {@link SortPriorityBadgeOptions} bag. The priority field is\n * written pure to the backing field here; inherited Component fields\n * cascade through `super.applyOptions`.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: SortPriorityBadgeOptions): this {\n super.applyOptions(options);\n\n if (options.priority !== undefined) {\n this._priority = options.priority;\n }\n\n return this;\n }\n\n /**\n * Renders the root span and flushes the cached priority into\n * `textContent` and visibility.\n *\n * @returns The rendered root element handle.\n */\n protected render(): Handle {\n const element = super.render();\n\n DOM.sink.apply(element, { text: this._priorityText() });\n\n return element;\n }\n\n /**\n * Returns the priority last written via {@link setPriority}, or `null`.\n *\n * @returns The current priority, or null when none has been set.\n */\n getPriority(): number | null {\n return this._priority;\n }\n\n /**\n * Sets the multi-sort priority. Priorities below 2 collapse to hidden\n * because the leading sort indicator on the header label is sufficient\n * without a numeric badge.\n *\n * @param value - The 1-based priority, or `null` to clear.\n * @returns This component, for method chaining.\n */\n setPriority(value: number | null): this {\n this._priority = value;\n\n const element = this.getElement();\n if (element) {\n DOM.sink.apply(element, { text: this._priorityText() });\n }\n\n this.setVisible(this._shouldShow(value));\n\n return this;\n }\n\n /**\n * Clears the priority (equivalent to `setPriority(null)`).\n *\n * @returns This component, for method chaining.\n */\n clearPriority(): this {\n return this.setPriority(null);\n }\n\n /**\n * Returns the priority rendered as a string, or the empty string when\n * the badge should not display text.\n *\n * @returns The text content for the current priority value.\n */\n private _priorityText(): string {\n return this._shouldShow(this._priority) ? String(this._priority) : \"\";\n }\n\n /**\n * Returns `true` when the given priority should make the badge visible.\n *\n * @param value - The priority value to evaluate.\n * @returns True if a badge with this priority should display.\n */\n private _shouldShow(value: number | null): boolean {\n return value != null && value >= 2;\n }\n}\n\nconst SortPriorityBadgeCallable = callable(SortPriorityBadge);\ntype SortPriorityBadgeCallable = SortPriorityBadge;\nexport {\n SortPriorityBadge as _SortPriorityBadge,\n SortPriorityBadgeCallable as SortPriorityBadge\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { DefaultCell } from \"~/component/table/cell/Default.js\";\nimport { ResizeHandle, RESIZE_HANDLE_CURSOR } from \"~/component/table/cell/ResizeHandle.js\";\nimport { SortPriorityBadge } from \"~/component/table/cell/SortPriorityBadge.js\";\nimport { CellEvent } from \"~/component/table/cell/Cell.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { beginPointerDrag, endPointerDrag } from \"~/core/PointerDrag.js\";\nimport { StyleRule } from \"~/core/StyleTarget.js\";\nimport { Tooltip } from \"~/overlay/Tooltip.js\";\nimport { ThemeManager } from \"~/core/Theme.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * String-literal union of the events emitted by {@link HeaderCell}. Extends\n * the inherited `CellEvent` union with the header-specific events.\n *\n * @category Components\n */\nexport type HeaderCellEvent = CellEvent | \"sortclick\" | \"contextmenu\" | \"resizestart\" | \"resizedrag\";\n\n/**\n * Width (px) used both for the side-loaded `Glyph`'s preferred size and for\n * computing the renderer's left inset when a glyph is mounted.\n */\nconst GLYPH_W = 16;\n\n/**\n * Height (px) used for the side-loaded `Glyph`'s preferred size.\n */\nconst GLYPH_H = 16;\n\n/**\n * Default gap (px) between the cell's left edge and the side-loaded glyph.\n * Mirrors the `--ts-ui-table-header-glyph-gap` token default and is used as\n * the fallback term when computing the renderer's left inset.\n */\nconst GLYPH_GAP = 4;\n\nlet _glyphClassRule: StyleRule | null = null;\n\n/**\n * Registers the shared `.HeaderCellGlyph` class rule once on first use. The\n * rule holds the side-loaded glyph's static placement (position, left, top)\n * so per-instance Component setters only carry transform, size, color, and\n * pointer-events.\n *\n * Idempotent and module-local; safe across hot reloads.\n */\nfunction ensureHeaderCellGlyphClassRule(): void {\n if (_glyphClassRule) {\n return;\n }\n\n _glyphClassRule = new StyleRule({\n scope: \"class\",\n name: \"HeaderCellGlyph\",\n styles: {\n position: \"absolute\",\n left: \"var(--ts-ui-table-header-glyph-gap, 4px)\",\n top: \"50%\",\n },\n });\n}\n\n/**\n * A non-editable header cell rendered as a `<th>` element.\n *\n * Extends {@link DefaultCell} with a sort state indicator (▲/▼ suffix on the\n * label), a click-to-sort callback, and a right-edge drag handle for column\n * resizing. The drag handle and the multi-sort priority badge are dedicated\n * Component subclasses ([`ResizeHandle`](/api/component/table/classes/ResizeHandle)\n * and [`SortPriorityBadge`](/api/component/table/classes/SortPriorityBadge))\n * added through the framework's render lifecycle; both use absolute\n * positioning so they overlay the renderer without disturbing its layout.\n *\n * @category Components\n */\nclass HeaderCell extends DefaultCell {\n\n private _text: String;\n private _fieldName: string;\n private _isDragging: boolean = false;\n private _tooltipText: string = '';\n declare private _resizeHandle: ResizeHandle;\n declare private _priorityBadge: SortPriorityBadge;\n private _sortState: { state: 'asc' | 'desc', priority: number | null } | null = null;\n private _headerGlyph: string | null = null;\n private _headerGlyphInstance: Glyph | null = null;\n private _columnFocused: boolean = false;\n private _required: boolean = false;\n\n /**\n * Creates a header cell with bold text and wires up the sort click listener.\n *\n * @param text - The column title to display.\n * @param fieldName - The model field name used when triggering sort callbacks.\n * @param headerGlyph - Optional registry glyph name mounted to the left of the text.\n */\n constructor(text: String, fieldName: string, headerGlyph?: string | null) {\n super(\"th\");\n\n this.getAria().setRole(\"columnheader\");\n this.getAria().setSort(\"none\");\n\n this._text = text;\n this._fieldName = fieldName;\n this._headerGlyph = headerGlyph ?? null;\n\n let renderer = this.getRenderer();\n renderer.getText().setFontSize(\"--ts-ui-table-header-font-size\");\n renderer.getText().setFontWeight(\"bold\");\n renderer.getText().setText(text);\n\n // DefaultCell's `(tag?: string)` super-signature cannot carry the\n // `styleRules` options bag, so the rule is allocated here via the\n // protected `createStyleRule` builder — same dedupe-and-defer path\n // the options-bag dispatch uses, just spelled imperatively. The\n // render-time `applyStyle` flushes the queued `boxShadow` onto the\n // stylesheet, matching ARCHITECTURE.md's \"construction stays\n // JS-only\" rule.\n this.createStyleRule(\":active\").set(\n \"boxShadow\",\n \"var(--ts-ui-button-pressed-shadow, 1px 2px 5px 0 rgba(0,0,0,0.2) inset)\",\n );\n\n // Wire the resize-handle drag lifecycle: mousedown installs viewport\n // mousemove/mouseup listeners that forward through the handle's\n // events. The `_isDragging` flag straddles the handle and the\n // host's click listener (which suppresses the synthetic post-drag\n // click) so the bookkeeping must live on the host.\n this._resizeHandle = new ResizeHandle({\n listeners: {\n dragstart: (e: MouseEvent) => this.onResizeDragStart(e),\n dragmove : (clientX: number) => this.emit(\"resizedrag\", clientX),\n },\n });\n this._priorityBadge = new SortPriorityBadge();\n }\n\n /**\n * Wires the host click + contextmenu listeners, attaches the tooltip,\n * and mounts the side-loaded glyph if one was supplied at construction.\n *\n * @param element - Optional element passed from the framework init chain.\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement();\n\n if (!el) {\n return this;\n }\n\n // Subtree listener so clicks on any child element (e.g. the Label) bubble up here.\n Event.addSubtreeListener(this, 'click', (e: MouseEvent) => this.onSortClick(e.shiftKey));\n\n Event.addSubtreeListener(this, 'contextmenu', (e: MouseEvent) => {\n e.preventDefault();\n\n this.emit(\"contextmenu\", this._fieldName, e.clientX, e.clientY);\n });\n\n // Side-load the resize handle and sort-priority badge as overlays.\n // Their `position:absolute` means they don't disturb the cell's `Card`\n // layout flow, and side-loading (instead of `addComponent`) keeps the\n // Card from hiding them as non-visible siblings of the renderer.\n DOM.sink.appendChild(el, this._resizeHandle.getElement(true)!);\n DOM.sink.appendChild(el, this._priorityBadge.getElement(true)!);\n\n if (this._tooltipText) {\n Tooltip.attachToElement(el, this._tooltipText);\n }\n\n if (this._headerGlyph) {\n this.setHeaderGlyph(this._headerGlyph);\n }\n\n return this;\n }\n\n /**\n * Returns the currently mounted header glyph registry name, or `null` if none.\n *\n * @returns The glyph registry name, or `null`.\n */\n getHeaderGlyph(): string | null {\n return this._headerGlyph;\n }\n\n /**\n * Mounts (or replaces) the leading header glyph. Pass `null` to remove it.\n *\n * The glyph is absolutely positioned at the cell's left edge using the\n * `--ts-ui-table-header-glyph-gap` / `--ts-ui-table-header-glyph-color`\n * tokens. The text renderer's left inset is shifted right to clear the\n * glyph when one is mounted, and restored to the theme default when cleared.\n *\n * @param name - A registered glyph name, or `null` to remove the glyph.\n * @returns This cell, for method chaining.\n */\n setHeaderGlyph(name: string | null): this {\n this._headerGlyph = name;\n this._mountHeaderGlyph(this.getElement());\n\n return this;\n }\n\n /**\n * Mounts or replaces the leading glyph against the given host element.\n * Called from {@link setHeaderGlyph} (post-init via cached element) and\n * from {@link init} (during render via the element parameter, before\n * `Component._element` has been cached).\n *\n * @param el - The owning `<th>` element, or undefined when the cell is\n * still pre-render. When undefined the renderer insets are reset but\n * no glyph is mounted; the next render's {@link init} call will mount\n * the glyph using its element parameter.\n */\n private _mountHeaderGlyph(el: Handle | undefined): void {\n if (this._headerGlyphInstance) {\n const glyphEl = this._headerGlyphInstance.getElement();\n if (glyphEl) {\n DOM.sink.removeElement(glyphEl);\n }\n this._headerGlyphInstance = null;\n }\n\n const themePad = ThemeManager.getTheme().table.cell.padding;\n const name = this._headerGlyph;\n\n if (!name || !el) {\n this.getRenderer().setInsets(new Insets(0, themePad, 0, themePad));\n\n return;\n }\n\n ensureHeaderCellGlyphClassRule();\n\n const glyph = new Glyph(name);\n\n glyph.setTransform(\"translateY(-50%)\");\n glyph.setSize({ width: GLYPH_W, height: GLYPH_H });\n glyph.setForegroundColor(\"var(--ts-ui-table-header-glyph-color, currentColor)\");\n glyph.setPointerEvents(\"none\");\n\n const gEl = glyph.getElement(true)!;\n DOM.sink.apply(gEl, { addClass: [\"HeaderCellGlyph\"] });\n\n DOM.sink.appendChild(el, gEl);\n this._headerGlyphInstance = glyph;\n\n const offset = GLYPH_W + GLYPH_GAP + themePad;\n this.getRenderer().setInsets(new Insets(0, themePad, 0, offset));\n }\n\n /**\n * Updates the label to show a sort direction arrow suffix and toggles the\n * multi-sort priority badge.\n *\n * @param state - 'asc' or 'desc'. Use `clearSortState()` to remove the indicator.\n * @param priority - Optional 1-based position of this sorter in a multi-sort.\n * The badge is only shown when priority is at least 2.\n */\n setSortState(state: 'asc' | 'desc', priority?: number | null): this {\n this._sortState = { state, priority: priority ?? null };\n\n this._renderTitle();\n this.getAria().setSort(state === 'asc' ? 'ascending' : 'descending');\n\n this._priorityBadge.setPriority(priority ?? null);\n\n return this;\n }\n\n /**\n * Returns the cached sort indicator state last passed to {@link setSortState},\n * or `null` if no sort indicator is active.\n *\n * @returns An object describing the sort direction and multi-sort priority,\n * or null.\n */\n getSortState(): { state: 'asc' | 'desc', priority: number | null } | null {\n return this._sortState;\n }\n\n /**\n * Clears the sort indicator arrow and hides the multi-sort priority badge.\n *\n * @returns This cell, for method chaining.\n */\n clearSortState(): this {\n this._sortState = null;\n\n this._renderTitle();\n this.getAria().setSort('none');\n\n this._priorityBadge.clearPriority();\n\n return this;\n }\n\n /**\n * Sets whether this header cell shows the required-column asterisk\n * suffix. Driven by the column's static `ColumnConfig.required`\n * flag only — the header has no bound record to evaluate a\n * per-record `requiredPredicate` against.\n *\n * @param value - `true` to show the asterisk, `false` to hide it.\n * @returns This cell, for method chaining.\n */\n setRequired(value: boolean): this {\n this._required = value;\n this._renderTitle();\n\n return this;\n }\n\n /**\n * Composes the header label from the base title plus the required\n * asterisk (` *`, when `_required`) and the sort arrow (` ▲`/` ▼`,\n * when a sort state is active), and writes it to the renderer.\n * Shared by {@link setSortState}, {@link clearSortState}, and\n * {@link setRequired} so the two suffixes never clobber each other.\n */\n private _renderTitle(): void {\n const arrow = this._sortState\n ? (this._sortState.state === 'asc' ? ' ▲' : ' ▼')\n : '';\n const req = this._required ? ' *' : '';\n\n this.getRenderer().getText().setText(this._text + req + arrow);\n }\n\n /**\n * Registers a listener for one of this header cell's events.\n *\n * @param event - `\"commit\"` / `\"editend\"` inherit from {@link Cell} (they\n * are unused on the non-editable header surface but kept on the typed\n * signature for inheritance compatibility); `\"sortclick\"` fires when\n * the user clicks the header, receiving the field name and the\n * shift-key state; `\"contextmenu\"` fires on right-click, receiving the\n * field name and the viewport x/y; `\"resizestart\"` fires on mousedown\n * over the resize handle, receiving the absolute pointer `clientX` at the\n * moment the drag began; `\"resizedrag\"` fires on each mousemove during a\n * resize drag, receiving the absolute pointer `clientX`.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This cell, for method chaining.\n */\n on(event: \"commit\", listener: (value: String | null) => void): this;\n on(event: \"editend\", listener: () => void): this;\n on(event: \"sortclick\", listener: (fieldName: string, shiftKey: boolean) => void): this;\n on(event: \"contextmenu\", listener: (fieldName: string, x: number, y: number) => void): this;\n on(event: \"resizestart\", listener: (clientX: number) => void): this;\n on(event: \"resizedrag\", listener: (clientX: number) => void): this;\n on(event: HeaderCellEvent, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered listener. The exact callback reference\n * must match.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This cell, for method chaining.\n */\n off(event: HeaderCellEvent, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every listener registered for `event` with `payload`, in\n * registration order.\n *\n * @param event - The event to emit.\n * @param payload - Forwarded to each listener.\n */\n protected emit(event: \"commit\", value: String | null): void;\n protected emit(event: \"editend\"): void;\n protected emit(event: \"sortclick\", fieldName: string, shiftKey: boolean): void;\n protected emit(event: \"contextmenu\", fieldName: string, x: number, y: number): void;\n protected emit(event: \"resizestart\", clientX: number): void;\n protected emit(event: \"resizedrag\", clientX: number): void;\n protected emit(event: HeaderCellEvent, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Sets the tooltip text shown when hovering this header cell.\n *\n * Safe to call before or after the cell has rendered: pre-init calls are\n * picked up by `init` when it wires the tooltip attachment; post-init\n * calls re-attach against the live element so the visible tooltip\n * updates immediately.\n *\n * @param text - The text to display in the tooltip.\n */\n setTooltip(text: string): this {\n this._tooltipText = text;\n\n const el = this.getElement();\n\n if (el) {\n Tooltip.attachToElement(el, text);\n }\n\n return this;\n }\n\n /**\n * Returns the tooltip text shown when hovering this header cell.\n *\n * @returns The current tooltip string (empty when no tooltip has been set).\n */\n getTooltip(): string {\n return this._tooltipText;\n }\n\n /**\n * Routes a click on the header to the registered sort callback,\n * unless a resize drag has just finished.\n *\n * @param shiftKey - Whether the shift key was held when the click fired.\n */\n private onSortClick(shiftKey: boolean): void {\n if (this._isDragging) {\n this._isDragging = false;\n return;\n }\n\n this.emit(\"sortclick\", this._fieldName, shiftKey);\n }\n\n private onResizeDragStart(e: MouseEvent): void {\n e.stopPropagation();\n\n this._isDragging = true;\n\n this.emit(\"resizestart\", e.clientX);\n\n Event.addViewportListener(this, 'mousemove', this.onResizeDrag);\n Event.addViewportListener(this, 'mouseup', this.onResizeDragStop);\n\n beginPointerDrag(RESIZE_HANDLE_CURSOR);\n }\n\n private onResizeDrag(e: MouseEvent): void {\n this._resizeHandle.dragMove(e.clientX);\n }\n\n private onResizeDragStop(): void {\n Event.removeViewportListener(this, 'mousemove', this.onResizeDrag);\n Event.removeViewportListener(this, 'mouseup', this.onResizeDragStop);\n\n endPointerDrag();\n\n this._resizeHandle.dragEnd();\n\n // clear flag after synthesized click fires\n setTimeout(() => { this._isDragging = false; }, 0);\n }\n\n /**\n * Toggles the column-focused visual indicator on this header cell.\n *\n * @param focused - True to paint the focus underline, false to clear it.\n *\n * @returns This component, for method chaining.\n *\n * @remarks The indicator is a 2 px inset bottom box-shadow in the\n * `--ts-ui-focus-ring` theme token, matching the colour of the body cell's\n * focus outline so the eye reads the header cue and the body cue as one\n * affordance. Driven by `Body._updateFocusStyle` whenever the body's\n * focused column changes.\n */\n setColumnFocused(focused: boolean): this {\n this._columnFocused = focused;\n\n if (focused) {\n this.setShadow(\"inset 0 -2px 0 0 var(--ts-ui-focus-ring, rgba(30, 100, 200, 0.6))\");\n } else {\n this.clearShadow();\n }\n\n return this;\n }\n\n /**\n * Returns whether this header cell currently shows the column-focused indicator.\n *\n * @returns True when `setColumnFocused(true)` was the last call; false otherwise.\n */\n isColumnFocused(): boolean {\n return this._columnFocused;\n }\n}\n\nconst HeaderCellCallable = callable(HeaderCell);\ntype HeaderCellCallable = HeaderCell;\nexport {\n HeaderCell as _HeaderCell,\n HeaderCellCallable as HeaderCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { DefaultCell } from \"~/component/table/cell/Default.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { Tooltip } from \"~/overlay/Tooltip.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A non-interactive header cell that labels a contiguous run of columns in\n * the parent-header row. Constructed once per visible group by the\n * [`Header`](/api/component/table/classes/Header) host (private\n * `rebuildParentCells`) and laid out via the table layout manager: x\n * and width are summed from the underlying column widths so the parent\n * cell visually spans its children.\n *\n * Empty-text instances render as blank spanning cells over ungrouped\n * columns; they exist so the parent header band has a continuous\n * surface with no gaps where the body background would otherwise leak\n * through.\n *\n * Sort / resize wiring is intentionally absent — parent cells do not\n * participate in the per-column sort cycle or carry resize handles.\n * They do support a right-click context menu (forwarded through the\n * same callback the column cells use, so the column-toggle menu opens\n * on either row), and a tooltip.\n *\n * @category Components\n */\nclass ParentHeaderCell extends DefaultCell {\n\n private _text: string;\n private _color: string | null;\n private _tooltipText: string = \"\";\n\n /**\n * Constructs a parent header cell over a contiguous run of grouped\n * columns.\n *\n * @param text - The group label to display. Empty string renders a\n * blank spanning cell over ungrouped columns.\n * @param color - Optional CSS color string for the cell's background;\n * `null` falls through to the header-band gradient inherited from\n * the `Header` parent.\n */\n constructor(text: string, color: string | null) {\n super(\"th\");\n\n this._text = text;\n this._color = color;\n\n this.getAria().setRole(\"columnheader\");\n\n const renderer = this.getRenderer();\n renderer.getText().setFontSize(\"--ts-ui-table-header-font-size\");\n renderer.getText().setFontWeight(\"bold\");\n renderer.getText().setTextAlign(\"center\");\n renderer.getText().setText(text);\n\n // The cell's `Cell` base writes `var(--ts-ui-table-cell-bg, …)` into\n // backgroundColor; override to either the consumer-supplied\n // `groupColor` or `transparent` so the `Header` band's gradient\n // shows through unaltered when no color is set. The base class only\n // re-applies the *border* on theme changes (Cell.ts:51), not the\n // background, so this write survives a theme swap.\n this.setBackgroundColor(color ?? \"transparent\");\n\n // Inter-group divider (right edge) and parent-row separator\n // (bottom edge) — two inset shadows in the same resize-handle\n // gray that paints the standard cell separators in the column\n // row beneath. Composing both into a single `setShadow` call\n // sidesteps `Cell`'s theme-change listener (which re-runs\n // `setBorder('var(--ts-ui-table-cell-border, none)')` and would\n // otherwise wipe a border-based divider on every theme toggle).\n this.setShadow([\n \"inset -1px 0 0 0 var(--ts-ui-table-resize-handle-color, rgba(0, 0, 0, 0.2))\",\n \"inset 0 -1px 0 0 var(--ts-ui-table-resize-handle-color, rgba(0, 0, 0, 0.2))\",\n ].join(\", \"));\n }\n\n /**\n * Registers a listener for this parent header cell's `\"contextmenu\"`\n * event, fired when the user right-clicks the cell. Mirrors\n * `HeaderCell.on(\"contextmenu\", fn)` but elides the per-column\n * `fieldName` — parent cells span multiple columns, so the host only\n * needs the viewport coordinates to anchor the menu.\n *\n * Wiring: the subtree `contextmenu` DOM listener is installed once at\n * `init` time and fires every registered listener at right-click time —\n * registering after `init` runs is supported (the next right-click\n * picks up the new listener), but the DOM listener itself is not\n * re-installed.\n *\n * @param event - The event name. Only `\"contextmenu\"` is accepted.\n * @param listener - Receives the viewport x and y coordinates of the event.\n *\n * @returns This cell, for method chaining.\n */\n on(event: \"contextmenu\", listener: (x: number, y: number) => void): this;\n on(event: \"commit\", listener: (value: String | null) => void): this;\n on(event: \"editend\", listener: () => void): this;\n on(event: string, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered listener. The exact callback\n * reference must match the one passed to {@link on}.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This cell, for method chaining.\n */\n off(event: string, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every registered listener for `event`, in registration order.\n * Widens the inherited {@link Cell} emitter with the `\"contextmenu\"`\n * event carrying the right-click's viewport coordinates.\n *\n * @param event - The event to emit.\n * @param payload - Forwarded to each listener.\n */\n protected emit(event: \"contextmenu\", x: number, y: number): void;\n protected emit(event: \"commit\", value: String | null): void;\n protected emit(event: \"editend\"): void;\n protected emit(event: string, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Sets the tooltip shown when hovering this parent header cell.\n * Wired through the framework's shared [`Tooltip`](/api/overlay/classes/Tooltip)\n * attach path, same as `HeaderCell.setTooltip`. Safe to call before or\n * after the cell has rendered: pre-init calls are picked up by `init`\n * when it wires the tooltip attachment; post-init calls re-attach\n * against the live element so the visible tooltip updates immediately.\n *\n * @param text - The text to display in the tooltip.\n */\n setTooltip(text: string): this {\n this._tooltipText = text;\n\n const el = this.getElement();\n\n if (el) {\n Tooltip.attachToElement(el, text);\n }\n\n return this;\n }\n\n /**\n * Returns the tooltip text shown when hovering this cell.\n *\n * @returns The current tooltip string (empty when no tooltip has been set).\n */\n getTooltip(): string {\n return this._tooltipText;\n }\n\n /**\n * Returns the group label rendered in this cell.\n *\n * @returns The label string; empty when the cell is a blank ungrouped span.\n */\n getText(): string {\n return this._text;\n }\n\n /**\n * Returns the optional background color this cell adopts, or `null`\n * when the header-band gradient shows through unaltered.\n *\n * @returns The CSS color string, or `null`.\n */\n getColor(): string | null {\n return this._color;\n }\n\n /**\n * Wires the host-supplied context-menu listener and attaches the\n * tooltip after the framework has mounted the element. Sub-tree\n * listener so right-clicks on the rendered label bubble up here.\n *\n * @param element - Optional element forwarded by the framework init chain.\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement();\n\n if (!el) {\n return this;\n }\n\n Event.addSubtreeListener(this, \"contextmenu\", this.onContextMenu);\n\n if (this._tooltipText) {\n Tooltip.attachToElement(el, this._tooltipText);\n }\n\n return this;\n }\n\n /**\n * Subtree contextmenu handler. Suppresses the browser's native menu\n * and fires the `\"contextmenu\"` event with the viewport coordinates\n * (the host's listener typically opens the table's column-toggle menu).\n *\n * @param e - The contextmenu event captured from a descendant.\n */\n private onContextMenu(e: MouseEvent): void {\n e.preventDefault();\n\n this.emit(\"contextmenu\", e.clientX, e.clientY);\n }\n}\n\nconst ParentHeaderCellCallable = callable(ParentHeaderCell);\ntype ParentHeaderCellCallable = ParentHeaderCell;\nexport {\n ParentHeaderCell as _ParentHeaderCell,\n ParentHeaderCellCallable as ParentHeaderCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { Row } from \"~/component/table/Row.js\";\nimport { AbstractModel } from \"~/data/AbstractModel.js\";\nimport { AbstractStore, SortDescriptor } from \"~/data/AbstractStore.js\";\nimport { Field } from \"~/data/Field.js\";\nimport { Column } from \"~/component/table/Column.js\";\nimport { HeaderCell } from \"~/component/table/cell/Header.js\";\nimport { ParentHeaderCell } from \"~/component/table/cell/ParentHeader.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n// The header surface, themed via `--ts-ui-table-header-bg`. The value is\n// applied as BOTH a background-color and a background-image because the token\n// is a flat colour in some themes (e.g. ModernTheme) and a gradient in others\n// (e.g. ClassicTheme): a colour is invalid as a background-image (resolves to\n// `none`) and a gradient is invalid as a background-color (resolves to\n// transparent), so setting both lets whichever form the active theme supplies\n// paint while the other harmlessly drops out. Setting only background-image —\n// the previous behaviour — left the surface transparent under a flat-colour\n// theme, which surfaced as a see-through scrollbar-cover band.\nconst TABLE_HEADER_BG = \"var(--ts-ui-table-header-bg, var(--ts-ui-button-bg, linear-gradient(rgb(241, 241, 241), rgb(200, 200, 200))))\";\n\n/**\n * String-literal union of the events emitted by the table {@link TableHeader}.\n *\n * @category Components\n */\nexport type TableHeaderEvent = \"columnresizestart\" | \"columnresize\" | \"columncontextmenu\";\n\n/**\n * The header section of a table, rendered as a `<thead>` element.\n *\n * Builds one {@link HeaderCell} per visible field from the supplied model. Each cell is\n * wired with a sort-click callback (cycles asc → desc → clear), a resize-drag\n * callback (forwarded to the owner via the `\"columnresize\"` event), and a\n * context-menu callback (forwarded via the `\"columncontextmenu\"` event); see\n * {@link TableHeader.on}.\n *\n * Re-exported as `TableHeader` from the package barrel.\n *\n * @category Components\n */\nclass TableHeader extends Component {\n\n private _model: AbstractModel;\n private _store: AbstractStore;\n private _hiddenColumns: Set<string> = new Set();\n private _columns: Column[] = [];\n private _listeners: ListenerBag<TableHeaderEvent> = new ListenerBag<TableHeaderEvent>();\n private _scrollbarCover: Handle | null = null;\n\n constructor(model: AbstractModel, store: AbstractStore) {\n super({ tag: \"thead\" });\n\n this.getAria().setRole(\"rowgroup\");\n this.setBorder({ borderBottom: \"1px solid var(--ts-ui-table-header-border, black)\" });\n this.setBackgroundColor(TABLE_HEADER_BG);\n this.setBackgroundImage(TABLE_HEADER_BG);\n // Clip cells that would otherwise extend past the header's right\n // edge when the inner rows are translated horizontally.\n this.setOverflow(\"hidden\");\n\n this._model = model;\n this._store = store;\n\n // Two `Row` children — parent row at index 0, column row at index 1.\n // The parent row collapses to zero height when no visible column\n // declares a group; existing no-group tables remain byte-identical\n // at runtime because `rebuildParentCells` produces no cells in\n // that case and the layout manager zeroes the parent-row height.\n const parentRow = new Row();\n const row = new Row();\n this.addRow(parentRow);\n this.addRow(row);\n\n this.rebuildCells();\n this.rebuildParentCells();\n }\n\n /**\n * Returns the model driving this header's columns.\n *\n * @returns The {@link AbstractModel} currently bound to this header.\n */\n getModel() {\n return this._model;\n }\n\n /**\n * Replaces the model, rebuilding header cells only when the visible field list changes.\n *\n * @param model - The new model to bind to the header.\n *\n * @remarks If the new model has the same visible fields in the same order as the current\n * model, the existing cells are left in place and sort indicators are re-synced.\n */\n setModel(model: AbstractModel): this {\n const toNames = (model: AbstractModel) =>\n model.getFields()\n .slice()\n .filter(f => !this._hiddenColumns.has(f.getName()))\n .sort((a, b) => a.getOrder() - b.getOrder())\n .map(f => f.getName());\n\n const oldNames = toNames(this._model);\n const newNames = toNames(model);\n\n const same = oldNames.length === newNames.length\n && oldNames.every((n, i) => n === newNames[i]);\n\n if (!same) {\n this._model = model;\n this.rebuildCells();\n this.rebuildParentCells();\n }\n\n this.syncSortIndicators();\n\n return this;\n }\n\n /**\n * Updates the set of hidden column field names and rebuilds both header\n * rows immediately.\n *\n * Field names belonging to {@link Column.isUnhideable} columns are stripped\n * from the set so a direct caller cannot bypass the unhideable contract.\n *\n * @param hidden - The new set of field names to hide.\n */\n setHiddenColumns(hidden: Set<string>): this {\n const filtered = new Set<string>();\n\n for (const name of hidden) {\n const col = this._columns.find(c => c.getField().getName() === name);\n\n if (!col || !col.isUnhideable()) {\n filtered.add(name);\n }\n }\n\n this._hiddenColumns = filtered;\n\n this.rebuildCells();\n this.rebuildParentCells();\n\n return this;\n }\n\n /**\n * Supplies the resolved {@link Column} list so that per-column header metadata\n * (e.g. `headerGlyph`, `group`) is available when cells are rebuilt.\n *\n * @param columns - The resolved columns in display order.\n * @returns This header, for method chaining.\n */\n setColumns(columns: Column[]): this {\n this._columns = columns;\n\n this.rebuildCells();\n this.rebuildParentCells();\n\n return this;\n }\n\n /**\n * Registers a listener for one of this header's events.\n *\n * @param event - `\"columnresizestart\"` fires on mousedown over a column\n * resize handle, receiving the zero-based column index and the absolute\n * pointer `clientX` at the moment the drag began; `\"columnresize\"` fires\n * when the user drags a column resize handle, receiving the zero-based\n * column index and the absolute pointer `clientX`; `\"columncontextmenu\"`\n * fires on a right-click anywhere in the header band, receiving the field\n * name (empty string when the click landed on a parent-header cell) and\n * the viewport x/y coordinates.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This header, for method chaining.\n */\n on(event: \"columnresizestart\", listener: (colIndex: number, clientX: number) => void): this;\n on(event: \"columnresize\", listener: (colIndex: number, clientX: number) => void): this;\n on(event: \"columncontextmenu\", listener: (fieldName: string, x: number, y: number) => void): this;\n on(event: TableHeaderEvent, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered listener. The exact callback reference\n * must match.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This header, for method chaining.\n */\n off(event: TableHeaderEvent, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every listener registered for `event` with `payload`, in\n * registration order.\n *\n * @param event - The event to emit.\n * @param payload - Forwarded to each listener.\n */\n protected emit(event: \"columnresizestart\", colIndex: number, clientX: number): void;\n protected emit(event: \"columnresize\", colIndex: number, clientX: number): void;\n protected emit(event: \"columncontextmenu\", fieldName: string, x: number, y: number): void;\n protected emit(event: TableHeaderEvent, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Returns the header cell components in column order. The column row\n * lives at child index 1 — the parent-header row sits at index 0.\n *\n * @returns An array of cell components from the column-header row.\n */\n getColumns() {\n return this.getComponents()[1].getComponents();\n }\n\n /**\n * Returns the parent-header row hosting the {@link ParentHeaderCell}\n * instances. Always present, even when no visible column declares a\n * group — in that case the row has zero cells and the layout manager\n * collapses it to zero height.\n *\n * @returns The parent row.\n */\n getParentRow(): Row {\n return this.getComponents()[0] as Row;\n }\n\n /**\n * Returns `true` when at least one visible column declares a group —\n * i.e. the parent-header row is rendered with non-zero height.\n * Driven by {@link Column.getGroup} across the resolved visible\n * column list.\n *\n * @returns `true` when the parent row should be laid out, `false`\n * when it should collapse.\n */\n hasParentRow(): boolean {\n return this._columns\n .filter(c => !this._hiddenColumns.has(c.getField().getName()))\n .some(c => c.getGroup() !== null);\n }\n\n /**\n * Returns the cover element that masks the vertical-scrollbar reservation\n * band at the header's right edge. Created lazily on first access; sized\n * and positioned by the table layout. Carries the same gradient as the\n * header so cells translated horizontally appear to clip at the trackW\n * boundary while the reservation band stays visually continuous with\n * the rest of the header.\n */\n getScrollbarCover(): Handle {\n if (this._scrollbarCover === null) {\n // `cover` is a raw presentational `<div>` owned by this header, not a\n // Component, so the Component style setters don't apply and direct\n // `.style` writes are correct here.\n const cover = DOM.sink.createElement(\"div\");\n DOM.sink.apply(cover, { style: {\n \"position\": \"absolute\",\n \"top\": \"0\",\n \"boxSizing\": \"border-box\",\n // Inner rows are Components with `z-index: 0`, which creates a\n // stacking context that paints AFTER positioned siblings with\n // `z-index: auto`. Without an explicit z-index here the cover\n // would be painted beneath the rows and cells could be seen\n // bleeding into the scrollbar-reservation band.\n \"zIndex\": \"1\",\n // Presentational only; don't intercept pointer events so column\n // resize handles whose cells happen to be horizontally scrolled\n // under the cover still receive clicks.\n \"pointerEvents\": \"none\",\n \"backgroundColor\": TABLE_HEADER_BG,\n \"backgroundImage\": TABLE_HEADER_BG,\n // Left border matches the column-cell right border so the cover\n // reads as a visual continuation of the column separators\n // rather than a seam in the gradient.\n \"borderLeft\": \"1px solid var(--ts-ui-table-resize-handle-color, rgba(0, 0, 0, 0.2))\",\n } });\n DOM.sink.appendChild(this.getElement(true)!, cover);\n // Track the header-owned cover so it is released with the header\n // (on destructor or GC), not left pinned in the registry.\n this.trackHandle(cover);\n this._scrollbarCover = cover;\n }\n\n return this._scrollbarCover;\n }\n\n /**\n * Reorders header cells by field order using their layout constraints.\n */\n sortColumns() {\n const row = this.getComponents()[1];\n\n row.sortComponents((c1, c2) => {\n const lc1 = row.getLayoutConstraints(c1);\n const lc2 = row.getLayoutConstraints(c2);\n\n if (!lc1) {\n return -1;\n }\n\n if (!lc2) {\n return 1;\n }\n\n return (lc1.data as Field).getOrder() - (lc2.data as Field).getOrder();\n });\n }\n\n /**\n * Appends a row to the header.\n *\n * @param row - The row to append.\n */\n addRow(row: Row) : this {\n this.addComponent(row);\n\n return this;\n }\n\n /**\n * Adds a row as a child component of the header.\n *\n * @param row - The row component to add.\n *\n * @returns This component, for method chaining.\n */\n addComponent(row: Row): this {\n super.addComponent(row);\n\n return this;\n }\n\n /**\n * Sets the header width and propagates it to both the parent and\n * column rows so the full header band shares the same width.\n *\n * @param width - The width in pixels.\n *\n * @returns This component, for method chaining.\n */\n setWidth(width: number): this {\n super.setWidth(width);\n\n const rows = this.getComponents();\n rows[0].setWidth(width);\n rows[1].setWidth(width);\n\n return this;\n }\n\n /**\n * Sets the header height. Row-level heights (parent row + column row)\n * are set independently by the table layout manager — `TableHeader` itself\n * just stores the total band height. The per-row height assignments\n * live in `layout/Table.doLayout` because the split depends on\n * {@link hasParentRow}.\n *\n * @param height - The total header band height in pixels.\n *\n * @returns This component, for method chaining.\n */\n setHeight(height: number): this {\n super.setHeight(height);\n\n return this;\n }\n\n /**\n * Reconciles the column-row's header cells against the current\n * model's visible fields. Surviving cells (whose field is still\n * visible) keep their sort indicator, resize-handle wiring,\n * tooltip, theme listener, and `setColumnFocused` state; cells for\n * newly-visible fields are constructed and wired; cells for\n * now-hidden fields are removed.\n *\n * Operates on the column row at child index 1; the parent row at\n * index 0 is rebuilt separately in {@link rebuildParentCells}.\n */\n private rebuildCells(): void {\n const row = this.getComponents()[1] as Row;\n\n const targetFields = this._model.getFields()\n .slice()\n .filter(f => !this._hiddenColumns.has(f.getName()))\n .sort((a, b) => a.getOrder() - b.getOrder());\n\n const columnMap = new Map(this._columns.map(c => [c.getField().getName(), c]));\n const existing = row.getComponents().slice() as HeaderCell[];\n const byName = new Map<string, HeaderCell>();\n\n for (const cell of existing) {\n const lc = row.getLayoutConstraints(cell);\n const field = lc?.data as Field | undefined;\n\n if (field) {\n byName.set(field.getName(), cell);\n }\n }\n\n const targetNames = new Set(targetFields.map(f => f.getName()));\n\n for (const cell of existing) {\n const lc = row.getLayoutConstraints(cell);\n const field = lc?.data as Field | undefined;\n\n if (!field || !targetNames.has(field.getName())) {\n row.removeComponent(cell);\n }\n }\n\n for (let i = 0; i < targetFields.length; i++) {\n const field = targetFields[i];\n const col = columnMap.get(field.getName());\n let cell = byName.get(field.getName());\n\n if (!cell) {\n const glyph = col?.getHeaderGlyph() ?? null;\n\n cell = new HeaderCell(field.getName(), field.getName(), glyph);\n cell.setTooltip(field.getDescription());\n\n row.addComponent(cell, { data: field });\n\n // Wire exactly once, at creation. The resize/sort/context\n // closures resolve the cell's visible-column index live (via\n // getColumns) at emit time, so a later hide/show that shifts\n // indices needs no re-wiring. Re-wiring a surviving cell would\n // stack duplicate listeners on its ListenerBag — making a\n // single drag emit `columnresize` several times with mismatched\n // indices, and a single header click cycle the sort twice.\n this.wireCell(cell);\n }\n\n // Tint the column header with the group's `groupColor` so\n // the header band reads as one visual group above the\n // matching body-cell tint applied in Row.ts. `Cell`'s\n // theme-change listener only re-applies the border, so\n // this background survives a theme swap. Re-applied on\n // every sync so a config swap that changed the tint also\n // takes effect.\n const groupColor = col?.getGroupColor();\n\n if (groupColor) {\n cell.setBackgroundColor(groupColor);\n }\n\n // Re-applied on every sync (unconditionally, not gated on\n // an `isRequired()` truthy check) so a config swap that\n // clears `required` also clears the asterisk on a\n // surviving cell — mirrors the group-tint cadence above.\n cell.setRequired(col?.isRequired() ?? false);\n }\n\n // Re-order children to the new visible-field display order so\n // sibling iteration (e.g. `syncSortIndicators`) matches the\n // visible-field list index.\n row.sortComponents((c1, c2) => {\n const f1 = (row.getLayoutConstraints(c1)?.data as Field).getOrder();\n const f2 = (row.getLayoutConstraints(c2)?.data as Field).getOrder();\n\n return f1 - f2;\n });\n\n this.syncSortIndicators();\n }\n\n /**\n * Removes all existing parent-header cells and recreates one\n * {@link ParentHeaderCell} per contiguous run of visible columns\n * sharing the same group key. Ungrouped columns each produce a\n * blank spanning cell so the parent row's surface stays continuous.\n *\n * The visible-column order is read from `_columns` filtered by\n * `_hiddenColumns` and sorted by {@link Field.getOrder} — same\n * resolution path used by {@link rebuildCells}. Each cell's\n * `spanFrom` / `spanTo` indices are stored in its layout constraints'\n * `data` slot; the table layout manager reads them to position the\n * cell as the sum of underlying column widths.\n */\n private rebuildParentCells(): void {\n const row = this.getParentRow();\n\n row.removeAllComponents();\n\n const visibleCols = this._columns\n .filter(c => !this._hiddenColumns.has(c.getField().getName()))\n .sort((a, b) => a.getField().getOrder() - b.getField().getOrder());\n\n if (visibleCols.length === 0) {\n return;\n }\n\n let runStart = 0;\n let runKey = visibleCols[0].getGroup();\n let runColor = visibleCols[0].getGroupColor();\n\n const flush = (endExclusive: number): void => {\n const cell = new ParentHeaderCell(runKey ?? \"\", runColor);\n\n // Field names spanned by this cell — drives the tooltip\n // and is also useful context if a future column-toggle\n // menu wants to operate on a whole group.\n const fieldNames = visibleCols\n .slice(runStart, endExclusive)\n .map(c => c.getField().getName());\n\n if (runKey !== null && fieldNames.length > 0) {\n cell.setTooltip(`${runKey}: ${fieldNames.join(\", \")}`);\n }\n\n cell.on(\"contextmenu\", (x, y) => {\n this.emit(\"columncontextmenu\", \"\", x, y);\n });\n\n row.addComponent(cell, { data: { spanFrom: runStart, spanTo: endExclusive - 1 } });\n };\n\n for (let i = 1; i < visibleCols.length; i++) {\n const nextKey = visibleCols[i].getGroup();\n // Ungrouped columns (group === null) each get their own blank\n // span — they never merge with adjacent ungrouped columns.\n // A run continues only when both sides share the same\n // non-null group key.\n const runContinues = runKey !== null && nextKey === runKey;\n\n if (!runContinues) {\n flush(i);\n\n runStart = i;\n runKey = nextKey;\n runColor = visibleCols[i].getGroupColor();\n } else if (runColor === null && visibleCols[i].getGroupColor() !== null) {\n // First non-null `groupColor` in a run wins; pick it up\n // when an earlier column omitted the field and a later\n // one supplied it.\n runColor = visibleCols[i].getGroupColor();\n }\n }\n\n flush(visibleCols.length);\n }\n\n /**\n * Wires the sort, resize, and context-menu callbacks for one cell. Called\n * exactly once per cell, at creation.\n *\n * @param cell - The header cell whose listeners are being attached.\n *\n * @remarks The resize callbacks report the cell's *current* visible-column\n * index by looking it up live through {@link getColumns} when the event\n * fires, rather than capturing an index at wiring time. This keeps the\n * index correct after a hide/show/reorder shuffles the columns without\n * re-wiring — re-wiring would stack duplicate listeners on the surviving\n * cell's `ListenerBag`.\n */\n private wireCell(cell: HeaderCell): void {\n cell.on(\"sortclick\", (fieldName, shiftKey) => this.handleSortClick(fieldName, shiftKey));\n cell.on(\"resizestart\", (clientX) => this.emit(\"columnresizestart\", this.getColumns().indexOf(cell), clientX));\n cell.on(\"resizedrag\", (clientX) => this.emit(\"columnresize\", this.getColumns().indexOf(cell), clientX));\n cell.on(\"contextmenu\", (fieldName, x, y) => this.emit(\"columncontextmenu\", fieldName, x, y));\n }\n\n /**\n * Handles a click on a header cell, cycling sort state on the underlying store.\n *\n * @param fieldName - The field associated with the clicked column.\n * @param shiftKey - Whether the shift key was held; toggles multi-column sort composition.\n *\n * @remarks\n * Without shift, behaviour is asc → desc → cleared on the clicked column.\n * With shift, the column is appended/toggled within the existing sort list:\n * not present → append asc; asc → flip to desc; desc → remove from the list.\n */\n private handleSortClick(fieldName: string, shiftKey: boolean): void {\n if (shiftKey) {\n const sorters = this._store.getActiveSorters();\n const idx = sorters.findIndex(s => s.field === fieldName);\n let next: SortDescriptor[];\n\n if (idx === -1) {\n next = [...sorters, { field: fieldName, dir: 'asc' }];\n } else if (sorters[idx].dir === 'asc') {\n next = sorters.map((s, i) =>\n i === idx ? { field: s.field, dir: 'desc' } : s\n );\n } else {\n next = sorters.filter((_, i) => i !== idx);\n }\n\n if (next.length === 0) {\n this._store.clearSort();\n } else {\n this._store.sort(next);\n }\n } else {\n const sorters = this._store.getActiveSorters();\n const current = sorters.length === 1 && sorters[0].field === fieldName\n ? sorters[0] : null;\n\n if (!current) {\n this._store.sort(fieldName, 'asc');\n } else if (current.dir === 'asc') {\n this._store.sort(fieldName, 'desc');\n } else {\n this._store.clearSort();\n }\n }\n\n this.syncSortIndicators();\n }\n\n /**\n * Refreshes every visible header cell's sort arrow and priority badge\n * to match the store's current `activeSorters` list.\n */\n private syncSortIndicators(): void {\n const cells = this.getColumns() as HeaderCell[];\n const visibleFields = this._model.getFields()\n .slice()\n .filter(f => !this._hiddenColumns.has(f.getName()))\n .sort((a, b) => a.getOrder() - b.getOrder());\n\n const sorters = this._store.getActiveSorters();\n const fieldToSorter = new Map(sorters.map((s, i) => [s.field, { dir: s.dir, priority: i + 1 }]));\n const showPriority = sorters.length > 1;\n\n cells.forEach((cell, i) => {\n const fieldName = visibleFields[i]?.getName();\n const entry = fieldName ? fieldToSorter.get(fieldName) : undefined;\n\n if (entry) {\n cell.setSortState(entry.dir, showPriority ? entry.priority : null);\n } else {\n cell.clearSortState();\n }\n });\n }\n}\n\nconst TableHeaderCallable = callable(TableHeader);\ntype TableHeaderCallable = TableHeader;\nexport {\n TableHeader as _TableHeader,\n TableHeaderCallable as TableHeader\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellEditor } from \"~/component/table/cell/editor/CellEditor.js\";\nimport { TextField } from \"~/component/input/TextField.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * An in-place editor for string cell values.\n *\n * Wraps a {@link TextField} and proxies blur and keydown events up to the parent\n * cell so the standard commit/cancel lifecycle works. Caches the typed\n * text on each input event; an empty field commits as `null`, mirroring\n * the cell-stack convention that \"no value\" is `null` and not `\"\"`.\n *\n * @category Components\n */\nclass StringEditor extends CellEditor<String | null> {\n\n private _textField: TextField = new TextField();\n private _value: String | null = null;\n\n constructor() {\n super();\n\n // Internal cell-editor wiring: listens on a privately-owned child;\n // see the cell-editor carve-out in ARCHITECTURE.md.\n Event.addListener(this._textField, \"blur\", (evnt: UIEvent) => {\n Event.fireEvent(this, \"blur\", evnt);\n });\n Event.addListener(this._textField, \"keydown\", (evnt: KeyboardEvent) => {\n Event.fireEvent(this, \"keydown\", { detail: {\n key : evnt.key , code : evnt.code , keyCode: evnt.keyCode,\n shiftKey: evnt.shiftKey, ctrlKey: evnt.ctrlKey,\n altKey : evnt.altKey , metaKey: evnt.metaKey\n } });\n });\n Event.addListener(this._textField, \"input\", () => this.onInput());\n\n this._textField.setMaxSize(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);\n this._textField.clearPadding();\n this._textField.setBorderRadius('0');\n this._textField.setBorder({ border: \"0px solid transparent\" });\n this._textField.setShadow('inset 0 0 0 1px var(--ts-ui-table-cell-editor-border, rgba(30, 100, 200, 0.6))');\n this._textField.setOutline('none');\n this._textField.setText(\"\");\n this.addComponent(this._textField);\n }\n\n /**\n * Returns the cached string value, or `null` when the field is\n * empty. The distinction between a typed `\"\"` and `null` is\n * intentionally dropped — empty input is empty input.\n *\n * @returns The current string value, or `null`.\n */\n getValue(): String | null {\n return this._value;\n }\n\n /**\n * Populates the text field and caches the value. `null` and\n * `undefined` populate an empty field.\n *\n * @param value - The string value to set in the text field, or\n * `null`/`undefined` to leave the field empty.\n */\n setValue(value: String | null): this {\n this._value = value ?? null;\n this._textField.setText(this._value === null ? \"\" : String(this._value));\n\n return this;\n }\n\n /**\n * Focuses the text field and selects all its content.\n *\n * @param preventScroll - Forwarded to the field's focus so a native\n * focus-scroll doesn't desync the table body's own scroll model.\n * @returns This component, for method chaining.\n */\n focus(preventScroll: boolean = false): this {\n this._textField.focus(preventScroll);\n this._textField.select();\n\n return this;\n }\n\n /**\n * Reads the live text field content into the cached value. An\n * empty string becomes `null`.\n */\n private onInput(): void {\n const raw = this._textField.getText();\n this._value = raw ? raw : null;\n }\n}\n\nconst StringEditorCallable = callable(StringEditor);\ntype StringEditorCallable = StringEditor;\nexport {\n StringEditor as _StringEditor,\n StringEditorCallable as StringEditor\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellEditor } from \"~/component/table/cell/editor/CellEditor.js\";\nimport { TextField } from \"~/component/input/TextField.js\";\nimport { AnchorType } from \"~/layout/AnchorType.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * An in-place editor for numeric cell values.\n *\n * Wraps a right-aligned {@link TextField} and proxies blur and keydown events\n * up to the parent cell so the standard commit/cancel lifecycle works.\n * Caches the parsed numeric value on each input event so an empty or\n * unparseable field commits as `null` rather than silently coercing to\n * `0` or `NaN`.\n *\n * @category Components\n */\nclass NumberEditor extends CellEditor<Number | null> {\n\n private _textField: TextField = new TextField();\n private _value: Number | null = null;\n\n constructor() {\n super();\n\n // Internal cell-editor wiring: listens on a privately-owned child;\n // see the cell-editor carve-out in ARCHITECTURE.md.\n Event.addListener(this._textField, \"blur\", (evnt: UIEvent) => {\n Event.fireEvent(this, \"blur\", evnt);\n });\n Event.addListener(this._textField, \"keydown\", (evnt: KeyboardEvent) => {\n Event.fireEvent(this, \"keydown\", { detail: {\n key : evnt.key , code : evnt.code , keyCode: evnt.keyCode,\n shiftKey: evnt.shiftKey, ctrlKey: evnt.ctrlKey,\n altKey : evnt.altKey , metaKey: evnt.metaKey\n } });\n });\n Event.addListener(this._textField, \"input\", () => this.onInput());\n\n this.setMaxSize(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);\n this._textField.clearPadding();\n this.setBorderRadius(\"0\");\n this._textField.setBorder({ border: \"0px solid transparent\" });\n this._textField.setShadow('inset 0 0 0 1px var(--ts-ui-table-cell-editor-border, rgba(30, 100, 200, 0.6))');\n this._textField.setOutline('none');\n this._textField.setTextAlign(\"right\");\n this._textField.setText(\"\");\n\n this.addComponent(this._textField, {\n anchor: AnchorType.NORTHEAST\n });\n }\n\n /**\n * Returns the cached numeric value, or `null` when the field is\n * empty or contains unparseable text. Reads the private cache\n * rather than re-parsing the text on demand so an empty field\n * commits as `null` instead of `0`, and unparseable text commits\n * as `null` instead of `NaN`.\n *\n * @returns The parsed numeric value, or `null`.\n */\n getValue(): Number | null {\n return this._value;\n }\n\n /**\n * Populates the text field with the number as a string and caches\n * the value. `null` and `undefined` populate an empty field so the\n * user sees a blank input on first edit instead of the literal\n * text `\"undefined\"` / `\"null\"`.\n *\n * @param value - The numeric value to set in the text field, or\n * `null`/`undefined` to leave the field empty.\n */\n setValue(value: Number | null): this {\n this._value = value ?? null;\n this._textField.setText(this._value === null ? \"\" : String(this._value));\n\n return this;\n }\n\n /**\n * Focuses the text field and selects all its content.\n *\n * @param preventScroll - Forwarded to the field's focus so a native\n * focus-scroll doesn't desync the table body's own scroll model.\n * @returns This component, for method chaining.\n */\n focus(preventScroll: boolean = false): this {\n this._textField.focus(preventScroll);\n this._textField.select();\n\n return this;\n }\n\n /**\n * Parses the live text field content into the cached value. An\n * empty string or unparseable text becomes `null`; otherwise the\n * cached value is the numeric `Number(raw)`.\n */\n private onInput(): void {\n const raw = this._textField.getText();\n\n if (!raw) {\n this._value = null;\n\n return;\n }\n\n const n = Number(raw);\n this._value = isNaN(n) ? null : n;\n }\n}\n\nconst NumberEditorCallable = callable(NumberEditor);\ntype NumberEditorCallable = NumberEditor;\nexport {\n NumberEditor as _NumberEditor,\n NumberEditorCallable as NumberEditor\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellEditor } from \"~/component/table/cell/editor/CellEditor.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\n\n/**\n * Base class for cell editors backed by a native `<input>` element.\n *\n * Owns the typed setters for the three input attributes that every\n * bare-input editor needs to declare its identity — `type`, `inputmode`,\n * and `autocomplete`. Subclasses call these from their constructor instead\n * of routing through `Component.setElementAttribute` directly, which keeps\n * the rule that behaviour-affecting attributes never reach the string-keyed\n * attribute API at the call site.\n *\n * Cannot reuse `Input`'s typed setters because [`CellEditor`](/api/component/table/classes/CellEditor)\n * extends [`Component`](/api/core/classes/Component) (not `Input`):\n * `Input` carries form-submission `name` plumbing and a default font CSS\n * rule that cell editors do not need, and most non-text editors prefer a\n * `<div>` root.\n *\n * @category Components\n */\nexport abstract class TextInputCellEditor<T> extends CellEditor<T> {\n\n private _type: string | null = null;\n private _inputMode: string | null = null;\n private _autoComplete: string | null = null;\n\n constructor() {\n super(\"input\");\n }\n\n /**\n * Sets the HTML `type` attribute on the underlying input element. Called\n * once from a subclass constructor to declare the editor's input shape.\n *\n * @param value - The input type (e.g. `\"text\"`, `\"number\"`).\n *\n * @returns This component, for method chaining.\n */\n protected setType(value: string): this {\n this._type = value;\n this.setElementAttribute(\"type\", value);\n\n return this;\n }\n\n /**\n * Sets the HTML `inputmode` attribute, which controls the on-screen\n * keyboard surface on mobile. Cell editors typically pass `\"none\"` so\n * the virtual keyboard does not pop while a picker dropdown is open.\n *\n * @param value - A valid `inputmode` value.\n *\n * @returns This component, for method chaining.\n */\n protected setInputMode(value: string): this {\n this._inputMode = value;\n this.setElementAttribute(\"inputmode\", value);\n\n return this;\n }\n\n /**\n * Sets the HTML `autocomplete` attribute, which controls browser\n * autofill. Cell editors typically pass `\"off\"` to keep autofill\n * suggestions from interfering with the picker dropdown.\n *\n * @param value - A valid `autocomplete` token.\n *\n * @returns This component, for method chaining.\n */\n protected setAutoComplete(value: string): this {\n this._autoComplete = value;\n this.setElementAttribute(\"autocomplete\", value);\n\n return this;\n }\n\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement()!;\n\n if (this._type !== null) {\n DOM.sink.apply(el, { setAttr: { \"type\": this._type } });\n }\n\n if (this._inputMode !== null) {\n DOM.sink.apply(el, { setAttr: { \"inputmode\": this._inputMode } });\n }\n\n if (this._autoComplete !== null) {\n DOM.sink.apply(el, { setAttr: { \"autocomplete\": this._autoComplete } });\n }\n\n return this;\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { TextInputCellEditor } from \"~/component/table/cell/editor/TextInputCellEditor.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport { DatePickerDropdown } from \"~/component/input/DatePickerDropdown.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * An in-place editor for date cell values.\n *\n * Renders a focusable `<input type=\"text\" inputmode=\"none\">` element and\n * pops a [`DatePickerDropdown`](/api/component/input/classes/DatePickerDropdown)\n * (the same class used by the form-field\n * [`DateField`](/api/component/input/classes/DateField)) on focus. The\n * dropdown's row `pointerdown` handler calls `preventDefault()` so the input\n * keeps focus while the user clicks a day — preserving the\n * [`CellEditorPool`](/api/component/table/classes/CellEditorPool)'s\n * blur-to-commit contract without modifying the pool.\n *\n * @category Components\n */\nclass DateEditor extends TextInputCellEditor<Date | null> {\n\n private _value: Date | null = null;\n private _dropdown: DatePickerDropdown | null = null;\n private _animated: boolean = true;\n private _text: string = \"\";\n\n constructor() {\n super();\n\n this.setMaxSize(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);\n this.setBorderRadius('0');\n this.setBorder({ border: \"0px solid transparent\" });\n this.setShadow('inset 0 0 0 1px var(--ts-ui-table-cell-editor-border, rgba(30, 100, 200, 0.6))');\n this.setOutline('none');\n\n Event.addListener(this, \"focus\", () => this.openDropdown());\n Event.addListener(this, \"blur\", () => this.closeDropdown());\n Event.addListener(this, \"input\", () => this.onInput());\n\n this.setType(\"text\");\n this.setInputMode(\"none\");\n this.setAutoComplete(\"off\");\n }\n\n /**\n * Returns whether the input is empty (no typed text, no committed value).\n *\n * @returns true when both the rendered text and the cached value are empty.\n */\n isEmpty(): boolean {\n return !this.getText() && this._value === null;\n }\n\n /**\n * Returns the currently committed `Date`, or null when the field is empty\n * or contains an unparseable value.\n */\n getValue(): Date | null {\n return this._value;\n }\n\n /**\n * Sets the field value and renders the formatted string in the input.\n *\n * @param value - The Date to display, or null to clear the field.\n */\n setValue(value: Date | null): this {\n this._value = value;\n this.setText(value ? this.toInputString(value) : \"\");\n\n return this;\n }\n\n /**\n * Writes `text` into the underlying `<input>` element's value through a\n * typed setter so call sites never touch `element.value` directly.\n *\n * @param text - The string to render in the input.\n */\n private setText(text: string): this {\n this._text = text;\n\n const el = this.getElement();\n if (el) {\n DOM.sink.setValue(el, text);\n }\n\n return this;\n }\n\n /**\n * Returns the cached input text. Stays in sync via {@link syncTextFromDom}\n * which is called from the input listener before `onInput`.\n */\n private getText(): string {\n return this._text;\n }\n\n /**\n * Reads the live value from the `<input>` element into the text cache.\n * Confines the raw `element.value` read to a single typed helper so\n * `onInput` can work off {@link getText}.\n */\n private syncTextFromDom(): void {\n const el = this.getElement();\n this._text = el ? DOM.source.getValue(el) : \"\";\n }\n\n /**\n * Enables or disables the fade animation on the dropdown.\n *\n * @param value - true to fade, false for instant open/close.\n */\n setDropdownAnimated(value: boolean): this {\n this._animated = value;\n\n if (this._dropdown) {\n this._dropdown.setAnimated(value);\n }\n\n return this;\n }\n\n /**\n * Returns whether the dropdown fade is enabled.\n *\n * @returns true when the dropdown fades; false when it opens/closes instantly.\n */\n isDropdownAnimated(): boolean {\n return this._animated;\n }\n\n /**\n * Returns or lazily creates the picker dropdown.\n */\n private ensureDropdown(): DatePickerDropdown {\n if (!this._dropdown) {\n this._dropdown = new DatePickerDropdown(date => this.onDateSelected(date));\n this._dropdown.setAnimated(this._animated);\n }\n\n return this._dropdown;\n }\n\n /**\n * Opens the picker dropdown anchored to the input element.\n */\n private openDropdown(): void {\n const dropdown = this.ensureDropdown();\n if (dropdown.isOpen()) {\n return;\n }\n\n const el = this.getElement();\n if (!el) {\n return;\n }\n\n dropdown.showAt(el, this._value);\n }\n\n /**\n * Hides the picker dropdown if it is currently open. Called when the\n * editor's input element loses focus (the cell exits edit mode).\n */\n private closeDropdown(): void {\n if (this._dropdown?.isOpen()) {\n this._dropdown.hideAnimated();\n }\n }\n\n /**\n * Updates the cached value from a typed text edit.\n */\n private onInput(): void {\n this.syncTextFromDom();\n const raw = this.getText();\n\n if (!raw) {\n this._value = null;\n return;\n }\n\n const d = new Date(raw + 'T00:00:00');\n this._value = isNaN(d.getTime()) ? null : d;\n }\n\n /**\n * Called when the user picks a day in the dropdown. Sets the value,\n * hides the dropdown, and fires the editor's blur to drive\n * `CellEditorPool`'s commit path.\n */\n private onDateSelected(date: Date): void {\n this.setValue(date);\n this._dropdown?.hideAnimated();\n\n const el = this.getElement();\n\n if (el) {\n DOM.sink.blur(el);\n }\n }\n\n private toInputString(date: Date): string {\n const y = date.getFullYear();\n const m = String(date.getMonth() + 1).padStart(2, '0');\n const d = String(date.getDate()).padStart(2, '0');\n return `${y}-${m}-${d}`;\n }\n}\n\nconst DateEditorCallable = callable(DateEditor);\ntype DateEditorCallable = DateEditor;\nexport {\n DateEditor as _DateEditor,\n DateEditorCallable as DateEditor\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { TextInputCellEditor } from \"~/component/table/cell/editor/TextInputCellEditor.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport { TimePickerDropdown } from \"~/component/input/TimePickerDropdown.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * An in-place editor for time cell values.\n *\n * Renders a focusable text input and pops a\n * [`TimePickerDropdown`](/api/component/input/classes/TimePickerDropdown)\n * (the same class used by [`TimeField`](/api/component/input/classes/TimeField))\n * on focus. The dropdown suppresses focus loss on `pointerdown` so the\n * [`CellEditorPool`](/api/component/table/classes/CellEditorPool)'s blur-to-commit\n * path is not invoked prematurely.\n *\n * The value is represented as a `Date` whose time portion is meaningful; the\n * date portion is normalised to 1970-01-01 local.\n *\n * @category Components\n */\nclass TimeEditor extends TextInputCellEditor<Date | null> {\n\n private _showSeconds: boolean;\n private _value: Date | null = null;\n private _dropdown: TimePickerDropdown | null = null;\n private _animated: boolean = true;\n private _text: string = \"\";\n\n constructor(showSeconds: boolean = false) {\n super();\n this._showSeconds = showSeconds;\n\n this.setMaxSize(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);\n this.setBorderRadius('0');\n this.setBorder({ border: \"0px solid transparent\" });\n this.setShadow('inset 0 0 0 1px var(--ts-ui-table-cell-editor-border, rgba(30, 100, 200, 0.6))');\n this.setOutline('none');\n\n Event.addListener(this, \"focus\", () => this.openDropdown());\n Event.addListener(this, \"blur\", () => this.closeDropdown());\n Event.addListener(this, \"input\", () => this.onInput());\n\n this.setType(\"text\");\n this.setInputMode(\"none\");\n this.setAutoComplete(\"off\");\n }\n\n /**\n * Returns whether the input is empty.\n *\n * @returns true when both the rendered text and the cached value are empty.\n */\n isEmpty(): boolean {\n return !this.getText() && this._value === null;\n }\n\n /**\n * Returns the currently committed `Date`, or null.\n */\n getValue(): Date | null {\n return this._value;\n }\n\n /**\n * Sets the field value and renders the formatted string in the input.\n *\n * @param value - The Date to display, or null to clear the field.\n */\n setValue(value: Date | null): this {\n this._value = value;\n this.setText(value ? this.toInputString(value) : \"\");\n\n return this;\n }\n\n /**\n * Writes `text` into the underlying `<input>` element's value through a\n * typed setter so call sites never touch `element.value` directly.\n *\n * @param text - The string to render in the input.\n */\n private setText(text: string): this {\n this._text = text;\n\n const el = this.getElement();\n if (el) {\n DOM.sink.setValue(el, text);\n }\n\n return this;\n }\n\n /**\n * Returns the cached input text. Stays in sync via {@link syncTextFromDom}\n * which is called from the input listener before `onInput`.\n */\n private getText(): string {\n return this._text;\n }\n\n /**\n * Reads the live value from the `<input>` element into the text cache.\n * Confines the raw `element.value` read to a single typed helper so\n * `onInput` can work off {@link getText}.\n */\n private syncTextFromDom(): void {\n const el = this.getElement();\n this._text = el ? DOM.source.getValue(el) : \"\";\n }\n\n /**\n * Enables or disables the fade animation on the dropdown.\n *\n * @param value - true to fade, false for instant open/close.\n */\n setDropdownAnimated(value: boolean): this {\n this._animated = value;\n\n if (this._dropdown) {\n this._dropdown.setAnimated(value);\n }\n\n return this;\n }\n\n /**\n * Returns whether the dropdown fade is enabled.\n *\n * @returns true when the dropdown fades; false when it opens/closes instantly.\n */\n isDropdownAnimated(): boolean {\n return this._animated;\n }\n\n /**\n * Returns or lazily creates the picker dropdown.\n */\n private ensureDropdown(): TimePickerDropdown {\n if (!this._dropdown) {\n this._dropdown = new TimePickerDropdown(\n (h: number, m: number, s: number) => this.onTimeSelected(h, m, s),\n { showSeconds: this._showSeconds },\n );\n this._dropdown.setAnimated(this._animated);\n }\n\n return this._dropdown;\n }\n\n /**\n * Opens the picker dropdown anchored to the input element.\n */\n private openDropdown(): void {\n const dropdown = this.ensureDropdown();\n if (dropdown.isOpen()) {\n return;\n }\n\n const el = this.getElement();\n if (!el) {\n return;\n }\n\n dropdown.showAt(el, this._value);\n }\n\n /**\n * Hides the picker dropdown if it is currently open. Called when the\n * editor's input element loses focus (the cell exits edit mode).\n */\n private closeDropdown(): void {\n if (this._dropdown?.isOpen()) {\n this._dropdown.hideAnimated();\n }\n }\n\n /**\n * Updates the cached value from a typed text edit.\n */\n private onInput(): void {\n this.syncTextFromDom();\n const raw = this.getText();\n\n if (!raw) {\n this._value = null;\n return;\n }\n\n const parts = raw.split(':').map(Number);\n if (parts.some(isNaN)) {\n this._value = null;\n return;\n }\n this._value = new Date(1970, 0, 1, parts[0], parts[1] ?? 0, parts[2] ?? 0);\n }\n\n /**\n * Called when the user picks a time in the dropdown. Updates the value\n * and re-renders the formatted text but does NOT hide the dropdown — the\n * user typically tweaks the minute after picking the hour. The\n * [`CellEditorPool`](/api/component/table/classes/CellEditorPool) commits when\n * focus eventually leaves the editor (e.g. Tab, Enter, click-outside).\n *\n * @param hours - The chosen hour (0-23).\n * @param minutes - The chosen minute (0-59).\n * @param seconds - The chosen second (0-59). Always 0 when `showSeconds` is false.\n */\n private onTimeSelected(hours: number, minutes: number, seconds: number): void {\n const d = new Date(1970, 0, 1, hours, minutes, seconds, 0);\n this.setValue(d);\n }\n\n private toInputString(date: Date): string {\n const h = String(date.getHours()).padStart(2, '0');\n const m = String(date.getMinutes()).padStart(2, '0');\n if (this._showSeconds) {\n const s = String(date.getSeconds()).padStart(2, '0');\n return `${h}:${m}:${s}`;\n }\n return `${h}:${m}`;\n }\n}\n\nconst TimeEditorCallable = callable(TimeEditor);\ntype TimeEditorCallable = TimeEditor;\nexport {\n TimeEditor as _TimeEditor,\n TimeEditorCallable as TimeEditor\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { TextInputCellEditor } from \"~/component/table/cell/editor/TextInputCellEditor.js\";\nimport { blurRelatedTargetHandle } from \"~/component/table/cell/editor/CellEditor.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { LayerManager } from \"~/core/LayerManager.js\";\nimport { DateTimePickerDropdown } from \"~/component/input/DateTimePickerDropdown.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * An in-place editor for date-time cell values.\n *\n * Renders a focusable text input and pops a\n * [`DateTimePickerDropdown`](/api/component/input/classes/DateTimePickerDropdown)\n * (the same class used by [`DateTimeField`](/api/component/input/classes/DateTimeField))\n * on focus. The dropdown suppresses focus loss via `pointerdown`\n * preventDefault so the\n * [`CellEditorPool`](/api/component/table/classes/CellEditorPool)'s blur-to-commit\n * path is not invoked prematurely.\n *\n * @category Components\n */\nclass DateTimeEditor extends TextInputCellEditor<Date | null> {\n\n private _showSeconds: boolean;\n private _value: Date | null = null;\n private _dropdown: DateTimePickerDropdown | null = null;\n private _animated: boolean = true;\n private _text: string = \"\";\n\n constructor(showSeconds: boolean = false) {\n super();\n this._showSeconds = showSeconds;\n\n this.setMaxSize(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);\n this.setBorderRadius('0');\n this.setBorder({ border: \"0px solid transparent\" });\n this.setShadow('inset 0 0 0 1px var(--ts-ui-table-cell-editor-border, rgba(30, 100, 200, 0.6))');\n this.setOutline('none');\n\n Event.addListener(this, \"focus\", () => this.openDropdown());\n Event.addListener(this, \"blur\", (e: FocusEvent) => this.onEditorBlur(e));\n Event.addListener(this, \"input\", () => this.onInput());\n\n this.setType(\"text\");\n this.setInputMode(\"none\");\n this.setAutoComplete(\"off\");\n }\n\n /**\n * Keeps the edit alive while focus moves from the editor input into the\n * picker dropdown — including the embedded time field and its own popped\n * time-picker layer. Without this, focusing the embedded field would blur\n * the editor and the {@link CellEditorPool} would commit + tear down the\n * dropdown mid-interaction.\n *\n * @param relatedTarget - The node handle receiving focus, or null.\n * @returns True when focus landed inside the dropdown's layer tree.\n */\n retainsFocus(relatedTarget: Handle | null): boolean {\n return this._dropdown !== null\n && this._dropdown.isOpen()\n && LayerManager.containsAcrossLayers(this._dropdown, relatedTarget);\n }\n\n /**\n * Returns whether the input is empty.\n *\n * @returns true when both the rendered text and the cached value are empty.\n */\n isEmpty(): boolean {\n return !this.getText() && this._value === null;\n }\n\n /**\n * Returns the currently committed `Date`, or null.\n */\n getValue(): Date | null {\n return this._value;\n }\n\n /**\n * Sets the field value and renders the formatted string in the input.\n *\n * @param value - The Date to display, or null to clear the field.\n */\n setValue(value: Date | null): this {\n this._value = value;\n this.setText(value ? this.toInputString(value) : \"\");\n\n return this;\n }\n\n /**\n * Writes `text` into the underlying `<input>` element's value through a\n * typed setter so call sites never touch `element.value` directly.\n *\n * @param text - The string to render in the input.\n */\n private setText(text: string): this {\n this._text = text;\n\n const el = this.getElement();\n if (el) {\n DOM.sink.setValue(el, text);\n }\n\n return this;\n }\n\n /**\n * Returns the cached input text. Stays in sync via {@link syncTextFromDom}\n * which is called from the input listener before `onInput`.\n */\n private getText(): string {\n return this._text;\n }\n\n /**\n * Reads the live value from the `<input>` element into the text cache.\n * Confines the raw `element.value` read to a single typed helper so\n * `onInput` can work off {@link getText}.\n */\n private syncTextFromDom(): void {\n const el = this.getElement();\n this._text = el ? DOM.source.getValue(el) : \"\";\n }\n\n /**\n * Enables or disables the fade animation on the dropdown.\n *\n * @param value - true to fade, false for instant open/close.\n */\n setDropdownAnimated(value: boolean): this {\n this._animated = value;\n\n if (this._dropdown) {\n this._dropdown.setAnimated(value);\n }\n\n return this;\n }\n\n /**\n * Returns whether the dropdown fade is enabled.\n *\n * @returns true when the dropdown fades; false when it opens/closes instantly.\n */\n isDropdownAnimated(): boolean {\n return this._animated;\n }\n\n /**\n * Returns or lazily creates the picker dropdown.\n */\n private ensureDropdown(): DateTimePickerDropdown {\n if (!this._dropdown) {\n this._dropdown = new DateTimePickerDropdown(\n date => this.onDateTimeSelected(date),\n { showSeconds: this._showSeconds },\n );\n this._dropdown.setAnimated(this._animated);\n }\n\n return this._dropdown;\n }\n\n /**\n * Opens the picker dropdown anchored to the input element. The dropdown's\n * own `\"click-outside\"` mode (driven by\n * [`LayerManager`](/api/core/namespaces/LayerManager)) commits the edit\n * when a pointerdown lands outside the editing surface — including when\n * focus has moved into the embedded time field — via the close thunk\n * installed here.\n */\n private openDropdown(): void {\n const dropdown = this.ensureDropdown();\n if (dropdown.isOpen()) {\n return;\n }\n\n const el = this.getElement();\n if (!el) {\n return;\n }\n\n // The manager dismisses the dropdown on an outside click; route that\n // through the editor so it also commits. The editor input is excluded\n // as the anchor so opening the editor doesn't immediately dismiss.\n dropdown.setCloseHandler(() => this.onOutsideDismiss());\n dropdown.setAnchorElement(el);\n\n dropdown.showAt(el, this._value);\n }\n\n /**\n * Hides the picker dropdown if it is currently open.\n */\n private closeDropdown(): void {\n if (this._dropdown?.isOpen()) {\n this._dropdown.hideAnimated();\n }\n }\n\n /**\n * Closes the dropdown and commits the edit when the manager reports an\n * outside click. This is the path the editor's blur-commit can no longer\n * cover once focus sits in the embedded time field rather than the\n * editor's own input.\n */\n private onOutsideDismiss(): void {\n this.closeDropdown();\n this.requestCommit();\n }\n\n /**\n * Closes the dropdown when the editor input blurs — unless focus moved into\n * the picker surface (the embedded time field or its time-picker layer), in\n * which case the edit stays open. The {@link CellEditorPool}'s blur-commit\n * is suppressed for the same case via {@link retainsFocus}.\n *\n * @param e - The blur event.\n */\n private onEditorBlur(e: FocusEvent): void {\n if (this.retainsFocus(blurRelatedTargetHandle(e))) {\n return;\n }\n\n this.closeDropdown();\n }\n\n /**\n * Updates the cached value from a typed text edit. The display format is\n * \"YYYY-MM-DD HH:MM[:SS]\" with a space separator; `Date.parse` only\n * accepts ISO-8601 reliably with a `T`, so we re-introduce it before\n * parsing.\n */\n private onInput(): void {\n this.syncTextFromDom();\n const raw = this.getText();\n\n if (!raw) {\n this._value = null;\n return;\n }\n\n const d = new Date(raw.replace(' ', 'T'));\n this._value = isNaN(d.getTime()) ? null : d;\n }\n\n /**\n * Called when the user picks a date+time in the dropdown.\n *\n * @param date - The chosen Date.\n */\n private onDateTimeSelected(date: Date): void {\n this.setValue(date);\n }\n\n private toInputString(date: Date): string {\n const y = date.getFullYear();\n const mo = String(date.getMonth() + 1).padStart(2, '0');\n const d = String(date.getDate()).padStart(2, '0');\n const h = String(date.getHours()).padStart(2, '0');\n const mi = String(date.getMinutes()).padStart(2, '0');\n if (this._showSeconds) {\n const s = String(date.getSeconds()).padStart(2, '0');\n return `${y}-${mo}-${d} ${h}:${mi}:${s}`;\n }\n return `${y}-${mo}-${d} ${h}:${mi}`;\n }\n}\n\nconst DateTimeEditorCallable = callable(DateTimeEditor);\ntype DateTimeEditorCallable = DateTimeEditor;\nexport {\n DateTimeEditor as _DateTimeEditor,\n DateTimeEditorCallable as DateTimeEditor\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Event } from \"~/core/Event.js\";\nimport type { Cell } from \"~/component/table/cell/Cell.js\";\nimport { CellEditor, blurRelatedTargetHandle } from \"~/component/table/cell/editor/CellEditor.js\";\nimport type { ForwardedKeyDetail } from \"~/component/table/cell/editor/CellEditor.js\";\nimport { StringEditor } from \"~/component/table/cell/editor/String.js\";\nimport { NumberEditor } from \"~/component/table/cell/editor/Number.js\";\nimport { DateEditor } from \"~/component/table/cell/editor/Date.js\";\nimport { TimeEditor } from \"~/component/table/cell/editor/Time.js\";\nimport { DateTimeEditor } from \"~/component/table/cell/editor/DateTime.js\";\n\n/**\n * Factory callable that constructs a fresh {@link CellEditor} for a given pool key.\n *\n * @category Components\n */\nexport type CellEditorFactory = () => CellEditor<unknown>;\n\n/**\n * Per-table registry that holds at most one shared editor instance per editor variant.\n *\n * Every cell in [`Body`](/api/component/table/classes/Body) used to allocate its own editor up\n * front, but only one cell can be edited at a time. The pool keeps the renderer-per-row model\n * untouched while collapsing the N editors down to one instance per cell-provided key returned\n * from {@link Cell.getEditorKey}. On {@link CellEditorPool.acquire} the editor is lazily\n * constructed (and its blur/keydown listeners wired once), then re-used across every subsequent\n * edit of the same variant.\n *\n * Built-in keys are seeded by the constructor:\n *\n * - `\"string\"`, `\"number\"`, `\"date\"`\n * - `\"time\"` / `\"time:seconds\"`\n * - `\"datetime\"` / `\"datetime:seconds\"`\n *\n * Custom cells can opt in by overriding {@link Cell.getEditorKey} and registering a factory via\n * {@link CellEditorPool.register} before the first edit. Cells that return `null` from\n * `getEditorKey` (e.g. `BooleanCell`, `GlyphCell`, `DefaultCell`) keep their legacy behaviour.\n *\n * @category Components\n */\nexport class CellEditorPool {\n\n private _editors : Map<string, CellEditor<unknown>> = new Map();\n private _factories : Map<string, CellEditorFactory> = new Map();\n private _activeCell: Cell<any> | null = null;\n\n /**\n * Constructs a pool pre-seeded with factories for every built-in typed cell.\n */\n constructor() {\n this._factories.set(\"string\", () => new StringEditor());\n this._factories.set(\"number\", () => new NumberEditor());\n this._factories.set(\"date\", () => new DateEditor());\n this._factories.set(\"time\", () => new TimeEditor(false));\n this._factories.set(\"time:seconds\", () => new TimeEditor(true));\n this._factories.set(\"datetime\", () => new DateTimeEditor(false));\n this._factories.set(\"datetime:seconds\", () => new DateTimeEditor(true));\n }\n\n /**\n * Registers or overrides a factory for the given pool key.\n *\n * @param key - The editor variant key, matching {@link Cell.getEditorKey}.\n * @param factory - A factory that constructs a fresh editor instance.\n * @returns This pool, for method chaining.\n *\n * @remarks If an editor was already cached for the key it is dropped so the new factory runs\n * on the next call to {@link CellEditorPool.acquire}.\n */\n register(key: string, factory: CellEditorFactory): this {\n this._factories.set(key, factory);\n this._editors.delete(key);\n\n return this;\n }\n\n /**\n * Returns the shared editor for `key`, lazily constructing it on first call and marking\n * `cell` as the active edit target.\n *\n * @param key - The editor variant key returned by {@link Cell.getEditorKey}.\n * @param cell - The cell that is starting an edit; receives subsequent blur/keydown events.\n * @returns The shared editor instance, or `null` if no factory is registered for `key`.\n */\n acquire(key: string, cell: Cell<any>): CellEditor<unknown> | null {\n const factory = this._factories.get(key);\n if (!factory) {\n return null;\n }\n\n let editor = this._editors.get(key);\n if (!editor) {\n editor = factory();\n this.wireListeners(editor);\n this._editors.set(key, editor);\n }\n\n this._activeCell = cell;\n\n return editor;\n }\n\n /**\n * Clears the active-cell pointer. Called by {@link Cell} when an edit commits or cancels.\n */\n release(): void {\n this._activeCell = null;\n }\n\n /**\n * Wires `blur` and `keydown` listeners on the shared editor exactly once at construction,\n * dispatching through `activeCell` so the same listener serves every cell that borrows the\n * editor.\n *\n * @param editor - The freshly constructed editor to wire.\n */\n private wireListeners(editor: CellEditor<unknown>): void {\n editor.setCommitRequestHandler(() => this._activeCell?.commitEdit());\n\n // Internal cell-editor wiring: listens on a privately-owned child;\n // see the cell-editor carve-out in ARCHITECTURE.md.\n Event.addListener(editor, \"blur\", (e: FocusEvent) => {\n if (editor.retainsFocus(blurRelatedTargetHandle(e))) {\n return;\n }\n\n this._activeCell?.commitEdit();\n });\n Event.addListener(editor, \"keydown\", (e: CustomEvent<ForwardedKeyDetail>) => {\n this._activeCell?.onKeyDown(e);\n });\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellEditor } from \"~/component/table/cell/editor/CellEditor.js\";\nimport { ComboBox } from \"~/component/input/ComboBox.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { normalizeComboOptions } from \"~/component/table/ColumnConfig.js\";\nimport type { ComboOption } from \"~/component/table/ColumnConfig.js\";\nimport type { SelectableListItem } from \"~/component/list/AbstractSelectableList.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * An in-place editor for constrained-choice (combo-box) cell values.\n *\n * Wraps a [`ComboBox`](/api/component/input/classes/ComboBox) whose option\n * list is fixed to the column's declared values. The combo box keeps DOM\n * focus on its own surface and forwards keystrokes into its dropdown\n * without a focus shift, so a row click never blurs the editor. Both a\n * selection (`\"action\"`) and a genuine blur — focus leaving the editor\n * entirely, e.g. an outside click — commit the edit through the cell's\n * commit-request hook; keydown is proxied to the parent cell so Escape\n * cancels and Enter ends the edit.\n *\n * The editor stores the option *value* (the code round-tripped to the\n * record); an empty selection commits as `null`, mirroring the cell-stack\n * convention that \"no value\" is `null` and not `\"\"`.\n *\n * @category Components\n */\nclass ComboEditor extends CellEditor<String | null> {\n\n private _combo: ComboBox;\n private _value: String | null = null;\n\n /**\n * @param optionList - The column's option set; each entry is a plain\n * string (value === label) or a `{ value, label }` pair. Built into\n * explicit `{ key, label }` dropdown items so the combo box's value\n * is the option value, not its array index.\n */\n constructor(optionList: Array<ComboOption | string>) {\n super(\"div\");\n\n const items: Array<SelectableListItem> = normalizeComboOptions(optionList).map(\n option => ({ key: option.value, label: option.label }),\n );\n\n this._combo = new ComboBox();\n this._combo.setItems(items);\n\n // Strip the combo box's own chrome and apply the shared inset\n // editor border so the surface reads as a cell editor, matching\n // the text-input editors.\n this._combo.setBorder({ border: \"0px solid transparent\" });\n this._combo.setBorderRadius('0');\n this._combo.setShadow('inset 0 0 0 1px var(--ts-ui-table-cell-editor-border, rgba(30, 100, 200, 0.6))');\n\n // A selection (mouse or keyboard) commits the edit, and so does a\n // genuine blur: the combo box keeps DOM focus on its own surface\n // throughout the dropdown's lifetime (row clicks `preventDefault`\n // pointerdown), so a blur means focus left the editor entirely —\n // commit the current value. Both route through the cell's\n // commit-request hook directly rather than re-dispatching a blur\n // for the pool to re-interpret.\n this._combo.on(\"action\", () => this.commitFromCombo());\n // Internal cell-editor wiring: listens on a privately-owned child;\n // see the cell-editor carve-out in ARCHITECTURE.md.\n Event.addListener(this._combo, \"blur\", () => this.commitFromCombo());\n\n // Proxy keydown to the parent cell so Escape cancels and Enter\n // ends the edit, mirroring the text editors' `Cell.onKeyDown` path.\n // Internal cell-editor wiring: listens on a privately-owned child;\n // see the cell-editor carve-out in ARCHITECTURE.md.\n Event.addListener(this._combo, \"keydown\", (evnt: KeyboardEvent) => {\n Event.fireEvent(this, \"keydown\", { detail: {\n key : evnt.key , code : evnt.code , keyCode: evnt.keyCode,\n shiftKey: evnt.shiftKey, ctrlKey: evnt.ctrlKey,\n altKey : evnt.altKey , metaKey: evnt.metaKey\n } });\n });\n\n this.addComponent(this._combo);\n }\n\n /**\n * Returns the cached option value, or `null` when nothing is selected.\n *\n * @returns The current option value, or `null`.\n */\n getValue(): String | null {\n return this._value;\n }\n\n /**\n * Selects the option whose value matches and caches it. `null` and\n * `undefined` clear the selection. A value outside the option set is\n * cached verbatim so the cell round-trips it unchanged.\n *\n * @param value - The option value to select, or `null`/`undefined` to\n * clear the selection.\n */\n setValue(value: String | null): this {\n this._value = value ?? null;\n this._combo.setValue(this._value === null ? \"\" : String(this._value));\n\n return this;\n }\n\n /**\n * Focuses the combo box surface and pops its dropdown so a double-click\n * on the cell lands the user straight in the option list.\n *\n * @param preventScroll - Forwarded to the combo box's focus so a native\n * focus-scroll doesn't desync the table body's own scroll model.\n * @returns This component, for method chaining.\n */\n focus(preventScroll: boolean = false): this {\n this._combo.focus(preventScroll);\n this._combo.openDropdown();\n\n return this;\n }\n\n /**\n * Rebuilds the dropdown's item list from a new option set, preserving\n * the currently selected value, so a shared editor can be reused across\n * rows whose combo options differ.\n *\n * @param optionList - The new option set; same shape as the constructor\n * argument.\n * @returns This editor, for method chaining.\n */\n setOptions(optionList: Array<ComboOption | string>): this {\n const items: Array<SelectableListItem> = normalizeComboOptions(optionList).map(\n option => ({ key: option.value, label: option.label }),\n );\n\n this._combo.setItems(items);\n this._combo.setValue(this._value === null ? \"\" : String(this._value));\n\n return this;\n }\n\n /**\n * Caches the combo box's current value and asks the active cell to\n * commit. Shared by the selection (`\"action\"`) and blur paths; an empty\n * selection commits as `null`. Re-entrant-safe: once the cell has\n * committed it releases the editor, so a follow-on blur is a no-op.\n */\n private commitFromCombo(): void {\n const selected = this._combo.getValue();\n\n this._value = selected ? selected : null;\n this.requestCommit();\n }\n}\n\nconst ComboEditorCallable = callable(ComboEditor);\ntype ComboEditorCallable = ComboEditor;\nexport {\n ComboEditor as _ComboEditor,\n ComboEditorCallable as ComboEditor\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { AbstractStore } from \"~/data/AbstractStore.js\";\nimport { ModelRecord } from \"~/data/ModelRecord.js\";\nimport { Row } from \"~/component/table/Row.js\";\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport { CellEditorPool } from \"~/component/table/cell/editor/CellEditorPool.js\";\nimport { ComboEditor } from \"~/component/table/cell/editor/Combo.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { VirtualRowView } from \"~/component/shared/VirtualRowView.js\";\nimport { reduceModifierSelection } from \"~/component/shared/reduceModifierSelection.js\";\nimport { ThemeManager } from \"~/core/Theme.js\";\nimport { Util } from \"~/core/Util.js\";\nimport type { ColumnConfig } from \"~/component/table/ColumnConfig.js\";\nimport { Column } from \"~/component/table/Column.js\";\nimport type { TableHeader } from \"~/component/table/Header.js\";\nimport type { HeaderCell } from \"~/component/table/cell/Header.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * String-literal union of the events emitted by the table {@link Body}.\n * `\"verticalscroll\"` / `\"horizontalscroll\"` fire after the body's virtual\n * scroll position changes, carrying the new pixel offset. `\"selection\"`\n * fires when the selected-record set changes; `\"cellclick\"` fires when a data\n * cell is clicked.\n */\nexport type BodyEvent = \"verticalscroll\" | \"horizontalscroll\" | \"selection\" | \"cellclick\";\n\n/**\n * Payload delivered to a `\"cellclick\"` listener when a data cell is clicked.\n *\n * `field` (the model field name) is the stable column identity; `columnIndex`\n * matches the visible-column order the body exposes via a row's cells and the\n * keyboard-focused column. `rowIndex` is the record's position in the filtered +\n * sorted view (`getVisibleRecords()`), the same basis selection uses — never a\n * pool-slot index. `value` is read live from the record at click time.\n */\nexport interface CellClickEvent {\n /** The clicked row's bound record. */\n record: ModelRecord;\n /** The clicked column's model field name. */\n field: string;\n /** The clicked column's index in visible-column order. */\n columnIndex: number;\n /** `record.get(field)` at click time. */\n value: unknown;\n /** The record's index into the body's visible-records list. */\n rowIndex: number;\n /** The raw DOM mouse event that triggered the click. */\n event: MouseEvent;\n}\n\n/**\n * Returns the index of the cell in `cells` whose element is, or contains, the\n * clicked `target` handle; `-1` when the target lies outside every cell (or is\n * null). Pure with respect to the interned handles — no component state.\n *\n * @param cells - The clicked row's cells, in visible-column order.\n * @param target - The interned click-target handle, or null.\n *\n * @returns The matching cell index, or `-1`.\n *\n * @internal\n */\nexport function resolveClickedColumn(cells: Component[], target: Handle | null): number {\n for (let ci = 0; ci < cells.length; ci++) {\n const cellEl = cells[ci].getElement();\n\n if (cellEl && (cellEl === target || DOM.source.contains(cellEl, target))) {\n return ci;\n }\n }\n\n return -1;\n}\n\nfunction columnWidthsEqual(a: number[], b: number[] | undefined): boolean {\n if (!b) return a.length === 0;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/**\n * Virtual-scrolling body for the Table component.\n *\n * Only the rows visible in the viewport plus SCROLL_BUFFER rows above and below\n * are ever in the DOM. The store is queried on every render so the body always\n * reflects current store state without maintaining a duplicate data array.\n *\n * A fixed pool of Row components (`rowPool`) is reused as the user scrolls.\n * Each pool slot is tracked in `boundIndices`: when a slot is mapped to a new\n * data index, `row.setData()` is called to rebind cell values; if the index\n * hasn't changed (e.g. during a pure resize) the call is skipped, avoiding the\n * text-measurement reflow inside `setText()`.\n *\n * Scrolling is delegated to a `VirtualScroller` that owns the\n * rows-container transform, two custom scrollbar overlays, and the wheel/touch\n * handlers with fling momentum.\n *\n * Re-exported as `TableBody` from the package barrel.\n *\n * @category Components\n */\nclass Body extends VirtualRowView<Row> {\n\n private _store : AbstractStore;\n private _hiddenColumns : Set<string> = new Set();\n private _columns : Column[] = [];\n private _columnConfigs : Map<string, ColumnConfig> = new Map();\n private _rowReadOnly : ((record: ModelRecord) => boolean) | null = null;\n private _cellGeom : Array<Array<{ x: number, w: number, h: number } | null>> = [];\n private _lastBodyWidth : number = 0;\n private _lastColumnWidths: number[] = [];\n private _lastAriaRowCount: number = -1;\n private _rowHeight : number;\n private _storeRefresh : (() => void) | null = null;\n private _selectedRecords : Set<ModelRecord> = new Set();\n private _anchorRecord : ModelRecord | null = null;\n private _focusedColIndex: number = 0;\n private _editorPool : CellEditorPool = new CellEditorPool();\n private _header : TableHeader | null = null;\n private _listeners : ListenerBag<BodyEvent> = new ListenerBag<BodyEvent>();\n\n constructor(store: AbstractStore) {\n super({ tag: \"tbody\" });\n\n this.setOverflow(\"hidden\");\n this.setBackgroundColor(\"var(--ts-ui-input-bg, rgb(255, 255, 255))\");\n this.getAria().setTabIndex(0);\n this.getAria().setRole(\"rowgroup\");\n\n this._store = store;\n this.bindStore(store);\n\n this._rowHeight = this.computeRowHeight();\n\n this.subscribeTheme(() => {\n this._rowHeight = this.computeRowHeight();\n this._boundIndices.fill(-1);\n this.invalidateGeom();\n this.renderWindow();\n });\n }\n\n /**\n * Derives the row height from the shared px line box plus top+bottom cell\n * padding.\n *\n * @remarks `theme.table.cell.height` is intentionally ignored: a fixed pixel\n * height ignores the active line box and clips text when the theme changes\n * the leading. The line box is the additive `font-size + --ts-ui-line-padding`\n * value `Util.lineHeightPx` derives at the root font size, keeping row\n * height in sync with the line box the cells are actually rendered at.\n */\n private computeRowHeight(): number {\n const theme = ThemeManager.getTheme();\n const lineHeight = Util.lineHeightPx();\n const padding = theme.table.cell.padding ?? 2;\n\n return lineHeight + 2 * padding;\n }\n\n /**\n * Returns the live row height. The base's window / geometry math reads it\n * on every call so a theme-driven `_rowHeight` recompute takes effect\n * immediately.\n */\n protected getRowHeight(): number {\n return this._rowHeight;\n }\n\n /**\n * Constructs one pool row and wires each of its cells to the shared editor\n * pool + horizontal scroll-into-view handler. The base's `growRowPool` owns\n * the append + parallel-array bookkeeping.\n *\n * @returns The wired, un-appended pool row.\n */\n protected createPoolRow(): Row {\n const row = this.createRow();\n\n for (const cell of row.getComponents() as Cell<any>[]) {\n cell.setEditorPool(this._editorPool);\n cell.setScrollIntoViewHandler(() => this.scrollColumnIntoView(this._focusedColIndex));\n }\n\n return row;\n }\n\n /**\n * Extends the Body-only `_cellGeom` cache in lockstep with the base pool\n * arrays as each new slot is added.\n */\n protected onPoolRowAdded(): void {\n this._cellGeom.push([]);\n }\n\n /**\n * Subscribes to all relevant store events to trigger a renderWindow refresh.\n *\n * @param store - The store whose events to subscribe to.\n *\n * @remarks The single store-event refresh callback is routed through\n * {@link onStoreChange}, a protected hook that subclasses (e.g.\n * `TreeBody`) override to rebuild per-row indexes before the\n * inherited rebind + render runs.\n */\n private bindStore(store: AbstractStore): void {\n const refresh = () => this.onStoreChange();\n\n this._storeRefresh = refresh;\n\n store.on('load', refresh);\n store.on('add', refresh);\n store.on('remove', refresh);\n store.on('datachange', refresh);\n store.on('beforesync', refresh);\n store.on('sync', refresh);\n }\n\n /**\n * Hook invoked from {@link bindStore}'s store-event callbacks before\n * the row pool is rebound and rendered. Default behaviour clears the\n * bound-index cache and re-renders.\n *\n * @remarks Subclassing seam — `TreeBody` overrides this to rebuild\n * its parent/child index and flatten the visible subtree before\n * delegating to `super.onStoreChange()`. Not for consumer use.\n */\n protected onStoreChange(): void {\n this._boundIndices.fill(-1);\n this.renderWindow();\n }\n\n /**\n * Returns the records visible in the current scroll window. Default\n * behaviour delegates to the store's view (filtered + sorted master\n * collection).\n *\n * @returns The records the row pool should bind to, in display order.\n *\n * @remarks Subclassing seam — `TreeBody` overrides this to return its\n * depth-flattened, expansion-aware visible subtree. Every internal\n * site that needs the visible records — virtual-window math, click\n * dispatch, focus + active-descendant tracking, keyboard nav,\n * scroll-into-view — goes through this method. Not for consumer use.\n */\n protected getVisibleRecords(): ModelRecord[] {\n return this._store.getRecords();\n }\n\n /**\n * Constructs one pool row. Default behaviour returns a plain `Row`\n * bound to the store's model with the current hidden-column and\n * column-config maps.\n *\n * @returns A new `Row` instance.\n *\n * @remarks Subclassing seam — `TreeBody` overrides this to pass the\n * `treeFieldName` so the tree column's cell gets a\n * `TreeCellRenderer`. Not for consumer use.\n */\n protected createRow(): Row {\n return new Row(\n this._store.model,\n undefined,\n this._hiddenColumns,\n this._columnConfigs,\n (record) => this._store.notifyRecordChanged(record),\n );\n }\n\n /**\n * Returns the field name of the column carrying a\n * {@link TreeCellRenderer}, or `undefined` when no column is the\n * tree column. The base returns `undefined`; `TreeBody` overrides\n * to return its `_treeColumn`.\n *\n * @returns The tree column's field name, or `undefined`.\n *\n * @remarks Subclassing seam — forwarded into {@link Row.syncCells}\n * from {@link setHiddenColumns} / {@link setColumnConfigs} so an\n * incremental column-toggle preserves the tree renderer on the\n * surviving cell. Not for consumer use.\n */\n protected getTreeFieldName(): string | undefined {\n return undefined;\n }\n\n /**\n * Updates the ARIA attributes that depend on a row's current data\n * index. Default behaviour writes only `aria-rowindex` (the +2\n * accounts for the 1-based ARIA spec plus the header band).\n *\n * @param row - The pool row whose ARIA attributes to update.\n * @param dataIndex - The row's index into the visible-records list.\n *\n * @remarks Subclassing seam — `TreeBody` overrides this to additionally\n * set `aria-level`, `aria-expanded`, `aria-setsize`, and\n * `aria-posinset` from the flat record entry. Not for consumer use.\n */\n protected computeRowAria(row: Row, dataIndex: number): void {\n row.getAria().setRowIndex(dataIndex + 2);\n }\n\n /**\n * Hook invoked once per pool slot inside the bind loop, after the\n * row has been rebound (when needed) but before the geometry-driven\n * cell layout runs. Default behaviour is a no-op.\n *\n * @param row - The pool row being processed.\n * @param dataIndex - The row's index into the visible-records list.\n * @param wasRebound - `true` when the row was just rebound to a new\n * record on this pass; `false` for a pure scroll where the slot's\n * data index is unchanged.\n *\n * @remarks Subclassing seam — `TreeBody` overrides this to push\n * depth + expansion state through {@link TreeCellRenderer.setTreeState}\n * on the row's tree cell. Not for consumer use.\n */\n protected afterRowBound(_row: Row, _dataIndex: number, _wasRebound: boolean): void {\n // Default implementation is a no-op; subclasses provide behaviour.\n }\n\n /**\n * Resets every pool slot's data-index cache so the next render pass\n * forces a full rebind. Use after the visible-records list has\n * changed shape (sort, expand/collapse, etc.) but the store itself\n * hasn't fired one of the events {@link bindStore} subscribes to.\n *\n * @remarks Subclassing seam — `TreeBody` calls this from\n * {@link TreeBody.setExpanded} before triggering a re-render.\n * Not for consumer use.\n */\n protected invalidateRowBindings(): void {\n this._boundIndices.fill(-1);\n }\n\n /**\n * Returns the data store this body is bound to.\n *\n * @returns The current {@link AbstractStore}.\n *\n * @remarks Exposed at protected scope for subclasses (e.g. `TreeBody`)\n * that need to read model fields when reconstructing pool rows or\n * walking records to rebuild a depth index.\n */\n protected getStore(): AbstractStore {\n return this._store;\n }\n\n /**\n * Returns the set of column field names currently hidden from\n * render.\n *\n * @returns The hidden-column set (do not mutate).\n *\n * @remarks Exposed at protected scope so subclasses can pass the\n * same set into custom pool-row construction.\n */\n protected getHiddenColumns(): Set<string> {\n return this._hiddenColumns;\n }\n\n /**\n * Returns the column-config map keyed by field name.\n *\n * @returns The column-config map (do not mutate).\n *\n * @remarks Exposed at protected scope so subclasses can pass the\n * same map into custom pool-row construction.\n */\n protected getColumnConfigs(): Map<string, ColumnConfig> {\n return this._columnConfigs;\n }\n\n /**\n * Returns the row pool used by the virtual scroll. Each entry is a\n * `Row` whose `data` may be bound to a record or `undefined` when\n * the slot is hidden.\n *\n * @returns The row-pool array (do not mutate the array; mutating\n * individual rows is the caller's responsibility).\n *\n * @remarks Exposed at protected scope so subclasses can walk the\n * pool — e.g. `TreeBody` does this from its `onSubtreeClick`\n * override to find the row whose tree-cell toggle was clicked.\n */\n protected getRowPool(): Row[] {\n return this._rowPool;\n }\n\n /**\n * Clears the cached row geometry (via the base) and additionally the\n * Body-only per-cell geometry so the next renderWindow re-applies positions\n * and sizes for every visible row.\n */\n protected invalidateGeom(): void {\n super.invalidateGeom();\n\n for (let i = 0; i < this._cellGeom.length; i++) {\n this._cellGeom[i] = [];\n }\n }\n\n /**\n * Updates the set of hidden column field names, syncs each pooled\n * row's cells in place to match the new visible-field list, and\n * re-renders.\n *\n * Field names belonging to {@link Column.isUnhideable} columns are stripped\n * from the set so a direct caller cannot bypass the unhideable contract.\n *\n * @param hidden - The new set of field names to hide.\n *\n * @remarks The previous implementation dropped the entire row pool\n * and rebuilt it via `growRowPool`. The in-place sync preserves\n * each row's existing cells (and their renderer / editor / theme\n * listener / sort state / group tint), constructing or removing\n * only the cells whose visibility actually changed.\n */\n setHiddenColumns(hidden: Set<string>): this {\n const filtered = new Set<string>();\n\n for (const name of hidden) {\n const col = this._columns.find(c => c.getField().getName() === name);\n\n if (!col || !col.isUnhideable()) {\n filtered.add(name);\n }\n }\n\n this._hiddenColumns = filtered;\n this.syncPoolCells();\n this.renderWindow();\n\n return this;\n }\n\n /**\n * Supplies the resolved {@link Column} list so the body can read per-column\n * metadata (e.g. `isUnhideable()`) when filtering hidden-column sets.\n *\n * @param columns - The resolved columns in display order.\n *\n * @returns This body, for method chaining.\n */\n setColumns(columns: Column[]): this {\n this._columns = columns;\n this.syncPoolCells();\n this.renderWindow();\n\n return this;\n }\n\n /**\n * Sets the table-wide row-level read-only predicate forwarded from\n * {@link ColumnSpec.rowReadOnly}. Cleared by passing `null`.\n *\n * @param predicate - Returns `true` to mark every cell in the\n * record's row read-only. Called on every rebind; must be O(1)\n * and pure.\n * @returns This body, for method chaining.\n *\n * @remarks Internal wiring called by {@link Table} — not for\n * consumer use. Consumers declare the predicate in the spec.\n */\n setRowReadOnly(predicate: ((record: ModelRecord) => boolean) | null): this {\n this._rowReadOnly = predicate;\n\n return this;\n }\n\n /**\n * Updates the per-field column-config map and re-syncs each pooled\n * row's cells in place so any field-type-driven cell options\n * (e.g. `showSeconds`) and group tints take effect immediately.\n *\n * @param configs - The new column-config map keyed by field name.\n */\n setColumnConfigs(configs: Map<string, ColumnConfig>): this {\n this._columnConfigs = configs;\n this.registerComboEditors(configs);\n this.syncPoolCells();\n this.renderWindow();\n\n return this;\n }\n\n /**\n * Registers a per-column [`ComboEditor`](/api/component/table/classes/ComboEditor)\n * factory on the editor pool for every column declaring `values`, plus\n * every column declaring `cellValues` (a per-record `DynamicCell` combo\n * column). The factory closes over that column's resolved option set —\n * an empty seed for `cellValues` columns, since the real per-row options\n * are injected at edit time by `DynamicCell.prepareEditor` — so each\n * combo column borrows an editor wired to its own choices under the\n * `combo:<field>` key returned by\n * [`ComboCell.getEditorKey`](/api/component/table/classes/ComboCell#geteditorkey)\n * or `DynamicCell.getEditorKey`.\n *\n * `register` overwrites and drops any cached editor, so re-applying\n * configs with new options rebuilds the editor on the next edit.\n *\n * @param configs - The column-config map keyed by field name.\n */\n private registerComboEditors(configs: Map<string, ColumnConfig>): void {\n for (const [field, config] of configs) {\n const values = config.values;\n\n if (values && values.length > 0) {\n this._editorPool.register(`combo:${field}`, () => new ComboEditor(values));\n } else if (config.cellValues) {\n this._editorPool.register(`combo:${field}`, () => new ComboEditor([]));\n }\n }\n }\n\n /**\n * Walks every pool row and asks it to reconcile its cell set\n * against the current `_hiddenColumns` + `_columnConfigs`, then\n * invalidates the per-slot geometry caches so the next\n * `renderWindow` re-positions cells against the new column count.\n */\n private syncPoolCells(): void {\n const treeFieldName = this.getTreeFieldName();\n\n for (let i = 0; i < this._rowPool.length; i++) {\n const row = this._rowPool[i];\n\n row.syncCells(\n this._store.model,\n this._hiddenColumns,\n this._columnConfigs,\n treeFieldName,\n );\n\n // Newly-shown cells need the editor pool wired so in-place\n // editing keeps working. `setEditorPool` is idempotent for\n // surviving cells.\n for (const cell of row.getComponents() as Cell<any>[]) {\n cell.setEditorPool(this._editorPool);\n cell.setScrollIntoViewHandler(() => this.scrollColumnIntoView(this._focusedColIndex));\n }\n\n // Per-slot geometry is keyed by cell position; both the\n // column count and per-cell (x, w, h) have changed.\n this._rowGeom[i] = null;\n this._cellGeom[i] = [];\n }\n }\n\n /**\n * Swaps the store, unsubscribing from the old one and rebinding to the new one.\n *\n * @param store - The new store to bind to the body.\n */\n setStore(store: AbstractStore): this {\n if (this._storeRefresh) {\n const old = this._store;\n\n (['load', 'add', 'remove', 'datachange', 'beforesync', 'sync'] as const).forEach(e =>\n old.off(e, this._storeRefresh!)\n );\n }\n\n this._store = store;\n this.bindStore(store);\n this.invalidateGeom();\n\n if (this.getElement()) {\n // Route through `onStoreChange` so subclasses (e.g. `TreeBody`)\n // can rebuild their per-row index against the new store before\n // the inherited rebind + render runs. The base implementation\n // is equivalent to the previous `_boundIndices.fill(-1) +\n // renderWindow()` inline pair.\n this.onStoreChange();\n }\n\n return this;\n }\n\n /**\n * Initializes the body element, constructs the `VirtualScroller`, and\n * wires keyboard and focus listeners.\n *\n * @param element - Optional. The element handle to initialize with; falls back to `getElement()`.\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement();\n if (!el) {\n return this;\n }\n\n this.initScroller(el);\n\n Event.addListener(this, \"focus\", this.onFocus);\n Event.addListener(this, \"keydown\", this.onKeyDown);\n\n // One subtree click listener replaces the per-row listener that\n // growRowPool used to install. Walk up from the event target to find\n // the matching pool row; identical complexity per click, one window\n // registration regardless of pool size. Routed through\n // `onSubtreeClick` so subclasses (e.g. `TreeBody`) can intercept\n // clicks on subtree-owned widgets like the expand/collapse toggle.\n Event.addSubtreeListener(this, \"click\", this.onSubtreeClick);\n\n this.renderWindow();\n\n return this;\n }\n\n /**\n * On every scroller tick, re-renders the window and then emits the\n * unconditional scroll events consumers mirror (the header translate and\n * the pinned-side body). Overrides the base default, which only re-renders.\n */\n protected onScrollerTick(): void {\n this.renderWindow();\n\n if (this._scroller) {\n this.emit(\"verticalscroll\", this._scroller.getScrollY());\n this.emit(\"horizontalscroll\", this._scroller.getScrollX());\n }\n }\n\n /** Refreshes the active-descendant pointer and focus ring when the body gains focus. */\n private onFocus(): void {\n this._updateActiveDescendant();\n this._updateFocusStyle();\n }\n\n /**\n * Returns the shared editor pool that backs in-place editing for every cell in this body.\n *\n * @returns The {@link CellEditorPool} owned by this body.\n *\n * @remarks Use this to register a custom editor factory for a cell-specific key. Built-in\n * factories for the seven standard typed cells are seeded automatically.\n */\n getEditorPool(): CellEditorPool {\n return this._editorPool;\n }\n\n /**\n * Recomputes the visible row window, rebinds changed rows from the pool, and hides excess rows.\n *\n * @param bodyWidth - Optional. The total body width in pixels; cached and reused on scroll updates.\n * @param columnWidths - Optional. The per-column widths in pixels; derived from bodyWidth when omitted.\n */\n renderWindow(bodyWidth?: number, columnWidths?: number[]) {\n const element = this.getElement();\n if (!element || !this._scroller) {\n return;\n }\n\n const scroller = this._scroller;\n const records = this.getVisibleRecords();\n const totalRows = records.length;\n\n // Capture scroll positions before clampToContent / layoutScrollbars\n // (called below) potentially shrink them in place. Those calls don't\n // go through setScrollX/Y, so the VirtualScroller's onScroll hook\n // never fires — without an explicit notification here the header's\n // horizontal translate would stay stuck at the pre-clamp value when\n // a widen-to-fit layout drops scrollX back toward 0.\n const prevScrollX = scroller.getScrollX();\n const prevScrollY = scroller.getScrollY();\n\n this.updateColumnWidthCache(bodyWidth, columnWidths);\n\n // Loose-clamp scroll positions against the new content sizes before\n // reading them for the window calc.\n const totalHeight = totalRows * this._rowHeight;\n const totalColumnWidth = this._lastColumnWidths.reduce((s, w) => s + w, 0);\n const totalContentWidth = Math.max(this._lastBodyWidth, totalColumnWidth);\n\n scroller.clampToContent(totalContentWidth, totalHeight);\n\n const visibleHeight = this.getHeight() || 0;\n const win = this.computeVisibleWindow(scroller.getScrollY(), visibleHeight, totalRows);\n\n const poolTarget = this.computePoolTarget(win.windowSize, visibleHeight, totalRows);\n this.growRowPool(poolTarget);\n\n const rowWidth = Math.max(this._lastBodyWidth, totalColumnWidth);\n const fieldCount = this._store.model.getFields()\n .filter(f => !this._hiddenColumns.has(f.getName()))\n .length;\n const fallback = fieldCount > 0 ? rowWidth / fieldCount : rowWidth;\n\n this.bindAndPositionRows(win.firstRow, win.windowSize, rowWidth, fallback, records);\n this.hideExcessPoolRows(win.windowSize);\n\n if (totalRows !== this._lastAriaRowCount) {\n this.getAria().setRowCount(totalRows);\n this._lastAriaRowCount = totalRows;\n }\n\n scroller.layoutScrollbars(totalContentWidth, totalHeight);\n\n const newScrollX = scroller.getScrollX();\n const newScrollY = scroller.getScrollY();\n if (newScrollX !== prevScrollX) {\n this.emit(\"horizontalscroll\", newScrollX);\n }\n if (newScrollY !== prevScrollY) {\n this.emit(\"verticalscroll\", newScrollY);\n }\n\n this._updateFocusStyle();\n }\n\n /**\n * Caches incoming bodyWidth / columnWidths from a layout-driven call and\n * invalidates the per-row geometry cache when either changes.\n *\n * @param bodyWidth - The new body width in pixels, or undefined to leave the cache untouched.\n * @param columnWidths - The new per-column widths in pixels.\n */\n private updateColumnWidthCache(bodyWidth?: number, columnWidths?: number[]): void {\n if (bodyWidth === undefined) {\n return;\n }\n\n const widthsChanged = this._lastBodyWidth !== bodyWidth\n || !columnWidthsEqual(this._lastColumnWidths, columnWidths);\n\n this._lastBodyWidth = bodyWidth;\n this._lastColumnWidths = columnWidths ?? [];\n\n if (widthsChanged) {\n this.invalidateGeom();\n }\n }\n\n /**\n * Binds visible pool slots to their data records and positions each row +\n * its cells. Skips data rebind when the slot's bound index hasn't changed;\n * skips geometry writes when the row geometry hasn't changed; skips cell\n * layout when the cell geometry hasn't changed.\n *\n * @param firstRow - The first data index covered by the visible window.\n * @param windowSize - The number of rows in the window.\n * @param rowWidth - The horizontal extent of each row in pixels.\n * @param fallback - Fallback column width for fields without an explicit width.\n * @param records - The current store records (passed in so this helper doesn't re-query).\n *\n * @remarks `protected` so subclasses (e.g. `TreeBody`) can wrap the\n * standard bind + position pass with their own post-bind work\n * (depth / toggle updates). Not for consumer use.\n */\n protected bindAndPositionRows(firstRow: number, windowSize: number, rowWidth: number, fallback: number, records: ModelRecord[]): void {\n const rowHeight = this._rowHeight;\n\n for (let i = 0; i < windowSize; i++) {\n const row = this._rowPool[i];\n const dataIndex = firstRow + i;\n const wasRebound = this._boundIndices[i] !== dataIndex;\n\n if (wasRebound) {\n row.setData(records[dataIndex]);\n\n this._boundIndices[i] = dataIndex;\n row.setStripe(dataIndex % 2 === 1); // odd logical rows carry the zebra stripe; set before the paint below\n this.updateRowVisualState(i);\n this.computeRowAria(row, dataIndex);\n this.applyReadOnlyState(row, records[dataIndex]);\n }\n\n this.afterRowBound(row, dataIndex, wasRebound);\n\n this.applyRequiredEmptyState(row, records[dataIndex]);\n\n this.positionRow(i, dataIndex * rowHeight, rowWidth);\n\n const cells = row.getComponents();\n const cellRow = this._cellGeom[i];\n let x = 0;\n\n for (let ci = 0; ci < cells.length; ci++) {\n const cell = cells[ci];\n const colW = this._lastColumnWidths[ci] ?? fallback;\n const prevCell = cellRow[ci];\n const cellChanged = !prevCell || prevCell.x !== x || prevCell.w !== colW || prevCell.h !== rowHeight;\n\n if (cellChanged) {\n cell.setAutoCommitStyle(false);\n cell.setX(x);\n cell.setY(0);\n cell.setWidth(colW);\n cell.setHeight(rowHeight);\n cell.setAutoCommitStyle(true);\n cellRow[ci] = { x: x, w: colW, h: rowHeight };\n if (!prevCell) {\n cell.getAria().setColIndex(ci + 1);\n }\n // Geometry change requires a full layout pass so the\n // renderer/editor (Card-layout siblings) re-fit. Pure data\n // rebinds don't need this because renderers with\n // setAutoMeasure(false) don't resize on text changes.\n cell.doLayout();\n }\n\n x += colW;\n }\n }\n }\n\n /**\n * Default subtree-click handler — walks up from the event target to\n * find the pool row that owns the click, then dispatches to\n * {@link onRowClick}. Subclasses (e.g. `TreeBody`) override this to\n * intercept clicks on subtree-owned widgets such as the\n * expand/collapse toggle.\n *\n * @param e - The bubbled click event.\n *\n * @remarks Subclassing seam — not for consumer use.\n */\n protected onSubtreeClick(e: MouseEvent): void {\n // Filter synthetic \"click\" events. `Checkbox.setSelected` dispatches\n // a `CustomEvent(\"click\")` on its root for backward-compat with\n // `on(\"action\", fn)` consumers; during a scroll rebind, the Active\n // column's cell receives a programmatic `setValue` for every pool\n // slot, so a flurry of synthetic clicks bubbles up here and would\n // each fire `onRowClick` — selecting whichever record happens to be\n // bound to that slot at the moment, effectively dragging the\n // selection downward with the scroll.\n if (!(e instanceof MouseEvent)) {\n return;\n }\n\n let node: Handle | null = e.target === null ? null : DOM.source.intern(e.target);\n\n while (node) {\n const row = this._rowPool.find(r => r.getElement() === node);\n\n if (row) {\n this.onRowClick(row, e);\n return;\n }\n\n node = DOM.source.getParentElement(node);\n }\n }\n\n /**\n * Handles a row click, updating selection with support for ctrl/cmd and shift modifiers.\n *\n * @param row - The pool row that was clicked.\n * @param e - The mouse event.\n */\n private onRowClick(row: Row, e: MouseEvent): void {\n const record = row.getData() ?? null;\n if (!record) return;\n\n const records = this.getVisibleRecords();\n\n this._anchorRecord = reduceModifierSelection(\n this._selectedRecords,\n this._anchorRecord,\n record,\n r => records.indexOf(r),\n i => records[i],\n { ctrl: e.ctrlKey || e.metaKey, shift: e.shiftKey },\n );\n\n this._boundIndices.forEach((dataIdx, i) => {\n if (dataIdx !== -1) this.updateRowVisualState(i);\n });\n\n this.notifySelectionChange();\n\n // Determine which column was clicked and update focused cell\n const targetHandle = e.target === null ? null : DOM.source.intern(e.target);\n const cells = row.getComponents();\n const columnIndex = resolveClickedColumn(cells, targetHandle);\n\n if (columnIndex >= 0) {\n this._focusedColIndex = columnIndex;\n }\n\n // Don't steal focus from an active cell editor (e.g. <input type=\"date\">).\n const targetTag = targetHandle === null ? \"\" : DOM.source.getTagName(targetHandle);\n if (targetTag !== 'INPUT' && targetTag !== 'TEXTAREA' && targetTag !== 'SELECT') {\n this.focus();\n }\n\n this._updateFocusStyle();\n this._updateActiveDescendant();\n\n // Fire the column-aware cell-click event last, after selection and\n // focus have settled, so it is purely additive. Skip clicks that land\n // inside the row but outside any cell (should not happen for a <td>\n // grid, but keeps the emit total).\n if (columnIndex >= 0) {\n const field = row.getFieldNames()[columnIndex];\n\n this.emit(\"cellclick\", {\n record,\n field,\n columnIndex,\n value: record.get(field),\n rowIndex: this.getVisibleRecords().indexOf(record),\n event: e,\n });\n }\n }\n\n /**\n * Sets the selected record set to contain exactly the given record (or clears selection).\n *\n * @param record - The record to select, or null to clear the selection.\n */\n selectRecord(record: ModelRecord | null): void {\n this._selectedRecords.clear();\n this._anchorRecord = record;\n\n if (record) {\n this._selectedRecords.add(record);\n }\n\n this._boundIndices.forEach((dataIdx, i) => {\n if (dataIdx !== -1) this.updateRowVisualState(i);\n });\n\n this.notifySelectionChange();\n }\n\n /**\n * Returns the most recently anchored selected record, or null if the selection is empty.\n *\n * @returns The anchor {@link ModelRecord}, or null.\n */\n getSelectedRecord(): ModelRecord | null {\n return this._anchorRecord && this._selectedRecords.has(this._anchorRecord)\n ? this._anchorRecord\n : (this._selectedRecords.size > 0 ? [...this._selectedRecords][0] : null);\n }\n\n /**\n * Returns all currently selected records.\n *\n * @returns An array of selected {@link ModelRecord} instances.\n */\n getSelectedRecords(): ModelRecord[] {\n return [...this._selectedRecords];\n }\n\n /**\n * Replaces the selected-record set with exactly the given records.\n * Mirrors {@link selectRecord} but accepts a multi-record list.\n *\n * @param records - The records that should appear selected. The\n * first record (if any) becomes the new anchor.\n */\n setSelectedRecords(records: ModelRecord[]): void {\n this._selectedRecords.clear();\n this._anchorRecord = records.length > 0 ? records[0] : null;\n\n for (const record of records) {\n this._selectedRecords.add(record);\n }\n\n this._boundIndices.forEach((dataIdx, i) => {\n if (dataIdx !== -1) this.updateRowVisualState(i);\n });\n\n this.notifySelectionChange();\n }\n\n /**\n * Registers a listener for one of this body's events.\n * `\"verticalscroll\"` fires after the body scrolls vertically with the\n * new `scrollY`; `\"horizontalscroll\"` fires after a horizontal scroll\n * with the new `scrollX`; `\"selection\"` fires with the current\n * selected-record array; `\"cellclick\"` fires when a data cell is clicked,\n * carrying the clicked record, the column's field name and visible index,\n * the cell value, the record's row index in the visible-records view, and\n * the raw mouse event.\n *\n * @param event - The event name.\n * @param listener - Receives the new pixel offset along the scroll axis\n * (scroll events), the selected records (`\"selection\"`), or the\n * cell-click payload (`\"cellclick\"`).\n *\n * @returns This body, for method chaining.\n *\n * @remarks `\"verticalscroll\"` is used by\n * [`PinnedTable`](/api/component/table/classes/PinnedTable) to mirror\n * `scrollY` from the scroll-side body into the pinned-side body;\n * `\"horizontalscroll\"` is used by `Table` to mirror `scrollX` into the\n * header's transform so column headers stay aligned with the body cells\n * they label. The listeners fire from the `VirtualScroller`'s\n * onScroll hook (see `init`) — the body uses transform-based virtual\n * scroll, so the native DOM `scroll` event never fires.\n */\n on(event: \"verticalscroll\", listener: (scrollTop: number) => void): this;\n on(event: \"horizontalscroll\", listener: (scrollLeft: number) => void): this;\n on(event: \"selection\", listener: (records: ModelRecord[]) => void): this;\n on(event: \"cellclick\", listener: (e: CellClickEvent) => void): this;\n on(event: BodyEvent, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered scroll listener. The exact callback\n * reference must match the one passed to {@link on}.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This body, for method chaining.\n */\n off(event: BodyEvent, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every listener registered for `event` with the new scroll\n * offset, in registration order.\n *\n * @param event - The event to emit.\n * @param payload - The scroll offset (scroll events) or the selected\n * records (`\"selection\"`).\n */\n protected emit(event: \"verticalscroll\" | \"horizontalscroll\", offset: number): void;\n protected emit(event: \"selection\", records: ModelRecord[]): void;\n protected emit(event: \"cellclick\", detail: CellClickEvent): void;\n protected emit(event: BodyEvent, payload: number | ModelRecord[] | CellClickEvent): void {\n this._listeners.fire(event, payload);\n }\n\n /** Fire `\"selection\"` with the current selection. */\n private notifySelectionChange(): void {\n this.emit(\"selection\", this.getSelectedRecords());\n }\n\n /**\n * Scrolls the body so the given record is visible at the top.\n *\n * @param record - The record to scroll into view.\n */\n scrollToRecord(record: ModelRecord): void {\n const idx = this.getVisibleRecords().indexOf(record);\n if (idx === -1) {\n return;\n }\n\n this.setScrollY(idx * this._rowHeight);\n }\n\n /**\n * Computes the read-only union per cell and forwards it to\n * {@link Cell.setReadOnly}. Runs inside the rebind block once per\n * row.\n *\n * The union is OR-composed from three sources:\n *\n * 1. Column-level static flag from {@link ColumnConfig.readOnly}.\n * 2. Spec-level row predicate from {@link ColumnSpec.rowReadOnly}\n * (cached in `_rowReadOnly`).\n * 3. Per-column per-record predicate from\n * {@link ColumnConfig.cellReadOnly}.\n *\n * Source 1 is read from the column config rather than the cell's\n * current `_readOnly` flag — a previous bind may have marked the\n * cell read-only via a dynamic predicate, and re-reading the cell\n * state would make a positive predicate result sticky once a row\n * went read-only.\n *\n * @param row - The pool row being rebound.\n * @param record - The record now bound to that row.\n */\n private applyReadOnlyState(row: Row, record: ModelRecord): void {\n const rowOverride = this._rowReadOnly?.(record) === true;\n const cells = row.getComponents() as Cell<any>[];\n const fieldNames = row.getFieldNames();\n\n for (let i = 0; i < cells.length; i++) {\n const cell = cells[i];\n const fieldName = fieldNames[i];\n const config = this._columnConfigs.get(fieldName);\n const colStatic = config?.readOnly === true;\n const cellPredOk = config?.cellReadOnly?.(record) === true;\n const union = colStatic || rowOverride || cellPredOk;\n\n cell.setReadOnly(union);\n }\n }\n\n /**\n * Returns whether `value` counts as \"empty\" for the required-cell\n * tint: `null`, `undefined`, or `''`. `0` and `false` are legit\n * values and are NOT empty; an unset boolean (`null`/`undefined`,\n * rendered indeterminate) IS empty.\n *\n * @param value - The raw record value to test.\n * @returns `true` when the value is empty.\n */\n private static isEmptyValue(value: unknown): boolean {\n return value === null || value === undefined || value === '';\n }\n\n /**\n * Computes the required union per cell and forwards it, AND-ed with\n * emptiness, to {@link Cell.setRequiredEmpty}. Unlike\n * {@link applyReadOnlyState}, this runs on every render (not gated\n * on `wasRebound`) because the tint depends on the cell's current\n * value, which changes on in-place edits — a commit cascades\n * through `store.notifyRecordChanged` back into a `renderWindow`\n * pass, and this must re-run then to clear a filled cell's tint.\n * `setRequiredEmpty` is idempotent, so an unchanged cell costs one\n * comparison.\n *\n * The union is OR-composed from two sources:\n *\n * 1. Column-level static flag from {@link ColumnConfig.required}.\n * 2. Per-column per-record predicate from\n * {@link ColumnConfig.requiredPredicate}.\n *\n * @param row - The pool row being rendered.\n * @param record - The record currently bound to that row.\n */\n private applyRequiredEmptyState(row: Row, record: ModelRecord): void {\n const cells = row.getComponents() as Cell<any>[];\n const fieldNames = row.getFieldNames();\n\n for (let i = 0; i < cells.length; i++) {\n const fieldName = fieldNames[i];\n const config = this._columnConfigs.get(fieldName);\n const required = config?.required === true\n || config?.requiredPredicate?.(record) === true;\n const empty = Body.isEmptyValue(record.get(fieldName));\n\n cells[i].setRequiredEmpty(required && empty);\n }\n }\n\n /**\n * Applies selection highlight or normal visual state to the pool row at index i.\n *\n * @param i - The zero-based index into the row pool.\n */\n private updateRowVisualState(i: number): void {\n const dataIdx = this._boundIndices[i];\n if (dataIdx === -1) {\n return;\n }\n\n const record = this.getVisibleRecords()[dataIdx];\n if (!record) {\n return;\n }\n\n const row = this._rowPool[i];\n const rowEl = row.getElement()!;\n const isSelected = this._selectedRecords.has(record);\n\n // Per-record ephemeral selection highlight on a pooled row re-bound to a\n // different record on every render. Routing this through cached Component\n // setters would persist it into _options and replay it onto the next record\n // bound to this reused row, so write/remove the inline styles directly.\n if (isSelected) {\n DOM.sink.apply(rowEl, { style: {\n 'background-color': 'var(--ts-ui-table-row-selected, rgba(30, 100, 200, 0.15))',\n 'box-shadow': 'var(--ts-ui-table-row-selected-border, none)',\n } });\n } else {\n DOM.sink.apply(rowEl, { style: { 'box-shadow': null } });\n row.updateVisualState();\n }\n\n row.getAria().setSelected(isSelected);\n }\n\n /**\n * Internal wiring called by [`Table`](/api/component/table/classes/Table) —\n * not for consumer use. Hands the Body a reference to its sibling TableHeader so\n * `_updateFocusStyle` can mirror the focused column index onto the header\n * cells. Consumers instantiating `Body` standalone may leave this unset; the\n * header-side indicator is then simply skipped.\n *\n * @param header - The TableHeader sibling owned by the same Table.\n *\n * @returns This component, for method chaining.\n */\n setHeader(header: TableHeader): this {\n this._header = header;\n\n return this;\n }\n\n /**\n * Applies a focus ring to the cell at `_focusedColIndex` in the anchor row, clearing it from all other cells.\n *\n * @remarks Called after every navigation and after `renderWindow` re-binds pool slots.\n * Also mirrors the focused column index onto the linked header cells (when\n * one has been wired in via `setHeader`) so the header shows the matching\n * column indicator. `protected` so subclasses (e.g. `TreeBody`) can\n * refresh the focus indicator after a programmatic navigation. Not\n * for consumer use.\n */\n protected _updateFocusStyle(): void {\n // Per-cell ephemeral focus outline on pooled cells re-bound to different\n // records on every render. Routing this through cached Component setters\n // would persist it into _options and replay it onto the next record bound\n // to the reused cell, so write/remove the inline styles directly.\n for (const row of this._rowPool) {\n for (const cell of row.getComponents()) {\n const el = cell.getElement();\n\n if (el) {\n DOM.sink.apply(el, { style: { \"outline\": null, \"outline-offset\": null } });\n }\n }\n }\n\n const headerCells: HeaderCell[] | null = this._header !== null\n ? this._header.getColumns() as HeaderCell[]\n : null;\n\n if (headerCells !== null) {\n for (const cell of headerCells) {\n cell.setColumnFocused(false);\n }\n }\n\n if (!this._anchorRecord) {\n return;\n }\n\n if (headerCells !== null) {\n for (let i = 0; i < headerCells.length; i++) {\n headerCells[i].setColumnFocused(i === this._focusedColIndex);\n }\n }\n\n const anchorIdx = this.getVisibleRecords().indexOf(this._anchorRecord);\n const poolSlotIdx = this._boundIndices.indexOf(anchorIdx);\n\n if (poolSlotIdx < 0) {\n return;\n }\n\n const cells = this._rowPool[poolSlotIdx].getComponents();\n const cell = cells[this._focusedColIndex];\n\n if (cell) {\n const el = cell.getElement();\n\n if (el) {\n // Pooled-cell ephemeral focus style; see note at method top.\n DOM.sink.apply(el, { style: {\n \"outline\": \"var(--ts-ui-indicator-selection, 1px dashed rgb(120, 170, 240))\",\n \"outline-offset\": \"-1px\",\n } });\n }\n }\n }\n\n /**\n * Sets `aria-activedescendant` on the body container to point at the focused cell (or row).\n *\n * @remarks Must be called after `renderWindow()` so the pool slot\n * for the anchor record is guaranteed in the DOM. `protected` so\n * subclasses (e.g. `TreeBody`) can refresh the active-descendant\n * pointer after a programmatic navigation. Not for consumer use.\n */\n protected _updateActiveDescendant(): void {\n if (!this._anchorRecord) {\n this.getAria().setActiveDescendant(\"\");\n\n return;\n }\n\n const anchorIdx = this.getVisibleRecords().indexOf(this._anchorRecord);\n const poolSlotIdx = this._boundIndices.indexOf(anchorIdx);\n\n if (poolSlotIdx < 0) {\n this.getAria().setActiveDescendant(\"\");\n\n return;\n }\n\n const cells = this._rowPool[poolSlotIdx].getComponents();\n const cell = cells[this._focusedColIndex];\n\n if (cell) {\n this.getAria().setActiveDescendant(cell.getId());\n } else {\n this.getAria().setActiveDescendant(this._rowPool[poolSlotIdx].getId());\n }\n }\n\n /**\n * Handles keyboard navigation: ArrowUp/Down/Home/End move row selection; ArrowLeft/Right\n * move column focus; PageUp/Down move by a viewport-height page; Enter starts cell edit.\n *\n * @param e - The keyboard event fired on the body element.\n *\n * @remarks `protected` so subclasses (e.g. `TreeBody`) can intercept\n * additional keys (ArrowRight/Left for expand/collapse) and delegate\n * the rest to `super.onKeyDown`. Not for consumer use.\n */\n protected onKeyDown(e: KeyboardEvent): void {\n const records = this.getVisibleRecords();\n\n if (records.length === 0) {\n return;\n }\n\n const navigable = new Set([\n 'ArrowDown', 'ArrowUp', 'Home', 'End',\n 'ArrowLeft', 'ArrowRight', 'PageUp', 'PageDown', 'Enter', ' '\n ]);\n\n if (!navigable.has(e.key)) {\n return;\n }\n\n e.preventDefault();\n\n // Column navigation — no row change needed\n if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {\n const visibleColCount = this._store.model.getFields()\n .filter(f => !this._hiddenColumns.has(f.getName())).length;\n\n if (e.key === 'ArrowLeft') {\n this._focusedColIndex = Math.max(0, this._focusedColIndex - 1);\n } else {\n this._focusedColIndex = Math.min(visibleColCount - 1, this._focusedColIndex + 1);\n }\n\n this._updateActiveDescendant();\n this._updateFocusStyle();\n\n return;\n }\n\n // Enter/Space — start editing the focused cell\n if (e.key === 'Enter' || e.key === ' ') {\n if (!this._anchorRecord) {\n return;\n }\n\n const anchorIdx = records.indexOf(this._anchorRecord);\n const poolSlotIdx = this._boundIndices.indexOf(anchorIdx);\n\n if (poolSlotIdx < 0) {\n return;\n }\n\n const cells = this._rowPool[poolSlotIdx].getComponents();\n const cell = cells[this._focusedColIndex];\n\n if (cell instanceof Cell) {\n const typedCell = cell as Cell<unknown>;\n\n typedCell.on(\"editend\", () => {\n this.focus();\n this._updateFocusStyle();\n this._updateActiveDescendant();\n });\n\n typedCell.startEdit();\n }\n\n return;\n }\n\n // Row navigation\n const currentIdx = this._anchorRecord ? records.indexOf(this._anchorRecord) : -1;\n const pageSize = this.computePageSize();\n let newIdx: number;\n\n if (e.key === 'ArrowDown') {\n newIdx = currentIdx < 0 ? 0 : Math.min(currentIdx + 1, records.length - 1);\n } else if (e.key === 'ArrowUp') {\n newIdx = currentIdx < 0 ? 0 : Math.max(currentIdx - 1, 0);\n } else if (e.key === 'PageDown') {\n newIdx = currentIdx < 0 ? 0 : Math.min(currentIdx + pageSize, records.length - 1);\n } else if (e.key === 'PageUp') {\n newIdx = currentIdx < 0 ? 0 : Math.max(currentIdx - pageSize, 0);\n } else if (e.key === 'Home') {\n newIdx = 0;\n } else {\n newIdx = records.length - 1;\n }\n\n const newAnchor = records[newIdx];\n\n this.selectRecord(newAnchor);\n this.scrollRecordIntoView(newAnchor);\n this.renderWindow();\n this._updateActiveDescendant();\n }\n\n /**\n * Scrolls the body so the given record is visible, without moving the viewport unless necessary.\n *\n * @param record - The record to scroll into view.\n *\n * @remarks `protected` so subclasses (e.g. `TreeBody`) can keep\n * keyboard-driven navigation inside the scroll viewport. Not for\n * consumer use.\n */\n protected scrollRecordIntoView(record: ModelRecord): void {\n this.scrollRowIntoView(this.getVisibleRecords().indexOf(record));\n }\n\n /**\n * Scrolls the body horizontally so the column at `colIndex` is fully\n * visible, without moving the viewport unless necessary. The horizontal\n * mirror of {@link scrollRecordIntoView}.\n *\n * @param colIndex - The visible-column index to reveal.\n *\n * @remarks Driving the shared `VirtualScroller` keeps the header\n * translate and the scrollbar thumb in sync with the move. This is why an\n * inline edit routes through here rather than relying on the browser's\n * native focus-scroll: that scroll shifts only the clipped content layer\n * and leaves the header + scrollbar behind. `protected` so subclasses can\n * reuse it. Not for consumer use.\n */\n protected scrollColumnIntoView(colIndex: number): void {\n const widths = this._lastColumnWidths;\n\n if (!this._scroller || colIndex < 0 || colIndex >= widths.length) {\n return;\n }\n\n let left = 0;\n for (let i = 0; i < colIndex; i++) {\n left += widths[i];\n }\n const right = left + widths[colIndex];\n const scrollLeft = this._scroller.getScrollX();\n const viewportWidth = this.getWidth() || 0;\n const visibleRight = scrollLeft + viewportWidth;\n\n let target = scrollLeft;\n if (left < scrollLeft) {\n target = left;\n } else if (right > visibleRight) {\n target = right - viewportWidth;\n }\n if (target !== scrollLeft) {\n this.setScrollX(target);\n }\n }\n}\n\nconst BodyCallable = callable(Body);\ntype BodyCallable = Body;\nexport {\n Body as _Body,\n BodyCallable as Body\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { Row } from \"~/component/table/Row.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * The footer section of a table, rendered as a `<tfoot>` element.\n *\n * Contains a single {@link Row} and delegates width/height changes to it.\n *\n * Re-exported as `TableFooter` from the package barrel.\n *\n * @category Components\n */\nclass FooterRow extends Component {\n\n constructor() {\n super({ tag: \"tfoot\" });\n\n this.setBorder({ borderTop: \"1px solid var(--ts-ui-border-color, black)\" });\n // Apply the surface as both a colour and an image so a flat-colour\n // theme (e.g. ModernTheme, where --ts-ui-button-bg is a solid colour)\n // paints via the colour and a gradient theme via the image. Setting\n // only background-image left the footer transparent under a flat-colour\n // theme, since a colour is invalid as a background-image.\n const footerBg = \"var(--ts-ui-button-bg, linear-gradient(rgb(241, 241, 241), rgb(200, 200, 200)))\";\n this.setBackgroundColor(footerBg);\n this.setBackgroundImage(footerBg);\n\n let row = new Row();\n this.addRow(row);\n }\n\n /**\n * Returns the footer cell components in column order.\n *\n * @returns An array of cell components from the footer's inner row.\n */\n getColumns() {\n return this.getComponents()[0].getComponents();\n }\n\n /**\n * Appends a row to the footer.\n *\n * @param row - The row to append.\n */\n addRow(row: Row) : this {\n this.addComponent(row);\n\n return this;\n }\n\n /**\n * Adds a row as a child component of the footer.\n *\n * @param row - The row component to add.\n *\n * @returns This component, for method chaining.\n */\n addComponent(row: Row): this {\n super.addComponent(row);\n\n return this;\n }\n\n /**\n * Sets the footer width and propagates it to the inner row.\n *\n * @param width - The width in pixels.\n *\n * @returns This component, for method chaining.\n */\n setWidth(width: number): this {\n super.setWidth(width);\n\n this.getComponents()[0].setWidth(width);\n\n return this;\n }\n\n /**\n * Sets the footer height and propagates it to the inner row.\n *\n * @param height - The height in pixels.\n *\n * @returns This component, for method chaining.\n */\n setHeight(height: number): this {\n super.setHeight(height);\n\n this.getComponents()[0].setHeight(height);\n\n return this;\n }\n}\n\nconst FooterRowCallable = callable(FooterRow);\ntype FooterRowCallable = FooterRow;\nexport {\n FooterRow as _FooterRow,\n FooterRowCallable as FooterRow\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Field } from \"~/data/Field.js\";\nimport { ColumnConfig, ColumnSpec } from \"~/component/table/ColumnConfig.js\";\n\n/**\n * Resolved presentation descriptor for a single table column.\n *\n * Created internally by `Column.resolve` — not constructed directly by\n * application code. Wraps a {@link Field} and carries optional width constraints\n * and an initial visibility flag derived from a {@link ColumnSpec}.\n *\n * Re-exported as `TableColumn` from the package barrel.\n *\n * @category Components\n */\nexport class Column {\n\n private _field : Field;\n private _minWidth : number | undefined;\n private _maxWidth : number | undefined;\n private _hidden : boolean;\n private _unhideable : boolean;\n private _readOnly : boolean;\n private _headerGlyph : string | null;\n private _group : string | null;\n private _groupColor : string | null;\n private _required : boolean;\n\n /**\n * Constructs a Column from a field and an optional presentation config.\n *\n * @param field - The model field this column represents.\n * @param config - Optional config; all constraint properties default to absent / false.\n */\n constructor(field: Field, config?: ColumnConfig) {\n this._field = field;\n this._minWidth = config?.minWidth;\n this._maxWidth = config?.maxWidth;\n this._hidden = config?.hidden ?? false;\n this._unhideable = config?.unhideable ?? false;\n this._readOnly = config?.readOnly ?? false;\n this._headerGlyph = config?.headerGlyph ?? null;\n this._group = config?.group ?? null;\n this._groupColor = config?.groupColor ?? null;\n this._required = config?.required ?? false;\n }\n\n /**\n * Returns the model field this column represents.\n *\n * @returns The underlying {@link Field}.\n */\n getField(): Field {\n return this._field;\n }\n\n /**\n * Returns the minimum column width in pixels declared in the spec.\n *\n * @returns The minimum width, or `undefined` if unconstrained.\n */\n getMinWidth(): number | undefined {\n return this._minWidth;\n }\n\n /**\n * Returns the maximum column width in pixels declared in the spec.\n *\n * @returns The maximum width, or `undefined` if unconstrained.\n */\n getMaxWidth(): number | undefined {\n return this._maxWidth;\n }\n\n /**\n * Returns whether this column starts hidden according to the spec.\n *\n * @returns `true` if the spec declared `hidden: true` for this column.\n */\n isInitiallyHidden(): boolean {\n return this._hidden;\n }\n\n /**\n * Returns whether this column is marked unhideable in the spec.\n * Unhideable columns always render and cannot be toggled off via\n * the column context menu or {@link Table.setColumnVisible}.\n *\n * @returns `true` when the spec declared `unhideable: true`.\n */\n isUnhideable(): boolean {\n return this._unhideable;\n }\n\n /**\n * Returns whether this column is marked read-only in the spec.\n * Every cell in a read-only column refuses inline editing and\n * renders with a grey tint.\n *\n * @returns `true` when the spec declared `readOnly: true`.\n */\n isReadOnly(): boolean {\n return this._readOnly;\n }\n\n /**\n * Returns the registry glyph name shown to the left of this column's header text.\n *\n * @returns The glyph registry name, or `null` if no header glyph is set.\n */\n getHeaderGlyph(): string | null {\n return this._headerGlyph;\n }\n\n /**\n * Sets the registry glyph name shown to the left of this column's header text.\n *\n * Pass `null` (or call {@link Column.clearHeaderGlyph}) to remove the glyph.\n *\n * @param name - A registered glyph name, or `null` to remove.\n * @returns This column, for method chaining.\n */\n setHeaderGlyph(name: string | null): this {\n this._headerGlyph = name;\n\n return this;\n }\n\n /**\n * Clears the header glyph for this column. Equivalent to `setHeaderGlyph(null)`.\n *\n * @returns This column, for method chaining.\n */\n clearHeaderGlyph(): this {\n return this.setHeaderGlyph(null);\n }\n\n /**\n * Returns the parent-header group name this column belongs to, fixed at\n * construction. Columns sharing a group name and appearing contiguously\n * render under a single spanning {@link ParentHeaderCell}.\n *\n * @returns The group name, or `null` when the column is ungrouped.\n */\n getGroup(): string | null {\n return this._group;\n }\n\n /**\n * Returns the optional background color advertised by this column's\n * `ColumnConfig.groupColor`. The first non-null value in a contiguous\n * group run wins; later mismatched values are ignored.\n *\n * @returns The CSS color string, or `null` when none was declared.\n */\n getGroupColor(): string | null {\n return this._groupColor;\n }\n\n /**\n * Returns whether this column is marked required in the spec. Drives\n * the header asterisk; the per-record `requiredPredicate` composes\n * with this flag for the empty-cell tint but does not affect this\n * accessor.\n *\n * @returns `true` when the spec declared `required: true`.\n */\n isRequired(): boolean {\n return this._required;\n }\n\n /**\n * Resolves a {@link ColumnSpec} against a set of model fields into an ordered\n * array of {@link Column} instances.\n *\n * When no spec is provided every field becomes a default Column, preserving\n * existing table behaviour exactly.\n *\n * When a spec is provided:\n * - Fields listed in `spec.columns` receive their declared constraints.\n * - Fields not listed are appended as default Columns when `appendUnlisted`\n * is `true` (the default), or omitted entirely when it is `false`.\n *\n * The returned array is sorted by {@link Field.getOrder} so that the column\n * sequence matches the order used by the header and body renderers.\n *\n * @param fields - All fields returned by the model.\n * @param spec - Optional column spec; omit to auto-generate from all fields.\n *\n * @returns Resolved Column instances in display order.\n */\n static resolve(fields: Field[], spec?: ColumnSpec): Column[] {\n const sorted = fields.slice().sort((a, b) => a.getOrder() - b.getOrder());\n\n if (!spec) {\n return sorted.map(f => new Column(f));\n }\n\n const configMap = new Map(spec.columns.map(c => [c.field, c]));\n const listedNames = new Set(spec.columns.map(c => c.field));\n\n if (spec.appendUnlisted === false) {\n return sorted\n .filter(f => listedNames.has(f.getName()))\n .map(f => new Column(f, configMap.get(f.getName())));\n }\n\n // All fields in field order; apply config where one exists.\n // This keeps Column order in sync with the header, which also sorts by field.getOrder().\n return sorted.map(f => new Column(f, configMap.get(f.getName())));\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { DOM } from \"~/core/DOM.js\";\nimport { Column } from \"~/component/table/Column.js\";\nimport { ColumnConfig } from \"~/component/table/ColumnConfig.js\";\nimport { ModelRecord } from \"~/data/ModelRecord.js\";\nimport { FieldType } from \"~/data/Field.js\";\n\n/**\n * Options controlling a {@link Table} export operation.\n *\n * @category Components\n */\nexport interface ExportOptions {\n /** When true, hidden columns are included in the export. Default: false. */\n includeHidden?: boolean;\n\n /** Override the downloaded filename. */\n filename?: string;\n}\n\n/**\n * Stateless helper that converts a column list and a record list into a CSV\n * or JSON download.\n *\n * @remarks\n * Used internally by {@link Table.exportCSV} and {@link Table.exportJSON}.\n * Date, time, and datetime values are formatted with the same `toLocale*`\n * options the cell renderers use, so exports match what the user sees.\n */\nexport class TableExporter {\n\n /**\n * Converts columns + records to an RFC 4180 CSV string and triggers a download.\n *\n * @param columns - The columns to include as fields in the CSV.\n * @param records - The records to serialize as rows.\n * @param columnConfigs - Per-field column config map (carries `showSeconds` for time/datetime).\n * @param options - Optional export options.\n */\n static exportCSV(\n columns : Column[],\n records : ModelRecord[],\n columnConfigs: Map<string, ColumnConfig>,\n options ?: ExportOptions\n ): void {\n const header = columns\n .map(c => TableExporter.escapeCSVField(c.getField().getName()))\n .join(',');\n\n const rows = records.map(record =>\n columns\n .map(c => TableExporter.escapeCSVField(TableExporter.formatValue(c, record.get(c.getField().getName()), columnConfigs)))\n .join(',')\n );\n\n const csv = [header, ...rows].join('\\r\\n');\n\n TableExporter.download(csv, options?.filename ?? 'table-export.csv', 'text/csv;charset=utf-8;');\n }\n\n /**\n * Converts columns + records to a pretty-printed JSON array of objects and triggers a download.\n *\n * @param columns - The columns whose field names become the keys of each emitted object.\n * @param records - The records to serialize.\n * @param columnConfigs - Per-field column config map (carries `showSeconds` for time/datetime).\n * @param options - Optional export options.\n */\n static exportJSON(\n columns : Column[],\n records : ModelRecord[],\n columnConfigs: Map<string, ColumnConfig>,\n options ?: ExportOptions\n ): void {\n const data = records.map(record =>\n Object.fromEntries(\n columns.map(c => [\n c.getField().getName(),\n TableExporter.formatValue(c, record.get(c.getField().getName()), columnConfigs)\n ])\n )\n );\n const json = JSON.stringify(data, null, 2);\n\n TableExporter.download(json, options?.filename ?? 'table-export.json', 'application/json');\n }\n\n /**\n * Formats a raw cell value the same way the matching cell renderer does.\n *\n * @param column - The column whose field type drives the formatting choice.\n * @param value - The raw value read from the record.\n * @param columnConfigs - Per-field config map; consulted for `showSeconds` on time/datetime.\n * @returns The formatted value, or the original value when no formatting applies.\n */\n private static formatValue(column: Column, value: any, columnConfigs: Map<string, ColumnConfig>): any {\n if (value == null || !(value instanceof Date)) {\n return value;\n }\n\n const type : FieldType = column.getField().getType();\n const showSeconds : boolean = columnConfigs.get(column.getField().getName())?.showSeconds ?? false;\n\n switch (type) {\n case 'date':\n return value.toLocaleDateString();\n case 'time':\n return value.toLocaleTimeString(undefined, showSeconds\n ? { hour: '2-digit', minute: '2-digit', second: '2-digit' }\n : { hour: '2-digit', minute: '2-digit' });\n case 'datetime':\n return value.toLocaleString(undefined, showSeconds\n ? { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' }\n : { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' });\n default:\n return value;\n }\n }\n\n /**\n * Escapes a single CSV field value per RFC 4180.\n *\n * @param value - The raw value to escape; null and undefined become empty strings.\n * @returns The escaped CSV field, wrapped in quotes when it contains `,`, `\"`, or `\\n`.\n */\n private static escapeCSVField(value: any): string {\n const str = String(value ?? '');\n\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n return '\"' + str.replace(/\"/g, '\"\"') + '\"';\n }\n\n return str;\n }\n\n /**\n * Triggers a browser download of the given content via a temporary anchor element.\n *\n * @param content - The textual content to download.\n * @param filename - The suggested filename for the download.\n * @param mimeType - The MIME type used to construct the Blob.\n */\n private static download(content: string, filename: string, mimeType: string): void {\n const blob = new Blob([content], { type: mimeType });\n const url = URL.createObjectURL(blob);\n const a = DOM.sink.createElement('a');\n\n DOM.sink.apply(a, { setAttr: { \"href\": url, \"download\": filename } });\n\n DOM.sink.appendChild(DOM.source.getBody(), a);\n DOM.sink.click(a);\n DOM.sink.removeChild(DOM.source.getBody(), a);\n DOM.sink.release(a);\n\n URL.revokeObjectURL(url);\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\nimport { Table as TableLayout } from \"~/layout/Table.js\";\nimport { TableHeader } from \"~/component/table/Header.js\";\nimport { Body } from \"~/component/table/Body.js\";\nimport type { CellClickEvent } from \"~/component/table/Body.js\";\nimport { FooterRow } from \"~/component/table/Footer.js\";\nimport { AbstractStore } from \"~/data/AbstractStore.js\";\nimport { ModelRecord } from \"~/data/ModelRecord.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { Menu } from \"~/overlay/Menu.js\";\nimport { MenuItemConfig } from \"~/component/container/MenuItem.js\";\nimport { Column } from \"~/component/table/Column.js\";\nimport type { ColumnConfig } from \"~/component/table/ColumnConfig.js\";\nimport { ColumnSpec } from \"~/component/table/ColumnConfig.js\";\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { Util } from \"~/core/Util.js\";\nimport { TableExporter, ExportOptions } from \"~/component/table/TableExporter.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/** Events emitted by {@link Table}. */\nexport type TableEvent = \"selection\" | \"cellclick\";\n\n/**\n * Construction-time options for {@link Table}.\n *\n * Table's primary configuration is the positional `(store, spec)` constructor\n * pair; this options bag carries only inherited Component styling. Defined\n * here so `this._options` is typed at the leaf class and Phase 6 has a place\n * to grow if Table starts accepting an options object.\n *\n * @category Components\n */\nexport interface TableOptions extends ComponentOptions {\n}\n\n/**\n * A data-bound table component rendered as an HTML `<table>` element.\n *\n * Composes a {@link TableHeader}, a virtual-scrolling {@link Body}, and an optional\n * {@link FooterRow}. Exposes CRUD and sync operations that delegate to the underlying\n * {@link AbstractStore}.\n *\n * An optional {@link ColumnSpec} controls which columns appear, their display\n * constraints (`minWidth`, `maxWidth`), and initial visibility. When no spec is\n * supplied the table auto-generates one column per model field — identical to the\n * pre-spec behaviour.\n *\n * @example\n * ```typescript\n * import { Model, MemoryStore } from '@jimka/typescript-ui/data';\n * import { Table } from '@jimka/typescript-ui/component/table';\n *\n * const PersonModel = new Model([\n * { name: 'id', type: 'number' },\n * { name: 'name', type: 'string' },\n * { name: 'age', type: 'number' },\n * ]);\n *\n * const store = new MemoryStore(PersonModel, [\n * { id: 1, name: 'Alice', age: 30 },\n * { id: 2, name: 'Bob', age: 25 },\n * ]);\n * await store.load();\n *\n * const table = new Table(store);\n * panel.addComponent(table);\n * ```\n *\n * @category Components\n */\nclass Table extends Component<TableOptions> {\n\n private _store : AbstractStore;\n private _spec : ColumnSpec | undefined;\n private _resolvedColumns : Column[] = [];\n private _hiddenColumns : Set<string> = new Set();\n private _columnContextMenu: Menu = new Menu();\n private _headerVisible : boolean;\n private _header : TableHeader;\n private _body : Body;\n private _bodyVisible : boolean;\n private _footer : FooterRow;\n private _footerVisible : boolean;\n private _columnWidths : number[] = [];\n private _resizeOriginClientX: number = 0;\n private _resizeOriginW0 : number = 0;\n private _resizeOriginW1 : number = 0;\n private _savedColumnWidths: Map<string, number> = new Map();\n private _columnConfigs : Map<string, ColumnConfig> = new Map();\n private _exportMenuEnabled: boolean = false;\n private _listeners : ListenerBag<TableEvent> = new ListenerBag<TableEvent>();\n\n /**\n * Constructs a Table bound to the given store, optionally constrained by a\n * column presentation spec.\n *\n * @param store - The data store to bind to this table.\n * @param spec - Optional column spec; omit to auto-generate all columns.\n * @param bodyFactory - Optional. Closure that constructs the body\n * component. Used by [`TreeTable`](/api/component/table/classes/TreeTable)\n * to install a [`TreeBody`](/api/component/table/classes/TreeBody)\n * in place of the default flat-list body. A closure (not a\n * subclass-overridable method) avoids the class-field super-trap\n * that would otherwise read tree-spec fields before they are\n * initialised.\n */\n constructor(store: AbstractStore, spec?: ColumnSpec, bodyFactory?: (store: AbstractStore) => Body) {\n super({ tag: \"table\" });\n\n this.setLayoutManager(new TableLayout());\n this.getAria().setRole(\"grid\");\n this.setBorder({ border: \"1px solid var(--ts-ui-border-color, black)\" });\n this.setInsets(new Insets(0, 0, 0, 0));\n this.setOverflow(\"hidden\");\n this.setMinSize(100, 100);\n\n this._store = store;\n this._spec = spec;\n this._headerVisible = true;\n this._bodyVisible = true;\n this._footerVisible = false;\n\n this._resolvedColumns = Column.resolve(store.model.getFields(), spec);\n this.initHiddenFromSpec();\n\n this._header = new TableHeader(store.model, store);\n this._header.on(\"columnresizestart\", (i, clientX) => this.onColumnResizeStart(i, clientX));\n this._header.on(\"columnresize\", (i, clientX) => this.onColumnResize(i, clientX));\n this._header.on(\"columncontextmenu\", (_, x, y) => this.showColumnMenu(x, y));\n // The header is the permanent target of horizontal scroll mirroring (see the\n // scroll listener below), so promote it to its own compositor layer for the\n // Table's lifetime. One hint per Table is well under the per-page threshold.\n this._header.setWillChange(\"transform\");\n this.addComponent(this._header);\n this._header.setColumns(this._resolvedColumns);\n\n this._body = bodyFactory ? bodyFactory(store) : new Body(store);\n this._body.setHeader(this._header);\n this.addComponent(this._body);\n this._body.setColumns(this._resolvedColumns);\n\n this._footer = new FooterRow();\n this.addComponent(this._footer);\n\n const effectiveHidden = this.getEffectiveHiddenSet();\n\n if (effectiveHidden.size > 0) {\n this._header.setHiddenColumns(effectiveHidden);\n this._body.setHiddenColumns(effectiveHidden);\n }\n\n if (spec) {\n this._columnConfigs = this.buildColumnConfigs(spec);\n this._body.setColumnConfigs(this._columnConfigs);\n }\n\n this._body.setRowReadOnly(spec?.rowReadOnly ?? null);\n\n this.getAria().setColCount(this.getColumns().length);\n\n // Sync header horizontal scroll with body. The body uses\n // transform-based virtual scroll (via `VirtualScroller`), so the\n // native DOM `scroll` event never fires; hook the body's\n // `on(\"horizontalscroll\")` listener instead. Translate the header's\n // two inner rows (parent row + column row) rather than the header\n // element itself — the header band stays pinned to the viewport\n // width so its background covers the vertical-scrollbar reserve\n // band on the right edge, and only the cells inside scroll with\n // the body.\n this._body.on(\"horizontalscroll\", scrollLeft => {\n this._header.getParentRow().setTranslate(-scrollLeft, 0);\n this._header.getComponents()[1].setTranslate(-scrollLeft, 0);\n });\n\n // Surface the body's selection changes on the Table's own event so\n // consumers can react (e.g. enabling a delete action) without reaching\n // into the private body.\n this._body.on(\"selection\", records => this.emit(\"selection\", records));\n\n // Forward the body's column-aware cell-click on the Table's own event,\n // mirroring the selection forward above so consumers can react to a\n // click on a specific cell (record + column) without inferring the\n // column from a selection change.\n this._body.on(\"cellclick\", e => this.emit(\"cellclick\", e));\n }\n\n /**\n * Registers a listener for one of this table's events.\n *\n * @param event - `\"selection\"` fires whenever the selected-record set\n * changes, receiving the current selection; `\"cellclick\"` fires when a\n * data cell is clicked, carrying the clicked record, the column's field\n * name and visible index, the cell value, the record's row index in the\n * visible-records view, and the raw mouse event.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This table, for method chaining.\n */\n on(event: \"selection\", listener: (records: ModelRecord[]) => void): this;\n on(event: \"cellclick\", listener: (e: CellClickEvent) => void): this;\n on(event: TableEvent, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered listener. The exact callback reference\n * must match the one passed to {@link on}.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This table, for method chaining.\n */\n off(event: TableEvent, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every listener registered for `event`, in registration order.\n *\n * @param event - The event to emit.\n * @param payload - The current selection (`\"selection\"`) or the\n * cell-click detail (`\"cellclick\"`) forwarded to each listener.\n */\n protected emit(event: \"selection\", records: ModelRecord[]): void;\n protected emit(event: \"cellclick\", detail: CellClickEvent): void;\n protected emit(event: TableEvent, payload: ModelRecord[] | CellClickEvent): void {\n this._listeners.fire(event, payload);\n }\n\n /**\n * Returns the resolved, visible columns in display order.\n *\n * Excludes columns that are currently hidden (via runtime toggle or the spec's\n * `hidden` flag) and columns excluded by a strict spec (`appendUnlisted: false`).\n *\n * @returns Visible {@link Column} instances in field display order.\n */\n getColumns(): Column[] {\n const effective = this.getEffectiveHiddenSet();\n\n return this._resolvedColumns.filter(c => !effective.has(c.getField().getName()));\n }\n\n /**\n * Returns the data store this table is bound to.\n *\n * @returns The current {@link AbstractStore}.\n */\n getStore(): AbstractStore {\n return this._store;\n }\n\n /**\n * Returns the model associated with this table's store.\n *\n * @returns The model from the store.\n */\n getModel() {\n return this._store.model;\n }\n\n /**\n * Replaces the data store, re-resolves columns from the new model, and updates\n * the body and header to reflect the change.\n *\n * @param store - The new store to bind to the table.\n */\n setStore(store: AbstractStore): this {\n this._store = store;\n this._columnWidths = [];\n this._savedColumnWidths = new Map();\n this._resolvedColumns = Column.resolve(store.model.getFields(), this._spec);\n\n this._body.setStore(store);\n this._header.setModel(store.model);\n this._header.setColumns(this._resolvedColumns);\n this._body.setColumns(this._resolvedColumns);\n this._header.setHiddenColumns(this.getEffectiveHiddenSet());\n this.getAria().setColCount(this.getColumns().length);\n\n return this;\n }\n\n /**\n * Returns the per-column width array maintained by the layout manager.\n *\n * @returns The current column widths in pixels.\n */\n getColumnWidths(): number[] {\n return this._columnWidths;\n }\n\n /**\n * Stores the per-column width array (called by the layout manager on each layout pass).\n *\n * @param widths - The new column widths in pixels.\n * @remarks Also mirrors each width into `savedColumnWidths` keyed by field name so\n * that show/hide toggles can restore per-column widths without a full re-initialisation.\n */\n setColumnWidths(widths: number[]): this {\n this._columnWidths = widths;\n\n const visibleColumns = this.getColumns();\n\n widths.forEach((w, i) => {\n const col = visibleColumns[i];\n\n if (col) {\n this._savedColumnWidths.set(col.getField().getName(), w);\n }\n });\n\n return this;\n }\n\n /**\n * Shows or hides the column identified by the given field name.\n *\n * Only columns present in the resolved column list can be toggled; columns\n * excluded by a strict spec (`appendUnlisted: false`) are unaffected.\n *\n * Calls to hide a column marked `unhideable: true` in the spec are a no-op —\n * the column always remains visible. Calls to show any column run normally.\n *\n * Manually resized widths are preserved across visibility toggles. When showing\n * a column introduces extra width that would overflow the container, existing\n * columns are proportionally trimmed via `trimToTarget` to make room before\n * the layout manager runs.\n *\n * @param fieldName - The model field name of the column to toggle.\n * @param visible - `true` to show the column, `false` to hide it.\n */\n setColumnVisible(fieldName: string, visible: boolean): this {\n if (!visible) {\n const col = this._resolvedColumns.find(c => c.getField().getName() === fieldName);\n\n if (col && col.isUnhideable()) {\n return this;\n }\n }\n\n if (visible) {\n this._hiddenColumns.delete(fieldName);\n } else {\n this._hiddenColumns.add(fieldName);\n }\n\n const newVisibleColumns = this.getColumns();\n const rawWidths = newVisibleColumns.map(col =>\n this._savedColumnWidths.get(col.getField().getName()) ?? this.defaultColumnWidth(col)\n );\n const savedTotal = this._columnWidths.reduce((s, w) => s + w, 0);\n const rawTotal = rawWidths.reduce((s, w) => s + w, 0);\n\n this._columnWidths = (rawTotal > savedTotal + 0.5 && savedTotal > 0)\n ? this.trimToTarget(newVisibleColumns, rawWidths, savedTotal, fieldName)\n : rawWidths;\n\n const effectiveHidden = this.getEffectiveHiddenSet();\n\n this._header.setHiddenColumns(effectiveHidden);\n this._body.setHiddenColumns(effectiveHidden);\n this.getAria().setColCount(this.getColumns().length);\n this.doLayout();\n\n return this;\n }\n\n /**\n * Returns the table header component.\n *\n * @returns The {@link TableHeader} section of this table.\n */\n getHeader() {\n return this._header;\n }\n\n /**\n * Returns whether the header section is visible.\n *\n * @returns `true` if the header is visible.\n */\n isHeaderVisible() {\n return this._headerVisible;\n }\n\n /**\n * Returns the table body component.\n *\n * @returns The virtual-scrolling {@link Body} section of this table.\n */\n getBody() {\n return this._body;\n }\n\n /**\n * Returns whether the body section is visible.\n *\n * @returns `true` if the body is visible.\n */\n isBodyVisible() {\n return this._bodyVisible;\n }\n\n /**\n * Returns the table footer component.\n *\n * @returns The {@link FooterRow} section of this table.\n */\n getFooter() {\n return this._footer;\n }\n\n /**\n * Returns whether the footer section is visible.\n *\n * @returns `true` if the footer is visible.\n */\n isFooterVisible() {\n return this._footerVisible;\n }\n\n /**\n * Adds a new record to the store, scrolls to it, and selects it.\n *\n * @param defaults - Optional initial field values for the new record.\n * \n * @returns The newly created {@link ModelRecord}.\n */\n addRow(defaults: Record<string, any> = {}): ModelRecord {\n const [record] = this._store.add(defaults);\n this._body.scrollToRecord(record);\n this._body.selectRecord(record);\n\n return record;\n }\n\n /**\n * Removes the currently selected record from the store.\n */\n removeSelectedRow(): this {\n const record = this._body.getSelectedRecord();\n\n if (!record) {\n return this;\n }\n\n this._body.selectRecord(null);\n this._store.remove(record);\n\n return this;\n }\n\n /**\n * Selects the given record — or clears the selection when `null` — and\n * scrolls it into view, driving the selection programmatically without the\n * caller reaching into the private body. This is the read/write counterpart\n * to {@link getSelectedRecord}: use it to sync the table to an external\n * selection (a linked tree/diagram, a master/detail view). Fires the\n * `\"selection\"` event like a user click, so a caller that drives it in\n * response to its own `\"selection\"` handler must guard against re-entrancy.\n *\n * @param record - The record to select and reveal, or `null` to clear.\n *\n * @returns This table, for method chaining.\n */\n selectRecord(record: ModelRecord | null): this {\n if (record) {\n this._body.scrollToRecord(record);\n }\n\n this._body.selectRecord(record);\n\n return this;\n }\n\n /**\n * Persists all pending store changes to the server via the configured proxy.\n *\n * @returns A Promise that resolves when the sync operation completes.\n */\n async sync(): Promise<void> {\n return this._store.sync();\n }\n\n /**\n * Discards all unsynced store changes — reverts dirty records, drops new\n * ones, and restores pending removals.\n */\n reject(): void {\n this._store.reject();\n }\n\n /**\n * Returns the currently selected record, or null if none is selected.\n *\n * @returns The selected {@link ModelRecord}, or null.\n */\n getSelectedRecord(): ModelRecord | null {\n return this._body.getSelectedRecord();\n }\n\n /**\n * Returns all currently selected records.\n *\n * @returns An array of selected {@link ModelRecord} instances.\n */\n getSelectedRecords(): ModelRecord[] {\n return this._body.getSelectedRecords();\n }\n\n /**\n * Adds a header, body, or footer section component, updating the stored reference.\n *\n * @param row - The section component to add.\n * @param constraints - Optional layout constraints for the section.\n *\n * @returns This component, for method chaining.\n */\n addComponent(row: TableHeader | Body | FooterRow, constraints?: LayoutConstraints): this {\n if (row instanceof TableHeader) {\n this._header = row;\n } else if (row instanceof Body) {\n this._body = row;\n } else if (row instanceof FooterRow) {\n this._footer = row;\n }\n\n super.addComponent(row, constraints);\n\n return this;\n }\n\n /**\n * Returns a type-based default width for a column that has no saved width yet.\n *\n * @param col - The column to compute a default width for.\n * @returns A positive pixel width appropriate for the column's field type.\n */\n private defaultColumnWidth(col: Column): number {\n const f = col.getField();\n\n // Measure under the same font properties HeaderCell actually renders with\n // (bold + table-header font size). 4px cell padding + 5px resize-handle gutter\n // + 12px breathing room for the optional sort indicator (▲/▼).\n const textWidth = Util.measureTextWidth(f.getName(), {\n fontSize : \"var(--ts-ui-table-header-font-size, var(--ts-ui-font-size))\",\n fontWeight: \"bold\",\n });\n const headerMin = textWidth + 21;\n\n switch (f.getType()) {\n case 'boolean': return Math.max(60, headerMin);\n case 'number': return Math.max(90, headerMin);\n case 'date': return Math.max(110, headerMin);\n default: return Math.max(100, headerMin);\n }\n }\n\n /**\n * Proportionally reduces existing saved columns to bring the total down to `targetTotal`,\n * leaving `exemptField` and any unsaved columns at their assigned widths.\n *\n * Flex (string/auto) columns are reduced first so that `rescaleWidths` can restore them\n * automatically when the shown column is later hidden again. Fixed-type (boolean/number/date)\n * columns are only reduced if flex space is fully exhausted.\n *\n * @param columns - The full new visible column list.\n * @param widths - Raw width per column (saved or default).\n * @param targetTotal - The pixel budget the result must not exceed.\n * @param exemptField - Field name of the column being shown; never trimmed.\n * \n * @returns Adjusted width array whose sum is at most `targetTotal`.\n */\n private trimToTarget(columns: Column[], widths: number[], targetTotal: number, exemptField: string): number[] {\n const result = [...widths];\n\n const isFixedType = (col: Column): boolean => {\n const t = col.getField().getType();\n\n return t === 'boolean' || t === 'number' || t === 'date';\n };\n\n const trim = (pool: { i: number; room: number }[], deficit: number): number => {\n const totalRoom = pool.reduce((s, c) => s + c.room, 0);\n\n if (totalRoom <= 0) {\n return deficit;\n }\n\n const toRemove = Math.min(deficit, totalRoom);\n\n for (const { i, room } of pool) {\n result[i] = Math.max(result[i] - (room / totalRoom) * toRemove, columns[i].getMinWidth() ?? 30);\n }\n\n return deficit - toRemove;\n };\n\n const pool = (fixedType: boolean) => columns\n .map((col, i) => ({\n i,\n room: (isFixedType(col) === fixedType\n && col.getField().getName() !== exemptField\n && this._savedColumnWidths.has(col.getField().getName()))\n ? result[i] - (col.getMinWidth() ?? 30)\n : 0\n }))\n .filter(c => c.room > 0.5);\n\n let deficit = result.reduce((s, w) => s + w, 0) - targetTotal;\n\n deficit = trim(pool(false), deficit);\n\n if (deficit > 0.5) {\n trim(pool(true), deficit);\n }\n\n return result;\n }\n\n /**\n * Populates `hiddenColumns` from columns declared `hidden: true` in the spec,\n * so they start hidden but remain user-toggleable via the context menu.\n */\n private buildColumnConfigs(spec: ColumnSpec): Map<string, ColumnConfig> {\n const map = new Map<string, ColumnConfig>();\n\n for (const col of spec.columns) {\n map.set(col.field, col);\n }\n\n return map;\n }\n\n private initHiddenFromSpec(): void {\n for (const col of this._resolvedColumns) {\n if (col.isInitiallyHidden() && !col.isUnhideable()) {\n this._hiddenColumns.add(col.getField().getName());\n }\n }\n }\n\n /**\n * Returns the union of runtime-hidden columns and columns excluded by the spec\n * (`appendUnlisted: false`). This is the set passed to the header and body renderers.\n *\n * @returns The effective set of field names that must not be rendered.\n */\n private getEffectiveHiddenSet(): Set<string> {\n const resolvedNames = new Set(this._resolvedColumns.map(c => c.getField().getName()));\n const result = new Set(this._hiddenColumns);\n\n for (const f of this._store.model.getFields()) {\n if (!resolvedNames.has(f.getName())) {\n result.add(f.getName());\n }\n }\n\n return result;\n }\n\n /**\n * Displays the column visibility context menu, listing only columns present in\n * the resolved column list (excluded columns do not appear).\n *\n * @param x - Viewport x coordinate for the menu.\n * @param y - Viewport y coordinate for the menu.\n */\n private showColumnMenu(x: number, y: number): void {\n const columns = this._resolvedColumns\n .slice()\n .sort((a, b) => a.getField().getOrder() - b.getField().getOrder());\n\n const items: MenuItemConfig[] = [];\n let lastGroup: string | null | undefined = undefined;\n\n // Walk in display order, emitting a disabled section header\n // each time the group changes. Ungrouped columns sit flush\n // (no section header above them); transitioning back to an\n // ungrouped column from a grouped run is treated as \"end of\n // group\" — the next ungrouped item just appears with no\n // preceding header. Field-list order matches the parent\n // header band above, so the menu reads as a vertical\n // restatement of what the user sees on screen.\n //\n // The indent uses non-breaking spaces (` `) because the\n // menu item renders text with the default `white-space: nowrap`\n // setting, which still collapses runs of ASCII spaces — regular\n // `' '` would render as a single space.\n const GROUP_INDENT = \"    \";\n\n columns.forEach(col => {\n const fieldName = col.getField().getName();\n const visible = !this._hiddenColumns.has(fieldName);\n const group = col.getGroup();\n\n if (group !== null && group !== lastGroup) {\n items.push({ text: group, enabled: false });\n }\n\n const indent = group !== null ? GROUP_INDENT : \"\";\n\n // An unhideable column is currently visible (since it\n // cannot be hidden) — the menu entry stays in the list so\n // the user sees the column's identity but is disabled to\n // signal it can't be toggled.\n const hideable = !col.isUnhideable();\n\n items.push({\n text: indent + (visible ? '✓ ' : ' ') + fieldName,\n action: () => this.setColumnVisible(fieldName, !visible),\n enabled: hideable,\n });\n\n lastGroup = group;\n });\n\n items.push(\n { separator: true },\n { text: 'Reset columns', action: () => this.resetColumns() }\n );\n\n if (this._exportMenuEnabled) {\n items.push(\n { separator: true },\n { text: 'Export as CSV', action: () => this.exportCSV() },\n { text: 'Export as JSON', action: () => this.exportJSON() }\n );\n }\n\n this._columnContextMenu.show(x, y, items);\n }\n\n /**\n * Enables or disables the \"Export as CSV\" / \"Export as JSON\" entries in\n * the column context menu.\n *\n * @param enabled - When true the export items are appended to the menu.\n */\n setExportMenuEnabled(enabled: boolean): this {\n this._exportMenuEnabled = enabled;\n\n return this;\n }\n\n /**\n * Triggers a CSV download of the current store view.\n *\n * @param options - Optional export options (e.g. include hidden columns, custom filename).\n */\n exportCSV(options?: ExportOptions): void {\n const columns = this.getExportColumns(options?.includeHidden ?? false);\n const records = this._store.getRecords();\n\n TableExporter.exportCSV(columns, records, this._columnConfigs, options);\n }\n\n /**\n * Triggers a JSON download of the current store view.\n *\n * @param options - Optional export options (e.g. include hidden columns, custom filename).\n */\n exportJSON(options?: ExportOptions): void {\n const columns = this.getExportColumns(options?.includeHidden ?? false);\n const records = this._store.getRecords();\n\n TableExporter.exportJSON(columns, records, this._columnConfigs, options);\n }\n\n /**\n * Returns the columns to include in an export.\n *\n * @param includeHidden - When true, all resolved columns are returned; otherwise only visible columns.\n * @returns The columns to export, in display order.\n */\n private getExportColumns(includeHidden: boolean): Column[] {\n return includeHidden ? this._resolvedColumns.slice() : this.getColumns();\n }\n\n /**\n * Captures the resize origin when a column-resize drag begins: the absolute\n * pointer `clientX` and the current widths of the adjacent column pair.\n * {@link onColumnResize} derives the new widths from these origins so\n * over-travel past a column's `minWidth`/`maxWidth` is absorbed without the\n * edge decoupling from the cursor on reversal.\n *\n * @param colIndex - Zero-based index of the column whose right edge is being dragged.\n * @param clientX - The absolute pointer `clientX` at the moment the drag began.\n */\n private onColumnResizeStart(colIndex: number, clientX: number): void {\n const n = this._columnWidths.length;\n\n if (n === 0 || colIndex >= n - 1) {\n return;\n }\n\n this._resizeOriginClientX = clientX;\n this._resizeOriginW0 = this._columnWidths[colIndex];\n this._resizeOriginW1 = this._columnWidths[colIndex + 1];\n }\n\n /**\n * Handles a column resize drag, clamping the adjacent column pair to their\n * per-column `minWidth` and `maxWidth` constraints.\n *\n * @param colIndex - Zero-based index of the column whose right edge is being dragged.\n * @param clientX - The absolute pointer `clientX` for this move.\n *\n * @remarks The new widths are derived from the origin captured in\n * {@link onColumnResizeStart} as `originWidth ± (clientX − originClientX)`,\n * then clamped/redistributed against the pair's `minWidth`/`maxWidth`.\n * Clamping the absolute result (rather than accumulating per-move deltas)\n * makes dragging past a column's minimum idempotent: the edge stays at the\n * boundary until the pointer returns past the coordinate where the clamp\n * was hit.\n */\n private onColumnResize(colIndex: number, clientX: number): void {\n const n = this._columnWidths.length;\n\n if (n === 0 || colIndex >= n - 1) {\n return;\n }\n\n const columns = this.getColumns();\n const min0 = columns[colIndex]?.getMinWidth() ?? 30;\n const max0 = columns[colIndex]?.getMaxWidth() ?? Infinity;\n const min1 = columns[colIndex + 1]?.getMinWidth() ?? 30;\n const max1 = columns[colIndex + 1]?.getMaxWidth() ?? Infinity;\n\n const delta = clientX - this._resizeOriginClientX;\n\n let w0 = this._resizeOriginW0 + delta;\n let w1 = this._resizeOriginW1 - delta;\n\n if (w0 < min0) {\n w1 += w0 - min0;\n w0 = min0;\n }\n\n if (w1 < min1) {\n w0 += w1 - min1;\n w1 = min1;\n }\n\n if (w0 < min0 || w1 < min1 || w0 > max0 || w1 > max1) {\n return;\n }\n\n this._columnWidths[colIndex] = w0;\n this._columnWidths[colIndex + 1] = w1;\n\n this.doLayout();\n }\n\n /**\n * Resets column visibility and widths to their initial spec-defined state.\n *\n * Columns marked `hidden: true` in the spec are re-hidden; all others are shown.\n * All manually resized widths are discarded and recomputed from defaults.\n */\n private resetColumns(): void {\n this._hiddenColumns = new Set();\n this.initHiddenFromSpec();\n this._savedColumnWidths = new Map();\n this._columnWidths = this.getColumns().map(col => this.defaultColumnWidth(col));\n\n const effectiveHidden = this.getEffectiveHiddenSet();\n\n this._header.setHiddenColumns(effectiveHidden);\n this._body.setHiddenColumns(effectiveHidden);\n this.doLayout();\n }\n}\n\nconst TableCallable = callable(Table);\ntype TableCallable = Table;\nexport {\n Table as _Table,\n TableCallable as Table\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { AbstractStore } from \"~/data/AbstractStore.js\";\nimport { Border } from \"~/layout/Border.js\";\nimport { HBox } from \"~/layout/HBox.js\";\nimport { Container } from \"~/core/Container.js\";\nimport { Placement } from \"~/primitive/Placement.js\";\nimport { Button } from \"~/component/button/Button.js\";\nimport { PaginationBar } from \"~/component/display/PaginationBar.js\";\nimport { ProgressSpinner } from \"~/component/display/ProgressSpinner.js\";\nimport { Table } from \"~/component/table/Table.js\";\nimport { ExportOptions } from \"~/component/table/TableExporter.js\";\nimport { Tooltip } from \"~/overlay/Tooltip.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { plus } from \"~/glyphs/solid/plus.js\";\nimport { minus } from \"~/glyphs/solid/minus.js\";\nimport { arrows_rotate } from \"~/glyphs/solid/arrows_rotate.js\";\nimport { ban } from \"~/glyphs/solid/ban.js\";\n\nGlyph.register(plus, minus, arrows_rotate, ban);\n\n/**\n * A composite panel that combines a {@link Table} with an add/remove/sync toolbar.\n *\n * The toolbar is docked to the north region; the table fills the center region.\n *\n * @category Components\n */\nclass TablePanel extends Container {\n\n private _table: Table;\n private _toolbar: Component;\n private _syncBtn: Button;\n private _rejectBtn: Button;\n private _spinner: ProgressSpinner | null = null;\n private _paginationBar: PaginationBar | undefined = undefined;\n\n constructor(store: AbstractStore) {\n super();\n\n this.setLayoutManager(new Border());\n\n this._toolbar = new Component();\n this._toolbar.setLayoutManager(new HBox());\n\n const addBtn = new Button({ glyph: \"plus\" });\n addBtn.setPreferredSize(28, 28);\n addBtn.on(\"action\", () => this._table.addRow());\n Tooltip.attach(addBtn, \"Add row\");\n this._toolbar.addComponent(addBtn);\n\n const removeBtn = new Button({ glyph: \"minus\" });\n removeBtn.setPreferredSize(28, 28);\n removeBtn.on(\"action\", () => this._table.removeSelectedRow());\n Tooltip.attach(removeBtn, \"Remove selected row\");\n this._toolbar.addComponent(removeBtn);\n\n this._syncBtn = new Button({ glyph: \"arrows-rotate\" });\n this._syncBtn.setPreferredSize(28, 28);\n this._syncBtn.on(\"action\", () => this._table.sync());\n Tooltip.attach(this._syncBtn, \"Sync pending changes\");\n this._toolbar.addComponent(this._syncBtn);\n\n this._rejectBtn = new Button({ glyph: \"ban\" });\n this._rejectBtn.setPreferredSize(28, 28);\n this._rejectBtn.on(\"action\", () => this._table.reject());\n Tooltip.attach(this._rejectBtn, \"Reject pending changes\");\n this._toolbar.addComponent(this._rejectBtn);\n\n this._table = new Table(store);\n\n super.addComponent(this._toolbar, { placement: Placement.NORTH });\n super.addComponent(this._table, { placement: Placement.CENTER });\n\n store.on('loadingchange', (payload: { loading: boolean }) => {\n if (!this._spinner) {\n this._spinner = new ProgressSpinner(24);\n }\n\n if (payload.loading) {\n this._spinner.showOverlay(this._table);\n } else {\n this._spinner.hideOverlay();\n }\n });\n\n const refreshSyncButtons = (): void => this.refreshSyncButtons();\n store.on('add', refreshSyncButtons);\n store.on('remove', refreshSyncButtons);\n store.on('datachange', refreshSyncButtons);\n store.on('sync', refreshSyncButtons);\n store.on('load', refreshSyncButtons);\n\n this.refreshSyncButtons();\n }\n\n /**\n * Updates the Sync and Reject button-enabled state to reflect whether the\n * store has any unsynced changes. Both buttons disable when there is\n * nothing to sync or reject.\n */\n private refreshSyncButtons(): void {\n const hasChanges = this._table.getStore().hasPendingChanges();\n\n this._syncBtn.setEnabled(hasChanges);\n this._rejectBtn.setEnabled(hasChanges);\n }\n\n /**\n * Returns the Table component managed by this panel.\n *\n * @returns The managed {@link Table} instance.\n */\n getTable(): Table {\n return this._table;\n }\n\n /**\n * Returns the toolbar component containing the add/remove/sync buttons.\n *\n * @returns The toolbar {@link Component}.\n */\n getToolbar(): Component {\n return this._toolbar;\n }\n\n /**\n * Enables or disables the \"Export as CSV\" / \"Export as JSON\" entries in\n * the underlying table's column context menu.\n *\n * @param enabled - When true the export items are appended to the menu.\n */\n setExportMenuEnabled(enabled: boolean): this {\n this._table.setExportMenuEnabled(enabled);\n\n return this;\n }\n\n /**\n * Triggers a CSV download of the current store view.\n *\n * @param options - Optional export options (e.g. include hidden columns, custom filename).\n */\n exportCSV(options?: ExportOptions): void {\n this._table.exportCSV(options);\n }\n\n /**\n * Triggers a JSON download of the current store view.\n *\n * @param options - Optional export options (e.g. include hidden columns, custom filename).\n */\n exportJSON(options?: ExportOptions): void {\n this._table.exportJSON(options);\n }\n\n /**\n * Docks a {@link PaginationBar} to the south region of the panel.\n *\n * @param bar - The pagination bar to attach. Replaces any previously attached bar.\n *\n * @remarks\n * Any previously attached bar is removed and disposed before the new one\n * is installed, so its store listeners do not leak.\n */\n setPaginationBar(bar: PaginationBar): this {\n if (this._paginationBar) {\n this.removeComponent(this._paginationBar);\n this._paginationBar.dispose();\n }\n\n this._paginationBar = bar;\n super.addComponent(bar, { placement: Placement.SOUTH });\n\n return this;\n }\n\n /**\n * Returns the currently attached pagination bar, or undefined if none is set.\n *\n * @returns The pagination bar previously installed via {@link setPaginationBar}.\n */\n getPaginationBar(): PaginationBar | undefined {\n return this._paginationBar;\n }\n}\n\nconst TablePanelCallable = callable(TablePanel);\ntype TablePanelCallable = TablePanel;\nexport {\n TablePanel as _TablePanel,\n TablePanelCallable as TablePanel\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { _Body } from \"~/component/table/Body.js\";\nimport { AbstractStore } from \"~/data/AbstractStore.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { DragEventDetail, DragManager } from \"~/overlay/DragManager.js\";\nimport { ModelRecord } from \"~/data/ModelRecord.js\";\nimport { Row } from \"~/component/table/Row.js\";\nimport { TreeCellRenderer } from \"~/component/table/cell/renderer/TreeCell.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Drop-target outcome callback injected by\n * [`TreeTable`](/api/component/table/classes/TreeTable). Returns whether\n * the drop reparented the dragged record.\n *\n * @internal Framework wiring between `TreeTable` and `TreeBody`. Not\n * part of the public API.\n */\ntype TreeBodyReparentHandler = (\n dragged: ModelRecord,\n newParent: ModelRecord | null,\n) => boolean;\n\n/**\n * Drop-target validity callback. Returns `true` when the dragged record\n * may legally land under `newParent`. Used by the per-row drop targets\n * to drive the green/red feedback tint before commit.\n *\n * @internal Framework wiring between `TreeTable` and `TreeBody`. Not\n * part of the public API.\n */\ntype TreeBodyReparentValidator = (\n dragged: ModelRecord,\n newParent: ModelRecord | null,\n) => boolean;\n\n/**\n * A flattened entry in {@link TreeBody}'s visible-row list.\n *\n * @category Components\n */\nexport interface FlatRecord {\n /** The record at this row of the flattened, expansion-aware view. */\n record: ModelRecord;\n /** Zero-based nesting depth (0 = root). */\n depth: number;\n /** Whether this record has at least one child record in the store. */\n hasChildren: boolean;\n /** Whether this record is currently expanded (false for leaves). */\n expanded: boolean;\n /** Number of sibling records under the same parent — drives `aria-setsize`. */\n siblingCount: number;\n /** 1-based position among siblings — drives `aria-posinset`. */\n posInSet: number;\n}\n\n/**\n * Construction-time configuration for a {@link TreeBody}. Mirrors the\n * subset of {@link TreeTableSpec} the body needs at run time.\n *\n * @category Components\n */\nexport interface TreeBodySpec {\n /** Field carrying each record's id. */\n idField: string;\n /** Field carrying each record's parent id, or `null` for roots. */\n parentField: string;\n /** Field name of the column whose cell carries the indent + toggle. */\n treeColumn: string;\n /** Pixels of indentation per depth level. */\n indentPx: number;\n}\n\n/**\n * A virtual-scrolling body for a {@link TreeTable}. Subclass of\n * [`Body`](/api/component/table/classes/Body) — reuses the entire\n * row-pool / virtual-scroll / selection / focus / editing / context\n * menu / keyboard machinery, overriding only the visible-record source\n * and a handful of hooks the base class exposes for subclasses:\n *\n * - `Body.onStoreChange` — rebuilds the parent/child index and\n * re-flattens the visible subtree before the inherited refresh runs.\n * - `Body.getVisibleRecords` — returns the depth-aware,\n * expansion-aware flat-record list rather than the store view.\n * - `Body.createRow` — constructs each pool row with the tree\n * column's field name so the matching cell carries a\n * [`TreeCellRenderer`](/api/component/table/classes/TreeCellRenderer).\n * - `Body.computeRowAria` — additionally writes `aria-level`,\n * `aria-expanded`, `aria-setsize`, `aria-posinset`.\n * - `Body.afterRowBound` — pushes depth + expansion state to the\n * row's tree cell renderer on every render pass.\n * - `Body.onSubtreeClick` — dispatches toggle clicks to\n * {@link setExpanded} before falling through to the inherited\n * row-click selection handling.\n * - `Body.onKeyDown` — adds `ArrowRight` / `ArrowLeft` expand /\n * collapse / parent-jump on top of the inherited row + column nav.\n *\n * Expand state is keyed by the record's id (the `idField` value), not\n * by `ModelRecord` reference — a store sync that replaces records\n * preserves expansion.\n *\n * Sort interaction: when the store is sorted, the parent/child index\n * walks the sorted records, so children sit right under each parent at\n * the moment of flatten. A parent dropped by a filter takes its subtree\n * out of the visible list.\n *\n * @category Components\n */\nclass TreeBody extends _Body {\n\n declare private _idField: string;\n declare private _parentField: string;\n declare private _treeColumn: string;\n declare private _indentPx: number;\n\n private _byId: Map<any, ModelRecord> = new Map();\n private _childIds: Map<any, ModelRecord[]> = new Map();\n private _expanded: Set<any> = new Set();\n private _flatRows: FlatRecord[] = [];\n\n /**\n * Per-row drag-source + drop-target teardown bag. Keyed by the\n * pool `Row` (whose identity is stable across rebinds — the\n * underlying record changes, but the row component does not), so a\n * rebind tears down the previous closures around the previous\n * record before installing fresh ones.\n */\n private _rowDnDTeardowns: Map<Row, () => void> = new Map();\n\n /** Empty-area drop-target teardown — registered in {@link init}. */\n private _emptyAreaDropTeardown: (() => void) | null = null;\n\n /** Optional reparent commit injected by `TreeTable`. */\n private _reparentHandler: TreeBodyReparentHandler | null = null;\n\n /** Optional reparent validity check injected by `TreeTable`. */\n private _reparentValidator: TreeBodyReparentValidator | null = null;\n\n /**\n * Constructs a TreeBody bound to the given store, configured by the\n * tree spec.\n *\n * @param store - The data store. Records must carry `idField` and\n * `parentField` values; roots have a null/undefined parent id.\n * @param spec - The tree configuration (id field, parent field,\n * tree column, indent pixels).\n */\n constructor(store: AbstractStore, spec: TreeBodySpec) {\n super(store);\n\n this._idField = spec.idField;\n this._parentField = spec.parentField;\n this._treeColumn = spec.treeColumn;\n this._indentPx = spec.indentPx;\n\n // The base constructor's `bindStore` has already wired\n // `onStoreChange`, but with `this._byId` / `_childIds` /\n // `_flatRows` still empty (the class-field initializers above\n // run *after* `super()`). Build the initial index now so the\n // first `renderWindow` reads a populated `_flatRows`.\n this.rebuildIndex();\n this.flatten();\n }\n\n /**\n * Returns the field name carrying each record's id.\n *\n * @returns The id field name.\n */\n getIdField(): string {\n return this._idField;\n }\n\n /**\n * Returns the field name carrying each record's parent id.\n *\n * @returns The parent-id field name.\n */\n getParentField(): string {\n return this._parentField;\n }\n\n /**\n * Returns the name of the column whose cell carries the indent +\n * expand/collapse toggle.\n *\n * @returns The tree-column field name.\n */\n getTreeColumn(): string {\n return this._treeColumn;\n }\n\n /**\n * Returns the flattened visible-row list snapshot. The list is\n * rebuilt on every store event and on every expand/collapse.\n *\n * @returns The current flat-record array (do not mutate).\n */\n getFlatRecords(): FlatRecord[] {\n return this._flatRows;\n }\n\n /**\n * Resolves a record by its id-field value. Reads the `_byId` map\n * maintained by `rebuildIndex`, so the lookup is O(1) and stays in\n * sync with the latest store snapshot.\n *\n * @param id - The id-field value to look up.\n *\n * @returns The matching record, or `undefined` when the id is not\n * present in the current store view.\n */\n getRecordById(id: any): ModelRecord | undefined {\n return this._byId.get(id);\n }\n\n /**\n * Returns the records that name `id` as their parent-id. Reads the\n * `_childIds` map maintained by `rebuildIndex`; returns an empty\n * array (not the underlying list) when the id has no children, so\n * callers can iterate without an existence check.\n *\n * @param id - The parent-id field value to look up.\n *\n * @returns A snapshot of the child records (do not mutate).\n */\n getChildrenOf(id: any): ModelRecord[] {\n return this._childIds.get(id) ?? [];\n }\n\n /**\n * Returns whether the given record currently has at least one\n * child — i.e. behaves as a directory in the rendered tree.\n *\n * @param record - The record to test.\n *\n * @returns `true` when the record has children; `false` for leaves.\n */\n isDirectoryRecord(record: ModelRecord): boolean {\n return this.getChildrenOf(record.get(this._idField)).length > 0;\n }\n\n /**\n * Walks the parent chain from `descendant` upward and returns\n * `true` when `ancestor` is found at any depth. Used by\n * [`TreeTable.reparentRow`](/api/component/table/classes/TreeTable#reparentrow)\n * to reject drops that would create a cycle.\n *\n * @param ancestor - The candidate ancestor record.\n * @param descendant - The starting record. The walk follows\n * `descendant.get(parentField)` recursively.\n *\n * @returns `true` when `ancestor === descendant` or `ancestor` is\n * any transitive parent of `descendant`.\n */\n isAncestorOf(ancestor: ModelRecord, descendant: ModelRecord): boolean {\n if (ancestor === descendant) {\n return true;\n }\n\n const ancestorId = ancestor.get(this._idField);\n\n let current: ModelRecord | undefined = descendant;\n const seen = new Set<any>();\n\n while (current) {\n const parentId = current.get(this._parentField);\n\n if (parentId == null) {\n return false;\n }\n\n if (parentId === ancestorId) {\n return true;\n }\n\n // Defensive cycle guard — a corrupt parent chain must not\n // infinite-loop the validity check.\n if (seen.has(parentId)) {\n return false;\n }\n\n seen.add(parentId);\n current = this._byId.get(parentId);\n }\n\n return false;\n }\n\n /**\n * Returns whether the given record is currently expanded. A leaf\n * record (no children) reports `false` even if its id is in the\n * expansion set.\n *\n * @param record - The record to query.\n *\n * @returns `true` when the record is a branch and currently\n * expanded, `false` otherwise.\n */\n isExpanded(record: ModelRecord): boolean {\n const id = record.get(this._idField);\n const children = this._childIds.get(id);\n\n return !!children && children.length > 0 && this._expanded.has(id);\n }\n\n /**\n * Expands or collapses the given record. No-op for leaves and for\n * records not present in the current store view.\n *\n * @param record - The record to expand or collapse.\n * @param expanded - `true` to expand, `false` to collapse.\n *\n * @returns This body, for method chaining.\n */\n setExpanded(record: ModelRecord, expanded: boolean): this {\n const id = record.get(this._idField);\n const children = this._childIds.get(id);\n\n if (!children || children.length === 0) {\n return this;\n }\n\n if (expanded) {\n if (this._expanded.has(id)) {\n return this;\n }\n\n this._expanded.add(id);\n } else {\n if (!this._expanded.has(id)) {\n return this;\n }\n\n this._expanded.delete(id);\n }\n\n this.flatten();\n this.invalidateRowBindings();\n this.renderWindow();\n\n return this;\n }\n\n /**\n * Expands every record at depth `<= depth`. Roots are at depth 0,\n * so `expandToDepth(0)` expands only the roots, `expandToDepth(1)`\n * expands roots and their children, and so on.\n *\n * @param depth - Maximum depth to expand.\n *\n * @returns This body, for method chaining.\n */\n expandToDepth(depth: number): this {\n this._expanded.clear();\n\n const recurse = (records: ModelRecord[], d: number): void => {\n if (d > depth) {\n return;\n }\n\n for (const record of records) {\n const id = record.get(this._idField);\n const children = this._childIds.get(id);\n\n if (children && children.length > 0) {\n this._expanded.add(id);\n recurse(children, d + 1);\n }\n }\n };\n\n const roots = this._childIds.get(null) ?? [];\n recurse(roots, 0);\n\n this.flatten();\n this.invalidateRowBindings();\n this.renderWindow();\n\n return this;\n }\n\n /**\n * Collapses every record. Equivalent to clearing the expansion set\n * and refreshing the view.\n *\n * @returns This body, for method chaining.\n */\n collapseAll(): this {\n if (this._expanded.size === 0) {\n return this;\n }\n\n this._expanded.clear();\n\n this.flatten();\n this.invalidateRowBindings();\n this.renderWindow();\n\n return this;\n }\n\n /**\n * Expands every record that has children, regardless of depth.\n *\n * @returns This body, for method chaining.\n */\n expandAll(): this {\n const next = new Set<any>();\n\n for (const [parentId, children] of this._childIds) {\n if (parentId !== null && children.length > 0) {\n next.add(parentId);\n }\n }\n\n // Roots that have children also expand — they're keyed in\n // `_childIds` under `null` (their parent) but the expand set\n // is keyed by *the record's own id*.\n const roots = this._childIds.get(null) ?? [];\n for (const root of roots) {\n const id = root.get(this._idField);\n const children = this._childIds.get(id);\n\n if (children && children.length > 0) {\n next.add(id);\n }\n }\n\n this._expanded = next;\n\n this.flatten();\n this.invalidateRowBindings();\n this.renderWindow();\n\n return this;\n }\n\n /**\n * Returns the toggle element of the row's `TreeCellRenderer`, or\n * `null` when the row has no tree cell or the tree cell renders a\n * leaf (no toggle).\n *\n * @param row - The pool row to inspect.\n *\n * @returns The toggle's DOM element handle, or `null`.\n */\n private getToggleElement(row: Row): Handle | null {\n const treeCell = row.getTreeCell();\n\n if (!treeCell) {\n return null;\n }\n\n const renderer = treeCell.getRenderer();\n\n if (!(renderer instanceof TreeCellRenderer)) {\n return null;\n }\n\n const toggle = renderer.getToggle();\n\n return toggle ? (toggle.getElement() ?? null) : null;\n }\n\n /**\n * Initialises the tree body, then registers the empty-area drop\n * target so a row released over the body's whitespace reparents to\n * root. The element must exist before [`DragManager.makeDropTarget`](/api/overlay/variables/DragManager#makedroptarget)\n * can resolve its id — defer past the base class `init`.\n *\n * @param element - Optional element to initialise with.\n *\n * @returns This body, for method chaining.\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n this.ensureEmptyAreaDropTarget();\n\n return this;\n }\n\n /**\n * Rebuilds the parent/child index from the current store records,\n * then re-flattens the visible subtree. Invoked from\n * `Body.onStoreChange` before the inherited refresh runs.\n */\n protected onStoreChange(): void {\n this.rebuildIndex();\n this.flatten();\n\n super.onStoreChange();\n }\n\n /**\n * Returns the depth-aware visible-record list — the records the\n * row pool binds to.\n *\n * @returns The current flat-record list as plain `ModelRecord` array.\n */\n protected getVisibleRecords(): ModelRecord[] {\n return this._flatRows.map(f => f.record);\n }\n\n /**\n * Constructs a row carrying a {@link TreeCellRenderer} on the tree\n * column.\n *\n * @returns The new pool row.\n */\n protected createRow(): Row {\n return new Row(\n this.getStore().model,\n undefined,\n this.getHiddenColumns(),\n this.getColumnConfigs(),\n (record) => this.getStore().notifyRecordChanged(record),\n this._treeColumn,\n );\n }\n\n /**\n * Returns the tree column's field name so an in-place column-toggle\n * sync preserves the {@link TreeCellRenderer} on the tree-column\n * cell. Overrides the base, which returns `undefined`.\n *\n * @returns The tree column's field name.\n */\n protected getTreeFieldName(): string {\n return this._treeColumn;\n }\n\n /**\n * Writes the standard `aria-rowindex` from the base class, then\n * adds the tree-specific ARIA properties read from the flat record\n * at the same index.\n *\n * @param row - The pool row whose ARIA attributes to update.\n * @param dataIndex - The row's index into `_flatRows`.\n */\n protected computeRowAria(row: Row, dataIndex: number): void {\n super.computeRowAria(row, dataIndex);\n\n const flat = this._flatRows[dataIndex];\n\n if (!flat) {\n return;\n }\n\n row.getAria().setLevel(flat.depth + 1);\n row.getAria().setExpanded(flat.hasChildren ? flat.expanded : null);\n row.getAria().setSetSize(flat.siblingCount);\n row.getAria().setPosInSet(flat.posInSet);\n }\n\n /**\n * Pushes depth + expansion state to the row's\n * [`TreeCellRenderer`](/api/component/table/classes/TreeCellRenderer)\n * so the toggle glyph and indent match the flat record at this\n * data index, then re-wires the per-row drag source / drop target\n * for the bound record.\n *\n * @param row - The pool row being processed.\n * @param dataIndex - The row's index into `_flatRows`.\n * @param wasRebound - True when the row's record changed on this\n * pass; the DnD closures captured the previous record and must be\n * replaced.\n */\n protected afterRowBound(row: Row, dataIndex: number, wasRebound: boolean): void {\n const treeCell = row.getTreeCell();\n const flat = this._flatRows[dataIndex];\n\n if (treeCell && flat) {\n const renderer = treeCell.getRenderer() as TreeCellRenderer<any>;\n\n renderer.setTreeState(flat.depth, flat.hasChildren, flat.expanded);\n }\n\n if (this._reparentHandler === null) {\n return;\n }\n\n if (!flat) {\n this.teardownRowDnD(row);\n\n return;\n }\n\n if (wasRebound || !this._rowDnDTeardowns.has(row)) {\n this.installRowDnD(row, flat.record);\n }\n }\n\n /**\n * Installs the manager-side [`DragManager.makeDragSource`](/api/overlay/variables/DragManager#makedragsource) +\n * [`DragManager.makeDropTarget`](/api/overlay/variables/DragManager#makedroptarget) pair for a pool row, replacing\n * any previously installed pair.\n */\n private installRowDnD(row: Row, record: ModelRecord): void {\n this.teardownRowDnD(row);\n\n const tearDownSource = DragManager.makeDragSource(row, {\n dragData: { recordId: record.get(this._idField) },\n });\n\n const dropTargetForRow = (detail: DragEventDetail): ModelRecord | null => {\n const dragged = this.resolveDragged(detail);\n\n if (!dragged) {\n return null;\n }\n\n return this.resolveDropParentForRow(record);\n };\n\n const tearDownTarget = DragManager.makeDropTarget(row, {\n accepts: (detail: DragEventDetail) => {\n const dragged = this.resolveDragged(detail);\n\n if (!dragged) {\n return false;\n }\n\n const newParent = dropTargetForRow(detail);\n\n return this._reparentValidator?.(dragged, newParent) ?? false;\n },\n onDrop: (detail: DragEventDetail): boolean | void => {\n const dragged = this.resolveDragged(detail);\n\n if (!dragged) {\n return false;\n }\n\n const newParent = dropTargetForRow(detail);\n\n return this._reparentHandler?.(dragged, newParent) ?? false;\n },\n });\n\n this._rowDnDTeardowns.set(row, () => {\n tearDownSource();\n tearDownTarget();\n });\n }\n\n /**\n * Resolves the dragged record from the manager's detail payload\n * (which carries the record-id assigned in {@link installRowDnD}).\n * Returns `undefined` when the record is no longer in the store.\n */\n private resolveDragged(detail: DragEventDetail): ModelRecord | undefined {\n const id = detail.dragData[\"recordId\"];\n\n return this._byId.get(id);\n }\n\n /**\n * Maps a hovered row's bound record to the directory it implies.\n * Directories accept drops directly; leaves forward to their\n * parent (so \"drop on a sibling file\" lands next to it under the\n * same directory).\n */\n private resolveDropParentForRow(record: ModelRecord): ModelRecord | null {\n if (this.isDirectoryRecord(record)) {\n return record;\n }\n\n const parentId = record.get(this._parentField);\n\n if (parentId == null) {\n return null;\n }\n\n return this._byId.get(parentId) ?? null;\n }\n\n /**\n * Tears down the drag-source + drop-target pair for a pool row.\n */\n private teardownRowDnD(row: Row): void {\n const teardown = this._rowDnDTeardowns.get(row);\n\n if (teardown) {\n teardown();\n this._rowDnDTeardowns.delete(row);\n }\n }\n\n /**\n * Wires the reparent callbacks supplied by `TreeTable`. Installed\n * once at construction so per-row DnD has everything it needs by\n * the time `afterRowBound` first runs.\n *\n * @param validator - Pre-drop validity check.\n * @param handler - Commits the reparent and returns whether it\n * succeeded.\n *\n * @returns This body, for method chaining.\n *\n * @internal Framework wiring; application code does not call this.\n */\n setReparentHandlers(validator: TreeBodyReparentValidator, handler: TreeBodyReparentHandler): this {\n this._reparentValidator = validator;\n this._reparentHandler = handler;\n\n if (this.getElement()) {\n this.ensureEmptyAreaDropTarget();\n }\n\n return this;\n }\n\n /**\n * Registers the body element itself as a drop target so a row\n * released over the empty area below the last row reparents to\n * root. Idempotent — safe to call after either\n * {@link setReparentHandlers} or `init`.\n */\n private ensureEmptyAreaDropTarget(): void {\n if (this._emptyAreaDropTeardown !== null) {\n return;\n }\n\n if (this._reparentHandler === null) {\n return;\n }\n\n this._emptyAreaDropTeardown = DragManager.makeDropTarget(this, {\n accepts: (detail: DragEventDetail) => {\n const dragged = this.resolveDragged(detail);\n\n if (!dragged) {\n return false;\n }\n\n return this._reparentValidator?.(dragged, null) ?? false;\n },\n onDrop: (detail: DragEventDetail): boolean | void => {\n const dragged = this.resolveDragged(detail);\n\n if (!dragged) {\n return false;\n }\n\n return this._reparentHandler?.(dragged, null) ?? false;\n },\n });\n }\n\n /**\n * Intercepts subtree clicks on a tree-toggle and routes them to\n * {@link setExpanded}. Other clicks fall through to the inherited\n * row-selection handler.\n *\n * @param e - The bubbled click event.\n */\n protected onSubtreeClick(e: MouseEvent): void {\n const target = e.target === null ? null : DOM.source.intern(e.target);\n\n for (const row of this.getRowPool()) {\n const toggleEl = this.getToggleElement(row);\n\n if (toggleEl && target !== null && (target === toggleEl || DOM.source.contains(toggleEl, target))) {\n const record = row.getData();\n\n if (record) {\n this.setExpanded(record, !this.isExpanded(record));\n }\n\n return;\n }\n }\n\n super.onSubtreeClick(e);\n }\n\n /**\n * Intercepts `ArrowRight` / `ArrowLeft` for tree navigation,\n * delegating every other key to the base `Body` handler. When\n * there is a selected anchor record:\n *\n * - `ArrowRight` expands a collapsed branch, or moves focus to the\n * first child of an already-expanded branch (no-op on leaves).\n * - `ArrowLeft` collapses an expanded branch, or moves focus to the\n * parent record (no-op at depth 0 with no children).\n *\n * Replaces the base body's `ArrowRight` / `ArrowLeft` column-focus\n * navigation while the user is interacting with the tree. Other\n * keys (`ArrowUp`/`Down`, `Home`/`End`, `Enter`/`Space`,\n * `PageUp`/`PageDown`) keep their inherited behaviour.\n *\n * @param e - The keyboard event fired on the body element.\n */\n protected onKeyDown(e: KeyboardEvent): void {\n if (e.key !== \"ArrowRight\" && e.key !== \"ArrowLeft\") {\n super.onKeyDown(e);\n\n return;\n }\n\n const anchor = this.getSelectedRecord();\n\n if (!anchor) {\n super.onKeyDown(e);\n\n return;\n }\n\n const idx = this._flatRows.findIndex(f => f.record === anchor);\n\n // Anchor exists but isn't in the flat view (e.g. a filter\n // dropped its subtree mid-key-press). Treat as no-op for tree\n // navigation rather than silently swallowing the keystroke.\n if (idx < 0) {\n e.preventDefault();\n\n return;\n }\n\n e.preventDefault();\n\n const flat = this._flatRows[idx];\n\n if (e.key === \"ArrowRight\") {\n this.handleArrowRight(flat, idx);\n } else {\n this.handleArrowLeft(flat, idx);\n }\n }\n\n /**\n * ArrowRight: expand a collapsed branch, or move focus into the\n * first child of an expanded branch. No-op on leaves.\n */\n private handleArrowRight(flat: FlatRecord, idx: number): void {\n if (!flat.hasChildren) {\n return;\n }\n\n if (!flat.expanded) {\n this.setExpanded(flat.record, true);\n\n return;\n }\n\n // Already expanded — move focus to the first child.\n const next = this._flatRows[idx + 1];\n\n if (next && next.depth === flat.depth + 1) {\n this.moveFocusTo(next.record);\n }\n }\n\n /**\n * ArrowLeft: collapse an expanded branch, or move focus to the\n * parent record. No-op when already at depth 0 and either leaf or\n * collapsed.\n */\n private handleArrowLeft(flat: FlatRecord, idx: number): void {\n if (flat.hasChildren && flat.expanded) {\n this.setExpanded(flat.record, false);\n\n return;\n }\n\n if (flat.depth === 0) {\n return;\n }\n\n // Walk backwards to find the parent record (the nearest\n // ancestor row with depth = current - 1).\n for (let i = idx - 1; i >= 0; i--) {\n if (this._flatRows[i].depth === flat.depth - 1) {\n this.moveFocusTo(this._flatRows[i].record);\n\n return;\n }\n }\n }\n\n /**\n * Programmatic equivalent of an arrow-key navigation: selects the\n * record, scrolls it into view, re-renders, and refreshes the\n * focus + active-descendant indicators. Mirrors the trailing\n * `selectRecord + scrollRecordIntoView + renderWindow +\n * _updateActiveDescendant + _updateFocusStyle` sequence the base\n * `Body.onKeyDown` runs after row-nav keys.\n */\n private moveFocusTo(record: ModelRecord): void {\n this.selectRecord(record);\n this.scrollRecordIntoView(record);\n this.renderWindow();\n this._updateActiveDescendant();\n this._updateFocusStyle();\n }\n\n /**\n * Rebuilds `_byId` and `_childIds` from the current store records.\n * Records with a parent id that doesn't resolve to any known record\n * are treated as roots (orphan fallback).\n */\n private rebuildIndex(): void {\n this._byId.clear();\n this._childIds.clear();\n\n const records = this.getStore().getRecords();\n\n for (const record of records) {\n const id = record.get(this._idField);\n\n this._byId.set(id, record);\n }\n\n // Roots sit under the `null` key in `_childIds`. Records whose\n // parent id doesn't resolve fall through to `null` too —\n // orphans render as additional roots rather than silently\n // disappearing.\n for (const record of records) {\n const parentId = record.get(this._parentField);\n const key = parentId != null && this._byId.has(parentId) ? parentId : null;\n\n const list = this._childIds.get(key);\n\n if (list) {\n list.push(record);\n } else {\n this._childIds.set(key, [record]);\n }\n }\n }\n\n /**\n * Re-flattens the visible subtree from the current `_byId` /\n * `_childIds` index. The walk skips children whose parent id is\n * absent from the `_expanded` set.\n */\n private flatten(): void {\n const rows: FlatRecord[] = [];\n\n const recurse = (records: ModelRecord[], depth: number): void => {\n const siblingCount = records.length;\n\n for (let i = 0; i < records.length; i++) {\n const record = records[i];\n const id = record.get(this._idField);\n const children = this._childIds.get(id);\n const hasKids = !!children && children.length > 0;\n const expanded = hasKids && this._expanded.has(id);\n\n rows.push({\n record,\n depth,\n hasChildren: hasKids,\n expanded,\n siblingCount,\n posInSet: i + 1,\n });\n\n if (expanded) {\n recurse(children!, depth + 1);\n }\n }\n };\n\n const roots = this._childIds.get(null) ?? [];\n\n recurse(roots, 0);\n\n this._flatRows = rows;\n }\n}\n\nconst TreeBodyCallable = callable(TreeBody);\ntype TreeBodyCallable = TreeBody;\nexport {\n TreeBody as _TreeBody,\n TreeBodyCallable as TreeBody\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Event } from \"~/core/Event.js\";\nimport { Table } from \"~/component/table/Table.js\";\nimport { TreeBody } from \"~/component/table/TreeBody.js\";\nimport { TreeTableSpec } from \"~/component/table/TreeTableSpec.js\";\nimport { AbstractStore } from \"~/data/AbstractStore.js\";\nimport { ModelRecord } from \"~/data/ModelRecord.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/** Default pixels of indentation per depth level when the spec omits `indentPx`. */\nconst DEFAULT_INDENT_PX = 16;\n\n/**\n * Payload delivered to listeners registered through\n * {@link TreeTable.addRowReparentListener}. Fired after a successful\n * {@link TreeTable.reparentRow} call.\n *\n * @category Components\n */\nexport interface RowReparentDetail {\n /** The record whose `parentField` value changed. */\n record: ModelRecord;\n /** The new parent record, or `null` when reparented to root. */\n newParent: ModelRecord | null;\n /** The previous parent record, or `null` when the row was previously at root. */\n oldParent: ModelRecord | null;\n}\n\n/**\n * A data-bound table whose rows form a parent/child hierarchy.\n *\n * Composes the standard [`Header`](/api/component/table/classes/Header),\n * a tree-aware [`TreeBody`](/api/component/table/classes/TreeBody), and an\n * optional [`FooterRow`](/api/component/table/classes/FooterRow). Reuses\n * every public surface from {@link Table} (CRUD, sync, column visibility\n * and resize, sort cycling, context menu, exporter) — the only\n * difference is the body: virtual-scroll over a depth-aware, expansion-\n * aware flat record list rather than over the store's view directly.\n *\n * Hierarchy is encoded on the store's existing model: each record names\n * the id of its parent (or `null` for roots) via the field named in\n * {@link TreeTableSpec.parentField}. No new store subtype or schema\n * extension is required.\n *\n * One designated column — {@link TreeTableSpec.treeColumn} — renders\n * with an indent + expand/collapse toggle to the left of the value.\n * The toggle is a [`Glyph`](/api/component/display/classes/Glyph)\n * (`caret-down` / `caret-right`); the rest of the cell content is\n * drawn by whichever typed renderer the column would normally use,\n * unchanged.\n *\n * The ARIA role is `treegrid` (not `grid`). Pool rows additionally\n * carry `aria-level`, `aria-expanded`, `aria-setsize`, and\n * `aria-posinset`.\n *\n * @example\n * ```typescript\n * const model = new Model([\n * { name: 'id', type: 'number' },\n * { name: 'parentId', type: 'number' },\n * { name: 'name', type: 'string' },\n * { name: 'size', type: 'number' },\n * ]);\n *\n * const store = new MemoryStore(model, [\n * { id: 1, parentId: null, name: 'Documents', size: 0 },\n * { id: 2, parentId: 1, name: 'report.pdf', size: 84 },\n * { id: 3, parentId: null, name: 'Music', size: 0 },\n * ]);\n *\n * const tree = new TreeTable(store, {\n * idField: 'id',\n * parentField: 'parentId',\n * treeColumn: 'name',\n * columns: [\n * { field: 'name', minWidth: 200 },\n * { field: 'size' },\n * ],\n * });\n *\n * tree.expandToDepth(0);\n * ```\n *\n * @category Components\n */\nclass TreeTable extends Table {\n\n private _treeSpec: TreeTableSpec;\n private _treeBody: TreeBody;\n\n /**\n * Maps each consumer-supplied `rowreparent` listener to the\n * CustomEvent-unwrapping wrapper actually registered with\n * `Event.addListener`. The wrapper is the removable reference;\n * the original listener is the lookup key. Inline arrows can't be\n * removed by reference, so the wrapper is built once at add-time\n * and stored.\n */\n private _rowReparentWrappers = new Map<\n (detail: RowReparentDetail) => void,\n (e: CustomEvent<RowReparentDetail>) => void\n >();\n\n /**\n * Constructs a TreeTable bound to the given store.\n *\n * @param store - The data store carrying the records. Each record\n * must have a value at `spec.idField`; root records have a\n * `null` (or absent) value at `spec.parentField`.\n * @param spec - Tree presentation + hierarchy configuration.\n */\n constructor(store: AbstractStore, spec: TreeTableSpec) {\n const indentPx = spec.indentPx ?? DEFAULT_INDENT_PX;\n const adjustedSpec = TreeTable.markTreeColumnUnhideable(spec);\n\n // Body factory is a closure capturing `spec` from the constructor\n // parameter, so super() can construct the TreeBody before any\n // TreeTable instance field has been initialised. A subclass-\n // overridable `createBody` method would hit the class-field\n // super-trap here (the field carrying the spec would still be\n // `undefined` when the override ran).\n super(store, adjustedSpec, (s) => new TreeBody(s, {\n idField: spec.idField,\n parentField: spec.parentField,\n treeColumn: spec.treeColumn,\n indentPx,\n }));\n\n this._treeSpec = spec;\n this._treeBody = this.getBody() as TreeBody;\n\n this.getAria().setRole(\"treegrid\");\n\n // Inject the reparent callbacks so `TreeBody` can install\n // per-row drag sources / drop targets once each pool row is\n // bound. The validator path lets the per-row drop targets\n // surface the cycle / no-op rejection through the green/red\n // feedback tint without committing the move.\n this._treeBody.setReparentHandlers(\n (dragged, newParent) => this.canReparent(dragged, newParent),\n (dragged, newParent) => this.reparentRow(dragged, newParent),\n );\n }\n\n /**\n * Returns a clone of `spec` with `unhideable: true` set on the column\n * matching `spec.treeColumn`. Synthesises a new `ColumnConfig` entry\n * if the tree column is not listed in `spec.columns`, so that\n * `appendUnlisted: false` cannot drop the tree column.\n *\n * @param spec - The consumer-supplied tree-table spec.\n *\n * @returns A new spec object whose `columns` entry for the tree\n * column carries `unhideable: true`.\n */\n private static markTreeColumnUnhideable(spec: TreeTableSpec): TreeTableSpec {\n const existing = spec.columns.find(c => c.field === spec.treeColumn);\n const updated = existing\n ? spec.columns.map(c => c.field === spec.treeColumn ? { ...c, unhideable: true } : c)\n : [...spec.columns, { field: spec.treeColumn, unhideable: true }];\n\n return { ...spec, columns: updated };\n }\n\n /**\n * Returns the tree-aware body that owns the depth-flattened\n * record list and expand/collapse state. Narrower return type than\n * {@link Table.getBody}.\n *\n * @returns The {@link TreeBody}.\n */\n getBody(): TreeBody {\n return super.getBody() as TreeBody;\n }\n\n /**\n * Returns the tree-table spec passed at construction.\n *\n * @returns The {@link TreeTableSpec} used to configure this tree.\n */\n getTreeSpec(): TreeTableSpec {\n return this._treeSpec;\n }\n\n /**\n * Expands or collapses the given record. No-op for leaves and for\n * records not present in the current store view.\n *\n * @param record - The record to expand or collapse.\n * @param expanded - `true` to expand, `false` to collapse.\n *\n * @returns This table, for method chaining.\n */\n setExpanded(record: ModelRecord, expanded: boolean): this {\n this._treeBody.setExpanded(record, expanded);\n\n return this;\n }\n\n /**\n * Returns whether the given record is currently expanded.\n *\n * @param record - The record to query.\n *\n * @returns `true` when the record is a branch and currently\n * expanded, `false` otherwise.\n */\n isExpanded(record: ModelRecord): boolean {\n return this._treeBody.isExpanded(record);\n }\n\n /**\n * Expands every record at depth `<= depth`. Roots are at depth 0.\n *\n * @param depth - Maximum depth to expand.\n *\n * @returns This table, for method chaining.\n */\n expandToDepth(depth: number): this {\n this._treeBody.expandToDepth(depth);\n\n return this;\n }\n\n /**\n * Collapses every record.\n *\n * @returns This table, for method chaining.\n */\n collapseAll(): this {\n this._treeBody.collapseAll();\n\n return this;\n }\n\n /**\n * Expands every record that has children.\n *\n * @returns This table, for method chaining.\n */\n expandAll(): this {\n this._treeBody.expandAll();\n\n return this;\n }\n\n /**\n * Adds a new record, optionally setting its parent-id field so the\n * new record slots under the given parent.\n *\n * @param defaults - Optional initial field values for the new record.\n * @param parent - Optional. When supplied, the new record's\n * `spec.parentField` value is set to `parent.get(spec.idField)`\n * before insertion.\n *\n * @returns The newly created {@link ModelRecord}.\n */\n addRow(defaults: Record<string, any> = {}, parent?: ModelRecord): ModelRecord {\n const payload = parent\n ? { ...defaults, [this._treeSpec.parentField]: parent.get(this._treeSpec.idField) }\n : defaults;\n\n return super.addRow(payload);\n }\n\n /**\n * Resolves a record by its `idField` value. Delegates to\n * [`TreeBody.getRecordById`](/api/component/table/classes/TreeBody#getrecordbyid).\n *\n * @param id - The id-field value to look up.\n *\n * @returns The matching record, or `undefined` when the id is not\n * present in the current store view.\n */\n getRecordById(id: any): ModelRecord | undefined {\n return this._treeBody.getRecordById(id);\n }\n\n /**\n * Returns whether the given record currently has at least one\n * child — i.e. behaves as a directory in the rendered tree.\n *\n * @param record - The record to test.\n *\n * @returns `true` when the record has children; `false` for leaves.\n */\n isDirectoryRecord(record: ModelRecord): boolean {\n return this._treeBody.isDirectoryRecord(record);\n }\n\n /**\n * Reparents the given record under `newParent` (or root when\n * `newParent` is `null`). Rejects cycles and no-ops, fires a\n * `rowreparent` event on success.\n *\n * @param record - The record to move.\n * @param newParent - The new parent record, or `null` for root.\n *\n * @returns `true` when the reparent succeeded; `false` when it was\n * rejected (cycle, no-op, or unknown record).\n */\n reparentRow(record: ModelRecord, newParent: ModelRecord | null): boolean {\n if (!this.canReparent(record, newParent)) {\n return false;\n }\n\n const parentField = this._treeSpec.parentField;\n const idField = this._treeSpec.idField;\n\n const oldParentId = record.get(parentField);\n const oldParent = oldParentId != null ? this._treeBody.getRecordById(oldParentId) ?? null : null;\n\n record.set(parentField, newParent !== null ? newParent.get(idField) : null);\n this.getStore().notifyRecordChanged(record);\n\n const detail: RowReparentDetail = {\n record,\n newParent,\n oldParent,\n };\n\n Event.fireEvent(this, \"rowreparent\", { detail });\n\n return true;\n }\n\n /**\n * Returns whether `record` may legally be reparented under\n * `newParent`. Used both by {@link reparentRow} for the commit-\n * time guard and by `TreeBody`'s per-row drop targets for the\n * visual feedback tint.\n *\n * @param record - The candidate record.\n * @param newParent - The candidate new parent, or `null` for root.\n *\n * @returns `true` when the move is a real change and would not\n * create a cycle; `false` otherwise.\n */\n private canReparent(record: ModelRecord, newParent: ModelRecord | null): boolean {\n if (!this._treeBody.getRecordById(record.get(this._treeSpec.idField))) {\n return false;\n }\n\n // No-op: dropping a record back into the same parent (or onto\n // itself) is rejected so the visual feedback tint reads \"no\n // valid move\" rather than flashing green and silently doing\n // nothing.\n const currentParentId = record.get(this._treeSpec.parentField);\n const newParentId = newParent !== null ? newParent.get(this._treeSpec.idField) : null;\n\n if (currentParentId === newParentId) {\n return false;\n }\n\n if (newParent !== null && this._treeBody.isAncestorOf(record, newParent)) {\n return false;\n }\n\n return true;\n }\n\n /**\n * Registers a listener fired after every successful\n * {@link reparentRow} call. Use to react to drag-and-drop row\n * moves originating from\n * [`DragManager`](/api/overlay/variables/DragManager).\n *\n * @param listener - Receives the {@link RowReparentDetail} payload.\n *\n * @returns This table, for method chaining.\n */\n addRowReparentListener(listener: (detail: RowReparentDetail) => void): this {\n if (this._rowReparentWrappers.has(listener)) {\n return this;\n }\n\n const wrapper = (e: CustomEvent<RowReparentDetail>): void => listener(e.detail);\n\n this._rowReparentWrappers.set(listener, wrapper);\n Event.addListener(this, \"rowreparent\", wrapper);\n\n return this;\n }\n\n /**\n * Removes a previously registered `rowreparent` listener.\n *\n * @param listener - The exact callback reference passed to\n * {@link addRowReparentListener}.\n *\n * @returns This table, for method chaining.\n */\n removeRowReparentListener(listener: (detail: RowReparentDetail) => void): this {\n const wrapper = this._rowReparentWrappers.get(listener);\n\n if (!wrapper) {\n return this;\n }\n\n Event.removeListener(this, \"rowreparent\", wrapper);\n this._rowReparentWrappers.delete(listener);\n\n return this;\n }\n}\n\nconst TreeTableCallable = callable(TreeTable);\ntype TreeTableCallable = TreeTable;\nexport {\n TreeTable as _TreeTable,\n TreeTableCallable as TreeTable\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { AbstractStore } from \"~/data/AbstractStore.js\";\nimport { Border } from \"~/layout/Border.js\";\nimport { HBox } from \"~/layout/HBox.js\";\nimport { Container } from \"~/core/Container.js\";\nimport { Placement } from \"~/primitive/Placement.js\";\nimport { Button } from \"~/component/button/Button.js\";\nimport { PaginationBar } from \"~/component/display/PaginationBar.js\";\nimport { ProgressSpinner } from \"~/component/display/ProgressSpinner.js\";\nimport { ModelRecord } from \"~/data/ModelRecord.js\";\nimport { TreeTable } from \"~/component/table/TreeTable.js\";\nimport { TreeTableSpec } from \"~/component/table/TreeTableSpec.js\";\nimport { ExportOptions } from \"~/component/table/TableExporter.js\";\nimport { Tooltip } from \"~/overlay/Tooltip.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { plus } from \"~/glyphs/solid/plus.js\";\nimport { minus } from \"~/glyphs/solid/minus.js\";\nimport { arrows_rotate } from \"~/glyphs/solid/arrows_rotate.js\";\nimport { ban } from \"~/glyphs/solid/ban.js\";\n\nGlyph.register(plus, minus, arrows_rotate, ban);\n\n/**\n * A composite panel that combines a {@link TreeTable} with an\n * add/remove/sync toolbar — the tree counterpart to {@link TablePanel}.\n *\n * The toolbar is docked to the north region; the tree table fills the\n * center region. The \"add\" button adds a row under the currently-\n * selected directory — or under a selected leaf's parent, or at root\n * when nothing is selected. Use {@link TreeTable.addRow} on the inner\n * table directly to override the selection-aware target.\n *\n * @category Components\n */\nclass TreeTablePanel extends Container {\n\n private _treeTable: TreeTable;\n private _toolbar: Component;\n private _syncBtn: Button;\n private _rejectBtn: Button;\n private _spinner: ProgressSpinner | null = null;\n private _paginationBar: PaginationBar | undefined = undefined;\n\n /**\n * Constructs a TreeTablePanel bound to the given store, configured\n * by the tree spec.\n *\n * @param store - The data store backing the tree.\n * @param spec - Tree presentation + hierarchy configuration.\n */\n constructor(store: AbstractStore, spec: TreeTableSpec) {\n super();\n\n this.setLayoutManager(new Border());\n\n this._toolbar = new Component();\n this._toolbar.setLayoutManager(new HBox());\n\n const addBtn = new Button({ glyph: \"plus\" });\n addBtn.setPreferredSize(28, 28);\n addBtn.on(\"action\", () => this.addRowUnderSelection());\n Tooltip.attach(addBtn, \"Add row\");\n this._toolbar.addComponent(addBtn);\n\n const removeBtn = new Button({ glyph: \"minus\" });\n removeBtn.setPreferredSize(28, 28);\n removeBtn.on(\"action\", () => this._treeTable.removeSelectedRow());\n Tooltip.attach(removeBtn, \"Remove selected row\");\n this._toolbar.addComponent(removeBtn);\n\n this._syncBtn = new Button({ glyph: \"arrows-rotate\" });\n this._syncBtn.setPreferredSize(28, 28);\n this._syncBtn.on(\"action\", () => this._treeTable.sync());\n Tooltip.attach(this._syncBtn, \"Sync pending changes\");\n this._toolbar.addComponent(this._syncBtn);\n\n this._rejectBtn = new Button({ glyph: \"ban\" });\n this._rejectBtn.setPreferredSize(28, 28);\n this._rejectBtn.on(\"action\", () => this._treeTable.reject());\n Tooltip.attach(this._rejectBtn, \"Reject pending changes\");\n this._toolbar.addComponent(this._rejectBtn);\n\n this._treeTable = new TreeTable(store, spec);\n\n super.addComponent(this._toolbar, { placement: Placement.NORTH });\n super.addComponent(this._treeTable, { placement: Placement.CENTER });\n\n store.on('loadingchange', (payload: { loading: boolean }) => {\n if (!this._spinner) {\n this._spinner = new ProgressSpinner(24);\n }\n\n if (payload.loading) {\n this._spinner.showOverlay(this._treeTable);\n } else {\n this._spinner.hideOverlay();\n }\n });\n\n const refreshSyncButtons = (): void => this.refreshSyncButtons();\n store.on('add', refreshSyncButtons);\n store.on('remove', refreshSyncButtons);\n store.on('datachange', refreshSyncButtons);\n store.on('sync', refreshSyncButtons);\n store.on('load', refreshSyncButtons);\n\n this.refreshSyncButtons();\n }\n\n /**\n * Adds a new record under the user's current selection. When the\n * selection is a directory, the new record slots underneath it;\n * when the selection is a leaf, the record slots under the leaf's\n * parent (matching VS Code / Finder \"new sibling\" behaviour). With\n * no selection, the record lands at root — the documented fallback\n * that mirrors the no-state add of {@link TreeTable.addRow}.\n *\n * @returns The newly created record.\n */\n private addRowUnderSelection(): ModelRecord {\n const selected = this._treeTable.getSelectedRecord();\n\n if (selected === null) {\n return this._treeTable.addRow();\n }\n\n if (this._treeTable.isDirectoryRecord(selected)) {\n return this._treeTable.addRow({}, selected);\n }\n\n const parentId = selected.get(this._treeTable.getTreeSpec().parentField);\n const parent = parentId != null ? this._treeTable.getRecordById(parentId) : undefined;\n\n return this._treeTable.addRow({}, parent);\n }\n\n /**\n * Updates the Sync and Reject button-enabled state to reflect whether the\n * store has any unsynced changes. Both buttons disable when there is\n * nothing to sync or reject.\n */\n private refreshSyncButtons(): void {\n const hasChanges = this._treeTable.getStore().hasPendingChanges();\n\n this._syncBtn.setEnabled(hasChanges);\n this._rejectBtn.setEnabled(hasChanges);\n }\n\n /**\n * Returns the TreeTable component managed by this panel.\n *\n * @returns The managed {@link TreeTable}.\n */\n getTreeTable(): TreeTable {\n return this._treeTable;\n }\n\n /**\n * Returns the toolbar component containing the add/remove/sync buttons.\n *\n * @returns The toolbar {@link Component}.\n */\n getToolbar(): Component {\n return this._toolbar;\n }\n\n /**\n * Enables or disables the \"Export as CSV\" / \"Export as JSON\" entries in\n * the underlying tree table's column context menu.\n *\n * @param enabled - When true the export items are appended to the menu.\n */\n setExportMenuEnabled(enabled: boolean): this {\n this._treeTable.setExportMenuEnabled(enabled);\n\n return this;\n }\n\n /**\n * Triggers a CSV download of the current store view.\n *\n * @param options - Optional export options (e.g. include hidden columns, custom filename).\n */\n exportCSV(options?: ExportOptions): void {\n this._treeTable.exportCSV(options);\n }\n\n /**\n * Triggers a JSON download of the current store view.\n *\n * @param options - Optional export options (e.g. include hidden columns, custom filename).\n */\n exportJSON(options?: ExportOptions): void {\n this._treeTable.exportJSON(options);\n }\n\n /**\n * Docks a {@link PaginationBar} to the south region of the panel.\n *\n * @param bar - The pagination bar to attach. Replaces any previously attached bar.\n */\n setPaginationBar(bar: PaginationBar): this {\n if (this._paginationBar) {\n this.removeComponent(this._paginationBar);\n this._paginationBar.dispose();\n }\n\n this._paginationBar = bar;\n super.addComponent(bar, { placement: Placement.SOUTH });\n\n return this;\n }\n\n /**\n * Returns the currently attached pagination bar, or undefined if none is set.\n *\n * @returns The pagination bar previously installed via {@link setPaginationBar}.\n */\n getPaginationBar(): PaginationBar | undefined {\n return this._paginationBar;\n }\n}\n\nconst TreeTablePanelCallable = callable(TreeTablePanel);\ntype TreeTablePanelCallable = TreeTablePanel;\nexport {\n TreeTablePanel as _TreeTablePanel,\n TreeTablePanelCallable as TreeTablePanel\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { Link } from \"~/component/input/Link.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction options for {@link LinkCellRenderer}.\n *\n * @category Components\n */\nexport interface LinkCellRendererOptions {\n /**\n * CSS colour for the link text. Defaults to the theme-tinted colour of\n * [`Link`](/api/component/input/classes/Link) itself, so leaving this unset\n * keeps every link cell in step with every standalone link.\n */\n color ?: string;\n}\n\n/**\n * A display-only cell renderer that presents its string value as a link.\n * Built for the\n * [`ColumnConfig.renderer`](/api/component/table/interfaces/ColumnConfig#renderer)\n * seam so a column can present a clickable value without a custom renderer\n * subclass.\n *\n * Composes a [`Link`](/api/component/input/classes/Link) in its presentational\n * (`interactive: false`) mode, so a link cell looks exactly like a standalone\n * link but claims no `role`, takes no tab focus, and does no keyboard\n * handling — all of which belong to the enclosing {@link Table}.\n *\n * The renderer is purely presentational: it does not handle the click itself.\n * Pair the column with the {@link Table} `\"cellclick\"` event and act on the\n * matching `field` to open / navigate. Because a custom-rendered cell carries no\n * editor, the cell never enters edit mode, so the click is always a plain click.\n *\n * @example\n * ```typescript\n * new Table(store, {\n * columns: [{ field: 'refTable', renderer: () => new LinkCellRenderer() }],\n * });\n * table.on('cellclick', e => {\n * if (e.field === 'refTable') open(String(e.value));\n * });\n * ```\n *\n * @category Components\n */\nclass LinkCellRenderer extends CellRenderer<String | null> {\n\n private readonly _text: Text;\n private _value: String | null = null;\n\n constructor(options?: LinkCellRendererOptions) {\n super();\n\n this._text = new Link(\"\", {\n // The Table owns keyboard nav (RovingTabIndex) and routes clicks\n // through its own \"cellclick\", so the link must neither take tab\n // focus nor wire its own keyboard handling.\n interactive: false,\n // An unset `color` is gated out by applyOptions, so it falls\n // through to Link's own class default rather than being pinned here.\n foregroundColor: options?.color,\n });\n // Renderer Texts opt out of auto-measure — the host cell force-sizes them.\n this._text.setAutoMeasure(false);\n this.addComponent(this._text);\n }\n\n /**\n * Returns the underlying {@link Text} used to display the link label.\n *\n * @returns The link's {@link Text} component.\n */\n getText(): Text {\n return this._text;\n }\n\n /**\n * Returns the cached value, or `null` when the cell is empty.\n *\n * @returns The displayed value, or `null`.\n */\n getValue(): String | null {\n return this._value;\n }\n\n /**\n * Caches the value and renders it as link text. `null` and `undefined`\n * both normalise to `null` and render as the empty string.\n *\n * @param value - The value to display, or `null`/`undefined` to clear.\n */\n setValue(value: String | null): void {\n this._value = value ?? null;\n this._text.setText(this._value === null ? \"\" : String(this._value));\n }\n}\n\nconst LinkCellRendererCallable = callable(LinkCellRenderer);\ntype LinkCellRendererCallable = LinkCellRenderer;\nexport {\n LinkCellRenderer as _LinkCellRenderer,\n LinkCellRendererCallable as LinkCellRenderer\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"table.es.js","names":[],"sources":["../../../src/typescript/lib/layout/Table.ts","../../../src/typescript/lib/component/table/cell/editor/CellEditor.ts","../../../src/typescript/lib/component/table/cell/Cell.ts","../../../src/typescript/lib/component/table/cell/editor/Boolean.ts","../../../src/typescript/lib/component/table/cell/renderer/CellRenderer.ts","../../../src/typescript/lib/component/table/cell/renderer/String.ts","../../../src/typescript/lib/component/table/cell/renderer/Number.ts","../../../src/typescript/lib/component/table/cell/renderer/Date.ts","../../../src/typescript/lib/component/table/cell/renderer/Time.ts","../../../src/typescript/lib/component/table/cell/renderer/DateTime.ts","../../../src/typescript/lib/component/table/cell/renderer/Glyph.ts","../../../src/typescript/lib/component/table/ColumnConfig.ts","../../../src/typescript/lib/component/table/cell/renderer/Combo.ts","../../../src/typescript/lib/component/table/cell/Dynamic.ts","../../../src/typescript/lib/component/table/cell/Default.ts","../../../src/typescript/lib/component/table/cell/String.ts","../../../src/typescript/lib/component/table/cell/Boolean.ts","../../../src/typescript/lib/component/table/cell/Number.ts","../../../src/typescript/lib/component/table/cell/Date.ts","../../../src/typescript/lib/component/table/cell/Time.ts","../../../src/typescript/lib/component/table/cell/DateTime.ts","../../../src/typescript/lib/component/table/cell/Glyph.ts","../../../src/typescript/lib/component/table/cell/Combo.ts","../../../src/typescript/lib/component/table/cell/renderer/TreeCell.ts","../../../src/typescript/lib/component/table/Row.ts","../../../src/typescript/lib/component/table/cell/ResizeHandle.ts","../../../src/typescript/lib/component/table/cell/SortPriorityBadge.ts","../../../src/typescript/lib/component/table/cell/Header.ts","../../../src/typescript/lib/component/table/cell/ParentHeader.ts","../../../src/typescript/lib/component/table/Header.ts","../../../src/typescript/lib/component/table/cell/editor/String.ts","../../../src/typescript/lib/component/table/cell/editor/Number.ts","../../../src/typescript/lib/component/table/cell/editor/TextInputCellEditor.ts","../../../src/typescript/lib/component/table/cell/editor/Date.ts","../../../src/typescript/lib/component/table/cell/editor/Time.ts","../../../src/typescript/lib/component/table/cell/editor/DateTime.ts","../../../src/typescript/lib/component/table/cell/editor/CellEditorPool.ts","../../../src/typescript/lib/component/table/cell/editor/Combo.ts","../../../src/typescript/lib/component/table/Body.ts","../../../src/typescript/lib/component/table/Footer.ts","../../../src/typescript/lib/component/table/Column.ts","../../../src/typescript/lib/component/table/TableExporter.ts","../../../src/typescript/lib/component/table/Table.ts","../../../src/typescript/lib/component/table/TablePanel.ts","../../../src/typescript/lib/component/table/TreeBody.ts","../../../src/typescript/lib/component/table/TreeTable.ts","../../../src/typescript/lib/component/table/TreeTablePanel.ts","../../../src/typescript/lib/component/table/cell/renderer/Link.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { LayoutManager, LayoutManagerOptions } from \"~/layout/LayoutManager.js\";\nimport { Table as TableComponent } from \"~/component/table/Table.js\";\nimport { Column } from \"~/component/table/Column.js\";\nimport { Component } from \"~/core/Component.js\";\nimport { Util } from \"~/core/Util.js\";\nimport { UNBOUNDED } from \"~/primitive/Size.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport { ThemeManager } from \"~/core/Theme.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for the {@link Table} layout manager.\n *\n * @category Layouts\n */\ninterface TableLayoutOptions extends LayoutManagerOptions {\n}\n\nconst BOOLEAN_WIDTH = 60;\nconst NUMBER_WIDTH = 90;\nconst DATE_WIDTH = 110;\nconst CHAR_WIDTH = 8;\nconst HEADER_PAD = 16;\n\n/**\n * A layout manager dedicated to the [`Table`](/api/component/table/classes/Table) component.\n * Positions the header, body, and footer sections within the container and\n * triggers virtual-scroll rendering on the body after each layout pass.\n *\n * Per-column widths are stored on the [`Table`](/api/component/table/classes/Table) component. On first render (or after\n * a model swap) widths are initialized based on field type and any `minWidth` /\n * `maxWidth` declared in the column spec: compact types (`boolean`, `number`, `date`)\n * receive a fixed base width floored at the header text width; string and auto columns\n * share the remaining space equally, clamped to their constraints. On container resize\n * compact-type columns keep their width unchanged; only flexible columns scale\n * proportionally, again clamped to their per-column constraints.\n */\nclass Table extends LayoutManager {\n\n /**\n * Constructs a Table layout manager.\n *\n * @param options - Optional construction-time options.\n */\n constructor(options?: TableLayoutOptions) {\n // LayoutManager's constructor takes no options; applied via applyOptions below.\n // eslint-disable-next-line local/forward-super-options\n super();\n\n if (options) {\n this.applyOptions(options);\n }\n }\n\n /**\n * Attaches to a container, throwing if it is not a [`Table`](/api/component/table/classes/Table)\n * or a [`TreeTable`](/api/component/table/classes/TreeTable) component.\n *\n * @param container - The container component to attach to.\n *\n * @remarks This layout manager is valid for containers whose class\n * name is `\"Table\"` or `\"TreeTable\"`. `TreeTable` extends `Table`\n * and reuses the entire header / body / footer geometry pipeline,\n * so it shares the same layout manager.\n */\n attach(container: Component) : this {\n const name = container.getClassName();\n\n if (name != \"Table\" && name != \"TreeTable\") {\n throw new Error(\"Container must be a Table or TreeTable.\");\n }\n\n super.attach(container);\n\n return this;\n }\n\n /**\n * Positions the header, body, and footer sections and triggers body virtual scroll rendering.\n *\n * @remarks Per-column widths are read from the Table component. If the stored widths do not\n * match the current column count (first render or model swap) they are re-initialized using\n * type-aware sizing clamped to each column's `minWidth` / `maxWidth`. On a container resize\n * the existing widths are scaled proportionally, again clamped to per-column constraints.\n */\n doLayout() {\n const container = <TableComponent>this.getContainer();\n\n if (!container) {\n return;\n }\n\n const containerSize = container.getInnerSize();\n\n if (!containerSize) {\n return;\n }\n\n const containerInsets = container.getContentInsets();\n const columns = container.getColumns();\n const columnCount = container.getHeader().getColumns().length;\n const availableWidth = containerSize.width - DOM.source.getScrollBarWidth();\n\n let columnWidths = container.getColumnWidths();\n\n if (columnWidths.length !== columnCount) {\n columnWidths = this.initializeWidths(columns, availableWidth);\n container.setColumnWidths(columnWidths);\n } else {\n columnWidths = this.rescaleWidths(columns, columnWidths, availableWidth);\n container.setColumnWidths(columnWidths);\n }\n\n const header = container.getHeader();\n const body = container.getBody();\n const footer = container.getFooter();\n\n // A section is laid out only when it is both visible (the Table's own\n // header/body/footer visibility flag) AND displayed (the core\n // `setDisplayed` flag). The two are reconciled, not substituted: a\n // `setDisplayed(false)` part drops out of the layout exactly like a\n // `setHeaderVisible(false)` one, and the body reflows to reclaim its band.\n if (container.isHeaderVisible() && header && header.isDisplayed()) {\n // Header cells render their text in the shared px line box, so the\n // row height is that line box plus the cell padding.\n const theme = ThemeManager.getTheme();\n // Cells render at the root font size, so the line box is the\n // additive `font-size + --ts-ui-line-padding` value `Util` derives\n // (the same line box the cell text is laid out at).\n const lineHeight = Util.lineHeightPx();\n const padding = theme.table.cell.padding ?? 2;\n const columnHeight = lineHeight + 2 * padding;\n\n // Parent header row uses the same arithmetic — same font, same\n // padding — so a theme swap re-runs `doLayout` and the two\n // rows stay aligned. Collapses to zero when no visible\n // column declares a `group`, so no-group tables are\n // byte-identical at runtime.\n const hasParentRow = header.hasParentRow();\n const parentRowHeight = hasParentRow ? columnHeight : 0;\n const headerBandHeight = parentRowHeight + columnHeight;\n\n // The header element stays pinned to the viewport width so the\n // gradient (and the scrollbar-cover band) covers the full\n // band, but the inner rows are sized to fit all cells —\n // including ones that overflow the viewport horizontally —\n // because `Row`'s default `overflow: hidden` would otherwise\n // clip cells at the row's right edge and prevent them from\n // coming into view when the inner rows translate left during\n // a horizontal scroll.\n const columnSum = columnWidths.reduce((s, w) => s + w, 0);\n const innerRowW = Math.max(containerSize.width, columnSum);\n\n header.setAutoCommitStyle(false);\n header.setX(containerInsets.getLeft());\n header.setY(containerInsets.getTop());\n header.setWidth(containerSize.width);\n header.setHeight(headerBandHeight);\n header.setAutoCommitStyle(true);\n\n // Parent row is sized + positioned first so its `spanFrom`\n // / `spanTo` constraints translate to x/width sums over the\n // column-width array beneath. When `hasParentRow` is false\n // the parent row stays collapsed at zero height and we\n // skip the per-cell layout pass entirely — no visible\n // column declares a group, so the parent-cell pool is\n // empty anyway.\n const parentRow = header.getParentRow();\n parentRow.setAutoCommitStyle(false);\n parentRow.setX(0);\n parentRow.setY(0);\n parentRow.setWidth(innerRowW);\n parentRow.setHeight(parentRowHeight);\n parentRow.setAutoCommitStyle(true);\n\n if (hasParentRow) {\n const parentCells = parentRow.getComponents();\n\n parentCells.forEach(cell => {\n const lc = parentRow.getLayoutConstraints(cell);\n const span = lc?.data as { spanFrom: number, spanTo: number } | undefined;\n const spanFrom = span?.spanFrom ?? 0;\n const spanTo = span?.spanTo ?? 0;\n\n let cellX = 0;\n for (let i = 0; i < spanFrom; i++) {\n cellX += columnWidths[i];\n }\n\n let cellW = 0;\n for (let i = spanFrom; i <= spanTo; i++) {\n cellW += columnWidths[i];\n }\n\n cell.setAutoCommitStyle(false);\n cell.setX(cellX);\n cell.setY(0);\n cell.setWidth(cellW);\n cell.setHeight(parentRowHeight);\n cell.setAutoCommitStyle(true);\n cell.doLayout();\n });\n }\n\n // Column row sits beneath the parent row. The cell y-coords\n // are relative to the column row's element, so they stay at\n // y=0 — only the row itself shifts down by `parentRowHeight`.\n const columnRow = header.getComponents()[1];\n columnRow.setAutoCommitStyle(false);\n columnRow.setX(0);\n columnRow.setY(parentRowHeight);\n columnRow.setWidth(innerRowW);\n columnRow.setHeight(columnHeight);\n columnRow.setAutoCommitStyle(true);\n\n const headerColumns = header.getColumns();\n let x = 0;\n\n headerColumns.forEach((col, i) => {\n col.setAutoCommitStyle(false);\n col.setX(x);\n col.setY(0);\n col.setWidth(columnWidths[i]);\n col.setHeight(columnHeight);\n col.setAutoCommitStyle(true);\n col.doLayout();\n\n x += columnWidths[i];\n });\n\n // Cover the vertical-scrollbar reservation at the header's\n // right edge so cells scrolled horizontally appear to clip at\n // the trackW boundary while the band stays continuous with the\n // rest of the header's gradient. Sits on top of the inner rows\n // by DOM order, beneath the scrollbar widget which lives in\n // the body.\n const trackW = DOM.source.getScrollBarWidth();\n const cover = header.getScrollbarCover();\n DOM.sink.apply(cover, {\n style: {\n left: (containerSize.width - trackW) + \"px\",\n width: trackW + \"px\",\n height: headerBandHeight + \"px\",\n },\n });\n }\n\n if (container.isFooterVisible() && footer && footer.isDisplayed()) {\n // Footer cells render their text in the shared px line box, so the\n // row height is that line box plus the cell padding.\n const theme = ThemeManager.getTheme();\n // Same additive line box as the header/body: the root font size plus\n // the `--ts-ui-line-padding` leading, via `Util`.\n const lineHeight = Util.lineHeightPx();\n const padding = theme.table.cell.padding ?? 2;\n const columnHeight = lineHeight + 2 * padding;\n const footerColumns = footer.getColumns();\n\n footer.setAutoCommitStyle(false);\n footer.setX(containerInsets.getLeft());\n footer.setY(containerInsets.getTop() + containerSize.height - columnHeight);\n footer.setWidth(containerSize.width);\n footer.setHeight(columnHeight);\n footer.setAutoCommitStyle(true);\n\n let x = 0;\n\n footerColumns.forEach((col, i) => {\n col.setAutoCommitStyle(false);\n col.setX(x);\n col.setY(0);\n col.setWidth(columnWidths[i]);\n col.setHeight(columnHeight);\n col.setAutoCommitStyle(true);\n col.doLayout();\n\n x += columnWidths[i];\n });\n }\n\n if (container.isBodyVisible() && body && body.isDisplayed()) {\n const headerHeight = container.isHeaderVisible() && header && header.isDisplayed() ? header.getHeight() : 0;\n const footerHeight = container.isFooterVisible() && footer && footer.isDisplayed() ? footer.getHeight() : 0;\n\n body.setAutoCommitStyle(false);\n body.setX(containerInsets.getLeft());\n body.setY(containerInsets.getTop() + headerHeight);\n body.setWidth(containerSize.width);\n body.setHeight(containerSize.height - headerHeight - footerHeight);\n body.setAutoCommitStyle(true);\n\n body.renderWindow(availableWidth, columnWidths);\n }\n }\n\n /**\n * Computes initial column widths from field types and column constraints.\n *\n * Fixed-type columns (boolean, number, date) get a type-based base width floored\n * at their header text width and clamped to any declared min/max. Flexible columns\n * (string, auto) share the remaining space equally, each clamped to its own min/max.\n *\n * @param columns - The visible resolved columns.\n * @param availableWidth - Total available pixel width for columns.\n * @returns The computed width for each column.\n */\n private initializeWidths(columns: Column[], availableWidth: number): number[] {\n const intrinsic: (number | null)[] = columns.map(col => {\n const f = col.getField();\n const headerMin = f.getDescription().length * CHAR_WIDTH + HEADER_PAD;\n\n switch (f.getType()) {\n case 'boolean': return this.clamp(Math.max(BOOLEAN_WIDTH, headerMin), col);\n case 'number': return this.clamp(Math.max(NUMBER_WIDTH, headerMin), col);\n case 'date': return this.clamp(Math.max(DATE_WIDTH, headerMin), col);\n default: return null;\n }\n });\n\n const fixedTotal = intrinsic.reduce((s: number, w) => s + (w ?? 0), 0);\n const flexCount = intrinsic.filter(w => w === null).length;\n const rawFlex = flexCount > 0\n ? (availableWidth - fixedTotal) / flexCount\n : 0;\n\n return intrinsic.map((w, i) => {\n if (w !== null) {\n return w;\n }\n\n const col = columns[i];\n const min = col.getMinWidth() ?? 30;\n const max = col.getMaxWidth() ?? UNBOUNDED;\n\n return Util.clamp(rawFlex, min, max);\n });\n }\n\n /**\n * Rescales existing flexible column widths to fit the new available width,\n * keeping fixed-type columns at their current size and clamping all columns\n * to their per-column constraints.\n *\n * @param columns - The visible resolved columns.\n * @param columnWidths - The existing width array from the previous layout.\n * @param availableWidth - Total available pixel width for columns.\n * @returns The updated width array, or the original if no rescaling was needed.\n */\n private rescaleWidths(columns: Column[], columnWidths: number[], availableWidth: number): number[] {\n const isFixed = columns.map(col => {\n const t = col.getField().getType();\n\n return t === 'boolean' || t === 'number' || t === 'date';\n });\n\n const fixedTotal = columnWidths.reduce((s: number, w, i) => s + (isFixed[i] ? w : 0), 0);\n const prevFlexTotal = columnWidths.reduce((s: number, w, i) => s + (isFixed[i] ? 0 : w), 0);\n const newFlexTotal = availableWidth - fixedTotal;\n\n if (prevFlexTotal <= 0 || Math.abs(prevFlexTotal - newFlexTotal) <= 0.5) {\n return columnWidths;\n }\n\n const ratio = newFlexTotal / prevFlexTotal;\n\n return columnWidths.map((w, i) => {\n if (isFixed[i]) {\n return w;\n }\n\n return this.clamp(w * ratio, columns[i]);\n });\n }\n\n /**\n * Clamps a width value to the `[minWidth, maxWidth]` range declared on a column,\n * using 30 px as the default minimum and no upper bound when unconstrained.\n *\n * @param width - The raw width to clamp.\n * @param column - The column whose constraints apply.\n * @returns The clamped width.\n */\n private clamp(width: number, column: Column): number {\n const min = column.getMinWidth() ?? 30;\n const max = column.getMaxWidth() ?? UNBOUNDED;\n\n return Util.clamp(width, min, max);\n }\n}\n\nconst TableCallable = callable(Table);\ntype TableCallable = Table;\nexport {\n Table as _Table,\n TableCallable as Table\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { Fit } from \"~/layout/Fit.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { ThemeManager } from \"~/core/Theme.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\n\n/**\n * Interns the `relatedTarget` of a blur event into a {@link Handle}, or returns\n * `null` when no node is receiving focus.\n *\n * A native `blur` carries `relatedTarget: Node | null`, but a cell editor that\n * re-fires its inner field's blur as a synthetic `CustomEvent(\"blur\")` (e.g.\n * {@link StringEditor}, {@link NumberEditor}) has no `relatedTarget` at all —\n * the property reads back `undefined`. Feeding that to\n * [`DOM.source.intern`](/api/core/namespaces/DOM/interfaces/Source#intern)\n * throws (`new WeakRef(undefined)` is a `TypeError`), which would abort the\n * blur listener *before* it commits the edit — leaving the cell stuck in edit\n * mode and the pooled editor un-released, so the whole column can no longer be\n * edited. Treating both `null` and `undefined` as \"focus left the editor\n * entirely\" keeps the commit path alive for the synthetic-blur editors while\n * preserving the real-node answer the {@link CellEditor.retainsFocus} overrides\n * depend on.\n *\n * @param e - The blur event, native or synthetically re-fired.\n * @returns The interned handle of the focus target, or `null`.\n *\n * @category Components\n */\nexport function blurRelatedTargetHandle(e: FocusEvent): Handle | null {\n return e.relatedTarget ? DOM.source.intern(e.relatedTarget) : null;\n}\n\n/**\n * The `detail` payload an editor forwards on its re-fired `\"keydown\"` custom\n * event. The editor wraps its inner field's native keydown so the parent cell's\n * commit/cancel logic can read `keyCode` (and the modifier flags) without the\n * editor naming the global `KeyboardEvent` constructor.\n *\n * @category Components\n */\nexport interface ForwardedKeyDetail {\n key: string;\n code: string;\n keyCode: number;\n shiftKey: boolean;\n ctrlKey: boolean;\n altKey: boolean;\n metaKey: boolean;\n}\n\n/**\n * Abstract base class for cell editors.\n *\n * Subclasses allow in-place editing of a typed value inside a table cell using a\n * Fit layout with theme-driven padding.\n *\n * @category Components\n */\nexport abstract class CellEditor<T> extends Component {\n\n /**\n * Installed by {@link CellEditorPool}: lets an editor that manages its own\n * dismissal (e.g. one that pops a floating picker dropdown) ask the active\n * cell to commit the edit. Null until wired.\n */\n private _onCommitRequested: (() => void) | null = null;\n\n constructor(tag: string = \"div\") {\n super({ tag });\n\n this.setLayoutManager(new Fit());\n\n // Inlined (rather than a private method) so CellRenderer and CellEditor stay\n // structurally compatible — BooleanCell relies on a CellEditor doubling as the\n // renderer, which fails if both classes declare a private member of the same name.\n const applyPadding = () => {\n const p = ThemeManager.getTheme().table.cell.padding;\n this.setInsets(new Insets(0, p, 0, p));\n };\n\n applyPadding();\n\n this.subscribeTheme(applyPadding);\n }\n\n /**\n * Returns the current editor value.\n *\n * @returns The edited value of type T.\n */\n abstract getValue(): T;\n\n /**\n * Sets the editor to an initial value before editing begins.\n *\n * @param t - The value to populate the editor with.\n */\n abstract setValue(t: T): void;\n\n /**\n * Returns whether the edit should stay alive when the editor's `<input>`\n * loses focus to `relatedTarget`. The default is `false` (any blur ends the\n * edit). Editors that own a floating sub-surface (e.g. a picker dropdown\n * with its own focusable field) override this to keep editing while focus\n * moves into that surface.\n *\n * @remarks Overrides delegate the cross-portal answer (\"did focus land\n * inside my dropdown or any descendant layer?\") to\n * [`LayerManager.containsAcrossLayers`](/api/core/namespaces/LayerManager/functions/containsAcrossLayers)\n * rather than walking a private layer stack themselves.\n *\n * @param _relatedTarget - The node handle receiving focus, or null.\n * @returns True to suppress the blur-driven commit/close.\n */\n retainsFocus(_relatedTarget: Handle | null): boolean {\n return false;\n }\n\n /**\n * Installs the handler the editor calls to ask the active cell to commit.\n * Wired once by {@link CellEditorPool}.\n *\n * @param handler - Callback that commits the active cell's edit.\n */\n setCommitRequestHandler(handler: () => void): void {\n this._onCommitRequested = handler;\n }\n\n /**\n * Asks the active cell to commit the edit. Used by editors that detect the\n * end of an edit themselves (e.g. an outside click while focus sits in a\n * popped picker rather than the editor's own input).\n */\n protected requestCommit(): void {\n this._onCommitRequested?.();\n }\n\n /**\n * Mirrors {@link CellRenderer.getContentX} so a `CellEditor` and a\n * `CellRenderer` stay structurally compatible — {@link BooleanCell}\n * uses a [`BooleanEditor`](/api/component/table/classes/BooleanEditor)\n * as both renderer and editor, and TS would reject that pass if\n * either base class declared a member the other didn't. Default\n * returns `0`; editors do not currently report indent offsets of\n * their own.\n *\n * @returns Always `0`.\n */\n getContentX(): number {\n return 0;\n }\n}","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { Card } from \"~/layout/Card.js\";\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { CellEditor, blurRelatedTargetHandle } from \"~/component/table/cell/editor/CellEditor.js\";\nimport type { ForwardedKeyDetail } from \"~/component/table/cell/editor/CellEditor.js\";\nimport { CellEditorPool } from \"~/component/table/cell/editor/CellEditorPool.js\";\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\n\n/**\n * String-literal union of the events emitted by {@link Cell}.\n *\n * @category Components\n */\nexport type CellEvent = \"commit\" | \"editend\";\n\n/**\n * Base class for table cells that support both a display renderer and an optional in-place editor.\n *\n * A {@link Card} layout toggles between the renderer and editor views. Double-clicking the\n * renderer starts an edit; blur or Enter commits it; Escape cancels it.\n *\n * Subclasses ({@link BooleanCell}, {@link NumberCell}, {@link StringCell}, {@link HeaderCell})\n * wire a typed renderer and editor pair. Custom cell types extend this class with your own\n * renderer / editor classes.\n *\n * @category Components\n */\nexport class Cell<T> extends Component {\n\n private _readOnly: boolean;\n private _requiredEmpty: boolean = false;\n private _baseBackground: string = 'var(--ts-ui-table-cell-bg, transparent)';\n private _renderer: CellRenderer<T>;\n private _editor: CellEditor<T> | undefined;\n private _editorPool: CellEditorPool | null = null;\n private _scrollIntoView: (() => void) | null = null;\n protected _activeEditor: CellEditor<T> | null = null;\n // Typed as `ListenerBag<string>` rather than `ListenerBag<CellEvent>` so\n // subclasses (e.g. HeaderCell) can re-declare the bag with a wider\n // event union without fighting TypeScript's invariant generic on\n // `ListenerBag.add`. The compile-time gate on event names lives on the\n // host's typed `on` / `off` / `emit` overloads, not on the bag field.\n protected _listeners: ListenerBag<string> = new ListenerBag<string>();\n\n constructor(tag: string, renderer: CellRenderer<T>, editor?: CellEditor<T>, rendererConstraints?: LayoutConstraints, editorContraints?: LayoutConstraints) {\n super({ tag: tag || \"td\" });\n\n this.setLayoutManager(new Card());\n\n this.getAria().setRole(\"gridcell\");\n\n this._readOnly = false;\n this._renderer = renderer;\n this._editor = editor;\n this.setInsets(new Insets(0, 0, 0, 0));\n\n this.setBackgroundColor('var(--ts-ui-table-cell-bg, transparent)');\n this.setForegroundColor('var(--ts-ui-table-cell-color, inherit)');\n this.setBorder('var(--ts-ui-table-cell-border, none)');\n\n this.subscribeTheme(() => this.setBorder('var(--ts-ui-table-cell-border, none)'));\n\n this.addComponent(renderer, rendererConstraints);\n\n if (editor) {\n this.addComponent(editor, editorContraints);\n\n editor.setCommitRequestHandler(() => this.commitEdit());\n\n // Internal cell-editor wiring: listens on a privately-owned child;\n // see the cell-editor carve-out in ARCHITECTURE.md.\n Event.addListener(editor, 'blur', (e: FocusEvent) => {\n if (editor.retainsFocus(blurRelatedTargetHandle(e))) {\n return;\n }\n\n this.commitEdit();\n });\n Event.addListener(editor, 'keydown', (e: CustomEvent<ForwardedKeyDetail>) => this.onKeyDown(e));\n }\n\n // Internal cell-editor wiring: listens on a privately-owned child;\n // see the cell-editor carve-out in ARCHITECTURE.md.\n Event.addListener(renderer, 'dblclick', () => this.startEdit());\n }\n\n /**\n * Opts out of content-size clamping: a cell fits the geometry its host\n * {@link Body} force-assigns (column width × row height), exactly like a\n * {@link Container} fits its parent's allocation. Without this, a cell\n * whose visible child carries a hard maximum — e.g. {@link BooleanCell}'s\n * 16×16 checkbox — would clamp {@link Component.setHeight} down to that\n * child max and sit shorter than the row, pinning the checkbox to the top\n * instead of letting the cell's anchored layout centre it. Only the cell's\n * own explicit min/max remain hard limits.\n *\n * @returns `false` — clamp to explicit constraints only, never to the\n * content-derived size.\n */\n protected clampsToContentSize(): boolean {\n return false;\n }\n\n /**\n * Returns the pool-key that selects which shared editor this cell should borrow on edit.\n *\n * @returns A string key registered on the {@link CellEditorPool}, or `null` to opt out of\n * the shared-editor mechanism.\n *\n * @remarks The default implementation returns `null` so user-authored cells that pass an\n * editor through the constructor (the legacy path) keep working unchanged. Built-in typed\n * cells override this to return their variant key (e.g. `\"string\"`, `\"time:seconds\"`).\n */\n getEditorKey(): string | null {\n return null;\n }\n\n /**\n * Attaches this cell to a {@link CellEditorPool} so that {@link Cell.startEdit} can borrow\n * a shared editor instead of allocating one.\n *\n * @param pool - The pool to borrow from, or `null` to disable shared-editor mode.\n * @returns This cell, for method chaining.\n */\n setEditorPool(pool: CellEditorPool | null): this {\n this._editorPool = pool;\n\n return this;\n }\n\n /**\n * Installs the callback {@link Cell.startEdit} runs before opening the\n * editor, used by the host {@link Body} to scroll this cell's column into\n * view through its scroll model. Running it first means the editor — and\n * any picker dropdown it anchors — opens at the cell's final position\n * rather than its pre-scroll one.\n *\n * @param handler - The scroll-into-view callback, or `null` to clear it.\n * @returns This cell, for method chaining.\n */\n setScrollIntoViewHandler(handler: (() => void) | null): this {\n this._scrollIntoView = handler;\n\n return this;\n }\n\n /**\n * Registers a listener for one of this cell's events.\n *\n * @param event - `\"commit\"` fires with the committed value whenever an\n * edit is committed; `\"editend\"` fires after the cell returns to\n * renderer view via keyboard action (Enter / Escape), not on blur.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This cell, for method chaining.\n */\n on(event: \"commit\", listener: (value: T) => void): this;\n on(event: \"editend\", listener: () => void): this;\n on(event: CellEvent, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered listener. The exact callback reference\n * must match.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This cell, for method chaining.\n */\n off(event: CellEvent, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every listener registered for `event` with `payload`, in\n * registration order.\n *\n * @param event - The event to emit.\n * @param payload - Forwarded to each listener.\n */\n protected emit(event: \"commit\", value: T): void;\n protected emit(event: \"editend\"): void;\n protected emit(event: CellEvent, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Returns true if the cell cannot be edited.\n *\n * @returns True if the cell is read-only.\n */\n isReadOnly() {\n return !!this._readOnly;\n }\n\n /**\n * Sets whether this cell is read-only. Read-only cells refuse\n * {@link Cell.startEdit}, render with the\n * `--ts-ui-table-cell-readonly-bg` tint, and present a default\n * cursor instead of any renderer-supplied edit affordance.\n * Idempotent — passing the current value short-circuits before any\n * style writes.\n *\n * Body rows call this from their cell-construction loop based on\n * the column's `ColumnConfig.readOnly` flag, and the body's\n * per-rebind read-only resolution forwards the OR of the static\n * column flag, the spec-level row predicate, and the per-cell\n * predicate. Application code should declare read-only through\n * those config-level surfaces rather than calling this setter\n * directly on a cell.\n *\n * Calling `setReadOnly(true)` while the cell is in edit mode\n * silently commits the active edit before flipping the flag, so the\n * borrowed editor releases back to the {@link CellEditorPool}\n * cleanly and the in-progress user input lands on the record. The\n * commit fires the cell's `onCommit` callback (and any cascading\n * store-event refresh) as if the user had blurred the editor.\n *\n * Read-only wins over the required-empty outline set via\n * {@link Cell.setRequiredEmpty} — a read-only cell cannot be\n * filled, so ringing it as required would be misleading.\n *\n * @param value - `true` to mark read-only, `false` to restore the\n * default editable appearance.\n * @returns This cell, for method chaining.\n */\n setReadOnly(value: boolean): this {\n if (this._readOnly === value) {\n return this;\n }\n\n // Flip the flag BEFORE running the mid-edit commit. The\n // commit's onCommit callback synchronously calls\n // `store.notifyRecordChanged`, which cascades back into\n // `Body.applyReadOnlyState` and re-invokes this same setter on\n // this same cell. Flipping first lets the re-entrant call\n // short-circuit on the idempotence guard above; otherwise it\n // would observe `_readOnly === false`, see `isEditing() ===\n // true`, and recurse into `commitEdit` indefinitely.\n // `commitEdit` itself checks `isReadOnly()` and would\n // short-circuit now that the flag is set, so we inline its\n // value/renderer/onCommit/detach sequence here instead.\n this._readOnly = value;\n\n if (value && this._activeEditor) {\n const committedValue = this._activeEditor.getValue();\n\n this._renderer.setValue(committedValue);\n this.emit(\"commit\", committedValue as T);\n this.detachEditor();\n }\n\n this._applyStateTint();\n\n return this;\n }\n\n /**\n * Sets whether this cell shows the required-empty outline — a visual\n * cue that a required column's cell currently holds an empty value.\n * Sourced from `--ts-ui-table-cell-required-outline`. Idempotent —\n * passing the current value short-circuits before any style writes.\n *\n * Body rows call this from their per-rebind resolution based on the\n * column's `ColumnConfig.required` flag and `requiredPredicate`.\n * Read-only wins over this outline — see {@link Cell.setReadOnly}'s\n * precedence note.\n *\n * @param value - `true` to show the required-empty outline, `false`\n * to hide it.\n * @returns This cell, for method chaining.\n */\n setRequiredEmpty(value: boolean): this {\n if (this._requiredEmpty === value) {\n return this;\n }\n\n this._requiredEmpty = value;\n this._applyStateTint();\n\n return this;\n }\n\n /**\n * Sets the background this cell falls back to when not read-only —\n * e.g. a column's `groupColor` tint. {@link Row} routes its\n * group-color write through this setter (instead of\n * `setBackgroundColor` directly) so a filled cell in a grouped,\n * required column restores its group tint rather than going\n * transparent (the required-empty outline is a separate overlay\n * and does not affect the background).\n *\n * @param color - The CSS color string to use as the base background.\n * @returns This cell, for method chaining.\n */\n setBaseBackground(color: string): this {\n this._baseBackground = color;\n this._applyStateTint();\n\n return this;\n }\n\n /**\n * Resolves this cell's background + cursor from precedence\n * read-only ▸ base background, and separately resolves the\n * required-empty outline (shown only when required-empty and NOT\n * read-only). The single owner of both so the read-only and\n * required-empty states cannot fight over either.\n */\n private _applyStateTint(): void {\n if (this._readOnly) {\n this.setBackgroundColor('var(--ts-ui-table-cell-readonly-bg, rgba(0, 0, 0, 0.04))');\n this.setCursor('default');\n } else {\n this.setBackgroundColor(this._baseBackground);\n this.clearCursor();\n }\n\n // Read-only wins: a read-only cell cannot be filled, so ringing it\n // as required would be misleading. Uses an inset box-shadow (the\n // same \"border via shadow\" idiom the cell editors use for their own\n // focus border) rather than the CSS `outline` property, which\n // `Body._updateFocusStyle` already owns for the keyboard-focus ring.\n if (this._requiredEmpty && !this._readOnly) {\n this.setShadow('inset 0 0 0 1px var(--ts-ui-table-cell-required-outline, rgba(220, 60, 60, 0.6))');\n } else {\n this.clearShadow();\n }\n }\n\n /**\n * Commits on Enter and cancels on Escape while editing, then fires `_onEditEnd` to return focus to the container.\n *\n * @param evnt - The forwarded keydown event. The editor re-fires its inner\n * field's keydown as a custom event whose `detail` carries `keyCode` and\n * the modifier flags, so this reads `detail` rather than the native\n * top-level properties.\n */\n onKeyDown(evnt: CustomEvent<ForwardedKeyDetail>): void {\n const keyCode = evnt.detail?.keyCode;\n\n if (keyCode == 13) { // Enter\n this.commitEdit();\n this.emit(\"editend\");\n } else if (keyCode == 27) { // Escape\n this.cancelEdit();\n this.emit(\"editend\");\n }\n }\n\n /**\n * Returns true if an editor is currently mounted on this cell.\n *\n * @returns True if the cell is in edit mode.\n */\n isEditing(): boolean {\n return this._activeEditor !== null;\n }\n\n /**\n * Returns the Card layout manager used to toggle between renderer and editor.\n *\n * @returns The {@link Card} layout manager for this cell.\n */\n getLayoutManager() {\n return <Card>super.getLayoutManager();\n }\n\n /**\n * Switches to the editor view, copies the renderer's value, and focuses the editor.\n *\n * @remarks When the cell was constructed with a per-cell editor (legacy mode), that editor\n * is used. Otherwise the cell asks its {@link CellEditorPool} for the shared editor matching\n * {@link Cell.getEditorKey} and parents it into this cell for the duration of the edit.\n */\n startEdit(): void {\n if (this.isReadOnly() || this.isEditing()) {\n return;\n }\n\n // Scroll this column fully into view before the editor opens, so the\n // editor and any picker dropdown it anchors land at the cell's final\n // position. The editor itself focuses with preventScroll, so the\n // browser never scrolls the body out from under the scroll model.\n this._scrollIntoView?.();\n\n if (this._editor) {\n this._activeEditor = this._editor;\n } else {\n const key = this.getEditorKey();\n if (!key || !this._editorPool) {\n return;\n }\n\n const shared = this._editorPool.acquire(key, this);\n if (!shared) {\n return;\n }\n\n this._activeEditor = shared as CellEditor<T>;\n this.addComponent(this._activeEditor);\n }\n\n const editor = this._activeEditor;\n const renderer = this._renderer;\n\n this.prepareEditor(editor);\n editor.setValue(renderer.getValue());\n\n this.getLayoutManager().setVisibleComponentId(editor.getId());\n this.doLayout();\n // preventScroll — the column was already revealed through the scroll\n // model above; a native focus-scroll would desync the header + scrollbar.\n editor.focus(true);\n }\n\n /**\n * Saves the editor value to the renderer, fires onCommit, and returns to renderer view.\n *\n * @returns This cell, for method chaining.\n */\n commitEdit(): this {\n if (this.isReadOnly() || !this.isEditing()) {\n return this;\n }\n\n const editor = this._activeEditor!;\n const value = editor.getValue();\n\n this._renderer.setValue(value);\n this.emit(\"commit\", value as T);\n\n this.detachEditor();\n\n return this;\n }\n\n /**\n * Discards the editor value and returns to renderer view.\n */\n cancelEdit(): void {\n if (this.isReadOnly() || !this.isEditing()) {\n return;\n }\n\n this.detachEditor();\n }\n\n /**\n * Restores the renderer as the visible card layer and, in shared-editor mode, detaches the\n * borrowed editor from this cell so the pool can lend it to the next cell.\n *\n * @remarks `activeEditor` is cleared **before** the Card swap because hiding the editor\n * pulls focus off its `<input>` and synchronously fires `blur`. The pool's blur listener\n * routes back into `commitEdit`, which would otherwise re-enter here and double-remove the\n * element — clearing the pointer first makes the re-entrant `isEditing()` check short-circuit.\n */\n private detachEditor(): void {\n const editor = this._activeEditor;\n if (!editor) {\n return;\n }\n\n this._activeEditor = null;\n\n this.getLayoutManager().setVisibleComponentId(this._renderer.getId());\n this.doLayout();\n\n if (this._editor !== editor) {\n this.removeComponent(editor);\n this._editorPool?.release();\n }\n }\n\n /**\n * Sets the renderer's displayed value.\n *\n * @param value - The value to pass to the renderer.\n */\n setValue(value: T) : this {\n this._renderer.setValue(value);\n\n return this;\n }\n\n /**\n * Returns the cell's renderer component.\n *\n * @returns The {@link CellRenderer} for this cell.\n */\n getRenderer() {\n return this._renderer;\n }\n\n /**\n * Runs the inherited (Card) layout, then re-indents the active\n * editor so it lines up with the renderer's content offset\n * (`renderer.getContentX()`). For a plain renderer the offset is\n * `0` and this is a no-op; for a\n * [`TreeCellRenderer`](/api/component/table/classes/TreeCellRenderer)\n * the editor shifts right by `depth * indentPx + TOGGLE_WIDTH` so\n * it stays aligned with the value the user double-clicked instead\n * of snapping to the cell's left edge.\n *\n * @returns This cell, for method chaining.\n */\n doLayout(): this {\n super.doLayout();\n\n this.alignEditorWithContent();\n\n return this;\n }\n\n /**\n * If an editor is currently active and the renderer reports a\n * non-zero content offset, override the Card layout's\n * fill-the-cell editor placement so the editor starts at that\n * offset and shrinks its width accordingly. Idempotent — when no\n * offset is reserved this is a single comparison and an early\n * return.\n */\n private alignEditorWithContent(): void {\n const editor = this._activeEditor;\n\n if (!editor) {\n return;\n }\n\n const contentX = this._renderer.getContentX();\n\n if (contentX <= 0) {\n return;\n }\n\n const cellWidth = this.getWidth() ?? 0;\n const editorWidth = Math.max(0, cellWidth - contentX);\n\n editor.setAutoCommitStyle(false);\n editor.setX(contentX);\n editor.setWidth(editorWidth);\n editor.setAutoCommitStyle(true);\n }\n\n /**\n * Replaces the cell's renderer with one returned by `factory`, after\n * passing the existing renderer to the factory so it can be adopted as\n * a delegate. The wrapping renderer becomes this cell's new\n * {@link CellRenderer.getValue} / {@link CellRenderer.setValue} target;\n * the Card layout's renderer-visible state is restored automatically.\n *\n * Used by tree-table rows to install a `TreeCellRenderer` that\n * prepends an indent + expand/collapse toggle in front of the typed\n * renderer appropriate for the column's field type.\n *\n * @param factory - Receives the detached old renderer; returns the\n * new wrapping renderer that must adopt it as a child.\n *\n * @returns This cell, for method chaining.\n */\n wrapRenderer(factory: (delegate: CellRenderer<T>) => CellRenderer<T>): this {\n const oldRenderer = this._renderer;\n\n this.removeComponent(oldRenderer);\n\n const newRenderer = factory(oldRenderer);\n\n this._renderer = newRenderer;\n this.addComponent(newRenderer);\n this.getLayoutManager().setVisibleComponentId(newRenderer.getId());\n\n return this;\n }\n\n /**\n * Returns the cell's editor component, or undefined if the cell is display-only.\n *\n * @returns The {@link CellEditor} for this cell, or undefined.\n */\n getEditor() {\n return this._editor;\n }\n\n /**\n * Swaps which renderer is this cell's active display + commit target.\n *\n * @param renderer - The renderer to make active.\n * @param isNewChild - `true` the first time `renderer` is shown on this\n * cell: it is parented via `addComponent` and wired for double-click\n * activation, mirroring the wiring the constructor does for the\n * initial renderer. `false` for a renderer already parented on an\n * earlier swap.\n */\n protected setActiveRenderer(renderer: CellRenderer<T>, isNewChild: boolean): void {\n if (isNewChild) {\n this.addComponent(renderer);\n\n // Internal cell-editor wiring: listens on a privately-owned child;\n // see the cell-editor carve-out in ARCHITECTURE.md.\n Event.addListener(renderer, 'dblclick', () => this.startEdit());\n }\n\n this._renderer = renderer;\n this.getLayoutManager().setVisibleComponentId(renderer.getId());\n }\n\n /**\n * Hook run immediately before the acquired editor receives the\n * renderer's value in {@link Cell.startEdit}. Default is a no-op.\n *\n * @param _editor - The editor about to be shown, not yet holding a value.\n */\n protected prepareEditor(_editor: CellEditor<T>): void {\n // no-op by default\n }\n}","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellEditor } from \"~/component/table/cell/editor/CellEditor.js\";\nimport { Checkbox } from \"~/component/input/Checkbox.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * String-literal union of the events emitted by {@link BooleanEditor}.\n *\n * @category Components\n */\nexport type BooleanEditorEvent = \"change\";\n\n/**\n * An always-visible checkbox editor for boolean cell values.\n *\n * Used directly as the renderer in {@link BooleanCell}; changes fire the onChange\n * callback immediately without a separate commit step. A record whose\n * boolean field is `null` or `undefined` renders as the checkbox's\n * indeterminate (mixed) state; the first user click follows the\n * Checkbox's WAI-ARIA mixed-state rule and lands at `true`, after\n * which the cell carries a concrete `true`/`false` value.\n *\n * @category Components\n */\nclass BooleanEditor extends CellEditor<Boolean | null> {\n\n private _checkBox: Checkbox = new Checkbox();\n private _value: Boolean | null = null;\n private _listeners: ListenerBag<BooleanEditorEvent> = new ListenerBag<BooleanEditorEvent>();\n private _suppressCommit: boolean = false;\n\n constructor() {\n super();\n\n this._checkBox.setIndeterminate(true);\n // Suppress the 120 ms fill/check transition — a virtualized table\n // can call `setValue` on dozens of pool slots per scroll frame, and\n // each transition would otherwise leave the checkbox visibly\n // animating mid-scroll.\n this._checkBox.setAnimated(false);\n this.addComponent(this._checkBox);\n\n this._checkBox.on(\"action\", () => {\n // `Checkbox.setSelected` dispatches a synthetic\n // `CustomEvent(\"click\")` on the root for backward-compat with\n // `on(\"action\", fn)` consumers, which fires for BOTH real user\n // toggles AND programmatic `setValue` calls. Without the\n // `_suppressCommit` guard, every scroll-driven `setValue` would\n // commit the bound record back to the store, fire\n // `'datachange'`, and re-render both bodies in a loop.\n if (this._suppressCommit) {\n return;\n }\n\n this._value = this._checkBox.isSelected();\n this.emit(\"change\", this._value);\n });\n }\n\n /**\n * Registers a listener for one of this editor's events.\n *\n * @param event - `\"change\"` fires when the checkbox value changes,\n * receiving the concrete `true`/`false` produced by the click. The\n * editor never emits `null` from user interaction.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This editor, for method chaining.\n */\n on(event: \"change\", listener: (value: Boolean | null) => void): this;\n on(event: BooleanEditorEvent, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered listener. The exact callback reference\n * must match.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This editor, for method chaining.\n */\n off(event: BooleanEditorEvent, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every listener registered for `event` with `payload`, in\n * registration order.\n *\n * @param event - The event to emit.\n * @param payload - Forwarded to each listener.\n */\n protected emit(event: \"change\", value: Boolean | null): void;\n protected emit(event: BooleanEditorEvent, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Returns the cached boolean value, or `null` when the checkbox is\n * still in its initial indeterminate state.\n *\n * @returns `true`/`false` once the user (or `setValue`) has set a\n * concrete value, otherwise `null`.\n */\n getValue(): Boolean | null {\n return this._value;\n }\n\n /**\n * Sets the checkbox state. `null` and `undefined` put the checkbox\n * into the indeterminate state and cache the value as `null`;\n * `true`/`false` clear indeterminate and select accordingly.\n *\n * @param value - The boolean value to reflect on the checkbox, or\n * `null`/`undefined` to render the indeterminate state.\n */\n setValue(value: Boolean | null): this {\n this._value = value ?? null;\n\n // Wrap the programmatic state updates in the suppress-commit guard\n // so the synthetic `click` events dispatched by `setIndeterminate`\n // and `setSelected` don't fire the cell's commit callback. The\n // guard is checked by the constructor's action listener.\n this._suppressCommit = true;\n try {\n if (this._value === null) {\n this._checkBox.setIndeterminate(true);\n } else {\n this._checkBox.setIndeterminate(false);\n this._checkBox.setSelected(this._value as boolean);\n }\n } finally {\n this._suppressCommit = false;\n }\n\n return this;\n }\n\n /**\n * Marks the checkbox read-only (or restores it). A read-only checkbox\n * ignores user clicks and keyboard toggles, so a read-only boolean cell\n * stops accepting edits; programmatic {@link setValue} is unaffected.\n *\n * @param value - `true` to make the checkbox read-only, `false` to restore.\n *\n * @returns This editor, for method chaining.\n */\n setReadOnly(value: boolean): this {\n this._checkBox.setReadOnly(value);\n\n return this;\n }\n\n /**\n * Returns whether the checkbox is currently read-only.\n *\n * @returns `true` when the checkbox rejects user interaction.\n */\n isReadOnly(): boolean {\n return this._checkBox.isReadOnly();\n }\n\n /**\n * Toggles the checkbox and fires the onChange callback. Clears the\n * indeterminate state if it was set, so the toggled value is always\n * a concrete boolean.\n */\n toggle(): this {\n const next = !this._checkBox.isSelected();\n\n this._checkBox.setIndeterminate(false);\n this._checkBox.setSelected(next);\n this._value = next;\n this.emit(\"change\", this._value);\n\n return this;\n }\n}\n\nconst BooleanEditorCallable = callable(BooleanEditor);\ntype BooleanEditorCallable = BooleanEditor;\nexport {\n BooleanEditor as _BooleanEditor,\n BooleanEditorCallable as BooleanEditor\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { Fit } from \"~/layout/Fit.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { ThemeManager } from \"~/core/Theme.js\";\n\n/**\n * Abstract base class for cell renderers.\n *\n * Subclasses display a typed value inside a table cell using a Fit layout with theme-driven padding.\n *\n * @category Components\n */\nexport abstract class CellRenderer<T> extends Component {\n\n constructor() {\n super();\n\n this.setLayoutManager(new Fit());\n\n // Inlined (rather than a private method) so CellRenderer and CellEditor stay\n // structurally compatible — BooleanCell relies on a CellEditor doubling as the\n // renderer, which fails if both classes declare a private member of the same name.\n const applyPadding = () => {\n const p = ThemeManager.getTheme().table.cell.padding;\n this.setInsets(new Insets(0, p, 0, p));\n };\n\n applyPadding();\n\n this.subscribeTheme(applyPadding);\n }\n\n /**\n * Returns the currently displayed value.\n *\n * @returns The rendered value of type T.\n */\n abstract getValue(): T;\n\n /**\n * Updates the displayed value.\n *\n * @param t - The value to render.\n */\n abstract setValue(t: T): void;\n\n /**\n * Returns the x offset (in this renderer's local space) at which\n * the cell's actual content begins. Defaults to `0` — the typed\n * renderer fills its box from the left.\n *\n * Overridden by [`TreeCellRenderer`](/api/component/table/classes/TreeCellRenderer)\n * to reserve space for the indent + toggle column. The owning\n * {@link Cell} consults this on every `doLayout` so the editor\n * stays visually aligned with where the renderer's content sat,\n * instead of snapping to the left cell edge on edit.\n *\n * @returns The pixels reserved at the left of the renderer's box\n * before its content begins.\n */\n getContentX(): number {\n return 0;\n }\n\n /**\n * Runs the Fit layout, then synchronises the Text child's `line-height`\n * to its own element height. A single-line `<span>` whose `line-height`\n * matches its block height renders the text glyphs vertically centered;\n * without this, the line-box sits at the top of the cell and leaves the\n * empty space below.\n *\n * Cell renderer Texts have `setAutoMeasure(false)`, so `setLineHeight`\n * skips its DOM measurement and only writes the CSS rule.\n */\n doLayout(): this {\n super.doLayout();\n\n const children = this.getComponents();\n if (children.length !== 1) {\n return this;\n }\n\n const child = children[0];\n if (!(child instanceof Text)) {\n return this;\n }\n\n const h = child.getHeight();\n if (h > 0) {\n child.setLineHeight(h);\n }\n\n return this;\n }\n}","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A read-only renderer for string cell values.\n *\n * Displays the value via a {@link Text}. Caches the last value passed\n * to {@link setValue} so {@link getValue} returns `null` for an empty\n * cell, distinct from the empty-string render the user sees.\n *\n * @category Components\n */\nclass StringRenderer extends CellRenderer<String | null> {\n\n private _text: Text = new Text();\n private _value: String | null = null;\n\n constructor() {\n super();\n\n this._text.setText(\"\");\n this._text.setPointerEvents(\"none\");\n this._text.setAutoMeasure(false);\n this.addComponent(this._text);\n }\n\n /**\n * Returns the text component used to display text.\n *\n * @returns The underlying {@link Text}.\n */\n getText(): Text {\n return this._text;\n }\n\n /**\n * Returns the cached string value, or `null` when the cell is empty.\n *\n * @returns The displayed string value, or `null`.\n */\n getValue(): String | null {\n return this._value;\n }\n\n /**\n * Caches the value and renders it as text. `null` and `undefined`\n * are both normalised to `null` and render as the empty string.\n *\n * @param value - The string value to display, or `null`/`undefined`\n * to clear the cell.\n */\n setValue(value: String | null): this {\n this._value = value ?? null;\n this._text.setText(this._value === null ? \"\" : String(this._value));\n\n return this;\n }\n}\n\nconst StringRendererCallable = callable(StringRenderer);\ntype StringRendererCallable = StringRenderer;\nexport {\n StringRenderer as _StringRenderer,\n StringRendererCallable as StringRenderer\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A read-only renderer for numeric cell values.\n *\n * Displays the value right-aligned via a {@link Text}. Caches the last\n * value passed to {@link setValue} so {@link getValue} returns the exact\n * `Number | null` that was rendered — never the result of re-parsing the\n * DOM text, which silently coerces an empty cell back to `0`.\n *\n * @category Components\n */\nclass NumberRenderer extends CellRenderer<Number | null> {\n\n private _text: Text = new Text();\n private _value: Number | null = null;\n\n constructor() {\n super();\n\n this._text.setPointerEvents(\"none\");\n this._text.setTextAlign(\"right\");\n this._text.setText(\"\");\n this._text.setAutoMeasure(false);\n\n this.addComponent(this._text);\n }\n\n /**\n * Returns the cached numeric value, or `null` when the cell is\n * empty. Reads the private cache rather than re-parsing the DOM\n * text so an empty cell round-trips as `null` instead of `0`.\n *\n * @returns The current numeric value, or `null`.\n */\n getValue(): Number | null {\n return this._value;\n }\n\n /**\n * Caches the value and renders it as text. `null` and `undefined`\n * are both normalised to `null` and render as the empty string;\n * every other value (including `0`, `-1`, `NaN`, `Infinity`) goes\n * through `String(value)` so the cell shows the actual literal —\n * never the words `\"undefined\"` or `\"null\"`.\n *\n * @param value - The numeric value to display, or `null`/`undefined`\n * to clear the cell.\n */\n setValue(value: Number | null): this {\n this._value = value ?? null;\n this._text.setText(this._value === null ? \"\" : String(this._value));\n\n return this;\n }\n}\n\nconst NumberRendererCallable = callable(NumberRenderer);\ntype NumberRendererCallable = NumberRenderer;\nexport {\n NumberRenderer as _NumberRenderer,\n NumberRendererCallable as NumberRenderer\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A read-only renderer for date cell values.\n *\n * Displays the value via a {@link Text} formatted with `Date.toLocaleDateString`.\n */\nclass DateRenderer extends CellRenderer<Date | null> {\n\n private _text: Text = new Text();\n private _value: Date | null = null;\n\n constructor() {\n super();\n\n this._text.setText(\"\");\n this._text.setPointerEvents(\"none\");\n this._text.setAutoMeasure(false);\n this.addComponent(this._text);\n }\n\n getValue(): Date | null {\n return this._value;\n }\n\n setValue(value: Date | null): this {\n this._value = value ?? null;\n this._text.setText(value ? value.toLocaleDateString() : \"\");\n\n return this;\n }\n}\n\nconst DateRendererCallable = callable(DateRenderer);\ntype DateRendererCallable = DateRenderer;\nexport {\n DateRenderer as _DateRenderer,\n DateRendererCallable as DateRenderer\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A read-only renderer for time cell values.\n *\n * Displays the time portion of a `Date` via a {@link Text} formatted with\n * `Date.toLocaleTimeString`.\n */\nclass TimeRenderer extends CellRenderer<Date | null> {\n\n private _text: Text = new Text();\n private _value: Date | null = null;\n private _showSeconds: boolean;\n\n constructor(showSeconds: boolean = false) {\n super();\n this._showSeconds = showSeconds;\n\n this._text.setText(\"\");\n this._text.setPointerEvents(\"none\");\n this._text.setAutoMeasure(false);\n this.addComponent(this._text);\n }\n\n getValue(): Date | null {\n return this._value;\n }\n\n setValue(value: Date | null): this {\n this._value = value ?? null;\n const opts: Intl.DateTimeFormatOptions = this._showSeconds\n ? { hour: '2-digit', minute: '2-digit', second: '2-digit' }\n : { hour: '2-digit', minute: '2-digit' };\n this._text.setText(value ? value.toLocaleTimeString(undefined, opts) : \"\");\n\n return this;\n }\n}\n\nconst TimeRendererCallable = callable(TimeRenderer);\ntype TimeRendererCallable = TimeRenderer;\nexport {\n TimeRenderer as _TimeRenderer,\n TimeRendererCallable as TimeRenderer\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A read-only renderer for date-time cell values.\n *\n * Displays a `Date` via a {@link Text} formatted with `Date.toLocaleString`.\n */\nclass DateTimeRenderer extends CellRenderer<Date | null> {\n\n private _text: Text = new Text();\n private _value: Date | null = null;\n private _showSeconds: boolean;\n\n constructor(showSeconds: boolean = false) {\n super();\n this._showSeconds = showSeconds;\n\n this._text.setText(\"\");\n this._text.setPointerEvents(\"none\");\n this._text.setAutoMeasure(false);\n this.addComponent(this._text);\n }\n\n getValue(): Date | null {\n return this._value;\n }\n\n setValue(value: Date | null): this {\n this._value = value ?? null;\n const opts: Intl.DateTimeFormatOptions = this._showSeconds\n ? { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' }\n : { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' };\n this._text.setText(value ? value.toLocaleString(undefined, opts) : \"\");\n\n return this;\n }\n}\n\nconst DateTimeRendererCallable = callable(DateTimeRenderer);\ntype DateTimeRendererCallable = DateTimeRenderer;\nexport {\n DateTimeRenderer as _DateTimeRenderer,\n DateTimeRendererCallable as DateTimeRenderer\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { Glyph as GlyphComponent } from \"~/component/display/Glyph.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A read-only renderer for cells whose value is a registry glyph name.\n *\n * Displays the value via a [`Glyph`](/api/component/display/classes/Glyph) instance.\n * The glyph follows `currentColor`, so the icon inherits the surrounding cell's\n * foreground colour for free. Caches the last value passed to\n * {@link setValue} so {@link getValue} returns `null` for an empty cell\n * instead of an empty string.\n *\n * @category Components\n */\nclass GlyphRenderer extends CellRenderer<String | null> {\n\n private _value: String | null = null;\n private _glyph: GlyphComponent | null = null;\n\n /**\n * Returns the cached glyph name, or `null` when the cell is empty.\n *\n * @returns The registry name passed to the most recent {@link setValue} call,\n * or `null` if the cell has no glyph.\n */\n getValue(): String | null {\n return this._value;\n }\n\n /**\n * Replaces the displayed glyph with one for the given registry name.\n * `null`, `undefined`, and any other falsy value remove the glyph\n * child entirely so the cell renders blank.\n *\n * @param value - Registry glyph name, or `null`/`undefined`/falsy to\n * clear the cell.\n *\n * @returns This renderer, for method chaining.\n */\n setValue(value: String | null): this {\n const next = value ? value : null;\n if (next === this._value && (next === null || this._glyph)) {\n return this;\n }\n\n if (this._glyph) {\n this.removeComponent(this._glyph);\n this._glyph = null;\n }\n\n this._value = next;\n\n if (next !== null) {\n this._glyph = new GlyphComponent(next as string);\n this._glyph.setPointerEvents(\"none\");\n this.addComponent(this._glyph);\n }\n\n return this;\n }\n}\n\nconst GlyphRendererCallable = callable(GlyphRenderer);\ntype GlyphRendererCallable = GlyphRenderer;\nexport {\n GlyphRenderer as _GlyphRenderer,\n GlyphRendererCallable as GlyphRenderer\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport type { ModelRecord } from \"~/data/ModelRecord.js\";\nimport type { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport type { FieldType } from \"~/data/Field.js\";\n\n/**\n * One selectable option for a constrained-choice (combo-box) column.\n *\n * A plain string in a {@link ColumnConfig.values} array is shorthand for\n * `{ value: s, label: s }`; an object form lets the value stored on the\n * record differ from the text shown to the user (e.g. value `\"AU\"`, label\n * `\"Australia\"`).\n *\n * @category Components\n */\nexport interface ComboOption {\n /** The value stored on the record and round-tripped by the cell. */\n value: string;\n /** Display text shown in the cell and dropdown; defaults to `value`. */\n label?: string;\n}\n\n/**\n * Expands a {@link ColumnConfig.values} array — whose entries are either\n * plain strings or {@link ComboOption} objects — into fully-populated\n * `{ value, label }` pairs, defaulting an omitted `label` to the `value`.\n *\n * Shared by the combo cell's renderer (which builds a value-to-label map)\n * and its editor (which builds the dropdown's item list) so the two never\n * disagree about what label a value renders.\n *\n * @param options - The raw `values` entries from a column config.\n * @returns One `{ value, label }` pair per entry, in the same order.\n *\n * @internal\n */\nexport function normalizeComboOptions(options: Array<ComboOption | string>): Array<Required<ComboOption>> {\n return options.map(option =>\n typeof option === \"string\"\n ? { value: option, label: option }\n : { value: option.value, label: option.label ?? option.value },\n );\n}\n\n/**\n * The cell variant a per-cell resolver may select. Extends {@link FieldType}\n * with `'combo'` for a constrained-choice (combo-box) cell.\n *\n * @category Components\n */\nexport type CellType = FieldType | 'combo';\n\n/**\n * Presentation configuration for a single table column.\n *\n * References a model field by name and carries optional display constraints.\n * Fields not mentioned in a {@link ColumnSpec} are auto-generated with default\n * sizing unless `appendUnlisted` is set to `false`.\n *\n * @category Components\n */\nexport interface ColumnConfig {\n /** The model field name this column presents. */\n field : string;\n /** Minimum width in pixels. The column cannot be dragged narrower than this value. */\n minWidth ?: number;\n /** Maximum width in pixels. The column cannot be dragged wider than this value. */\n maxWidth ?: number;\n /** When `true` the column starts hidden; the user can still reveal it via the context menu. */\n hidden ?: boolean;\n /**\n * When `true` the user cannot hide this column via the context menu;\n * the entry renders disabled with the visible checkmark, and\n * `Table.setColumnVisible(field, false)` is a no-op. Takes precedence\n * over `hidden: true` — an unhideable column never starts hidden.\n *\n * Defaults to `false`. The flag does not auto-include the column when\n * `appendUnlisted: false` excludes unlisted fields; list the column\n * explicitly in `spec.columns` to mark it unhideable.\n */\n unhideable ?: boolean;\n\n /**\n * When `true`, every cell in this column is read-only — the value\n * is displayed but the user cannot edit it. Read-only cells refuse\n * inline editing (double-click is a no-op), render with a subtle\n * grey tint sourced from `--ts-ui-table-cell-readonly-bg`, and\n * present a default cursor on hover instead of the edit affordance.\n *\n * Selection, keyboard navigation, sorting, resizing, drag-reorder,\n * and CSV / JSON export are unaffected — read-only means \"value is\n * fixed,\" not \"row is inert.\" In a {@link TreeTable}, toggling\n * expand / collapse on the tree column still works when that column\n * is read-only (expansion is not editing).\n *\n * Defaults to `false`.\n */\n readOnly ?: boolean;\n /**\n * Per-cell read-only predicate, evaluated per record on every\n * rebind. Returns `true` to mark this column's cell read-only for\n * the given record. Composes with {@link ColumnConfig.readOnly} and\n * {@link ColumnSpec.rowReadOnly} via OR — a cell is read-only when\n * ANY of the three signals says so.\n *\n * The predicate fires on every row rebind: when scrolling pulls\n * new records into the visible window, when the store emits\n * `'datachange'` (which\n * [`notifyRecordChanged`](/api/data/classes/AbstractStore#notifyRecordChanged)\n * does), or when columns are hidden / shown. It MUST be O(1) and\n * pure — read fields off `record`, return a boolean, do not call\n * back into the store, do not allocate, do not perform DOM work.\n * Memoise inside the predicate if your computation is non-trivial;\n * the table does not cache results.\n *\n * Mutating a store-owned record auto-refreshes the table; call\n * `store.notifyRecordChanged(record)` only for an unowned record or\n * to force a refresh — the predicate fires again on the next paint.\n */\n cellReadOnly ?: (record: ModelRecord) => boolean;\n /**\n * For `time` and `datetime` columns: when `true` the editor and renderer include seconds.\n * Defaults to `false` (hours and minutes only).\n */\n showSeconds ?: boolean;\n /**\n * When present, this column renders as a constrained-choice (combo-box)\n * cell regardless of the field's declared type. The inline editor offers\n * exactly these options; the cell displays each option's label for the\n * stored value, falling back to the raw value when it is not in the set.\n *\n * Each entry is either a plain string (shorthand for value === label) or\n * a {@link ComboOption} `{ value, label }` pair. The value stored on the\n * record is always the option's `value` string. An empty or absent array\n * leaves the column on its field-type-driven cell.\n */\n values ?: Array<ComboOption | string>;\n /**\n * Per-cell (per-record) variant resolver. Returns which built-in cell\n * type to render/edit for THIS row's cell, or `null` to fall back to\n * the column's field-type-driven cell. Its presence switches the\n * column onto a dynamic, per-record cell.\n *\n * The resolver fires on every row rebind — when scrolling pulls new\n * records into the visible window, when the store emits\n * `'datachange'`, or when columns are hidden / shown. It MUST be O(1)\n * and pure — read fields off `record`, return a variant, do not call\n * back into the store, do not allocate, do not perform DOM work.\n *\n * Heterogeneous columns (rows that commit different native types)\n * must declare the field `'auto'` so commits are not coerced to one\n * type.\n */\n cellType ?: (record: ModelRecord) => CellType | null;\n /**\n * Per-cell combo options, consulted only when {@link ColumnConfig.cellType}\n * returns `'combo'` for the record. Each entry is a plain string or a\n * {@link ComboOption}. Absent/empty yields an empty dropdown.\n */\n cellValues ?: (record: ModelRecord) => Array<ComboOption | string> | undefined;\n /**\n * Custom cell-renderer factory for this column. When present it overrides\n * both the `values` (combo) routing and the field-type-driven cell: every\n * cell in the column is built display-only (no inline editor) around a\n * fresh {@link CellRenderer} from this factory, so the column shows\n * computed or styled content — a link, a badge, an icon beside text —\n * instead of an editable value.\n *\n * The factory runs once per rendered cell (one per row in the visible\n * window; cells are reused as the window scrolls). Each cell's value is\n * pushed to the renderer through `setValue` on every rebind, exactly like\n * the built-in typed renderers, so keep the renderer O(1) and pure. A\n * custom-rendered cell never enters edit mode; wire the {@link Table}\n * `\"cellclick\"` event for click behaviour.\n *\n * The library ships\n * [`LinkCellRenderer`](/api/component/table/classes/LinkCellRenderer) for the\n * common \"clickable link cell\" case — pass `renderer: () => new\n * LinkCellRenderer()` and handle `\"cellclick\"`.\n */\n renderer ?: () => CellRenderer<any>;\n /**\n * Registry glyph name shown to the left of the header text.\n * Omit for no glyph; no left-side gap is reserved when absent.\n */\n headerGlyph ?: string;\n /**\n * Name of the parent-header group this column belongs to. Adjacent columns\n * sharing the same group name render under a single spanning parent header\n * cell. Non-adjacent same-named columns render as two separate parent cells.\n * Omit to leave the column ungrouped; the parent row then renders an empty\n * spanning cell above it.\n */\n group ?: string;\n /**\n * Optional background color (CSS color string) for the parent header cell.\n * All columns in a contiguous group should agree on this value; the first\n * encountered value in the run wins.\n */\n groupColor ?: string;\n\n /**\n * When `true`, marks this column required: the header renders a\n * trailing asterisk, and every row's cell in this column outlines\n * with `--ts-ui-table-cell-required-outline` while its bound value\n * is empty (`null`, `undefined`, or `''`). Composes with\n * {@link ColumnConfig.requiredPredicate} via OR for the empty-cell\n * outline, but drives the header asterisk alone — the header cell\n * has no bound record to evaluate a per-record predicate against.\n *\n * Defaults to `false`.\n */\n required ?: boolean;\n /**\n * Per-record required predicate. Returns `true` to mark this\n * column's cell required for the given record. Composes with\n * {@link ColumnConfig.required} via OR for the empty-cell outline;\n * does NOT drive the header asterisk (the header has no bound\n * record).\n *\n * Unlike {@link ColumnConfig.cellReadOnly}, this predicate fires on\n * every visible-window render pass, not just on row rebind —\n * scrolling pulls new records in, the store emitting `'datachange'`\n * (which\n * [`notifyRecordChanged`](/api/data/classes/AbstractStore#notifyRecordChanged)\n * does) triggers one, columns hiding / showing triggers one, AND a\n * plain in-place cell edit re-runs it too, since the empty-cell\n * outline must track the live value. It MUST be O(1) and pure —\n * read fields off `record`, return a boolean, do not call back\n * into the store, do not allocate, do not perform DOM work.\n *\n * This is a visual affordance only: it does not block commits or\n * integrate with store-level validation.\n */\n requiredPredicate ?: (record: ModelRecord) => boolean;\n}\n\n/**\n * Presentation specification passed to a {@link Table} to control which columns\n * are shown and how they behave.\n *\n * When omitted entirely the table auto-generates one column per model field\n * using default sizing — identical to the pre-spec behaviour.\n *\n * @example\n * ```typescript\n * new Table(store, {\n * columns: [\n * { field: 'name', minWidth: 120 },\n * { field: 'id', maxWidth: 80 },\n * ],\n * })\n * ```\n *\n * @category Components\n */\nexport interface ColumnSpec {\n /** Per-column presentation overrides, in preferred display order. */\n columns : ColumnConfig[];\n /**\n * When `true` (the default) model fields not mentioned in `columns` are\n * appended automatically after the listed columns with default sizing.\n * When `false` only the explicitly listed fields appear in the table.\n */\n appendUnlisted ?: boolean;\n /**\n * Per-row read-only predicate. Receives each visible record on every\n * rebind and returns `true` to mark every cell in that row read-only\n * for the next render pass.\n *\n * Composes with {@link ColumnConfig.readOnly} (column-level static\n * flag) and {@link ColumnConfig.cellReadOnly} (per-cell predicate):\n * a cell is read-only when ANY of the three signals says so.\n *\n * The predicate fires on every row rebind — when scrolling pulls\n * new records into the visible window, when the store emits\n * `'datachange'` (which\n * [`notifyRecordChanged`](/api/data/classes/AbstractStore#notifyRecordChanged)\n * does), or when columns are hidden / shown. It MUST be O(1) and\n * pure: read fields off `record`, return a boolean, do not call\n * back into the store, do not allocate, do not touch the DOM.\n * Memoise inside your predicate if the computation is non-trivial;\n * the framework does not cache results.\n *\n * Mutating a store-owned record auto-refreshes the table; call\n * `store.notifyRecordChanged(record)` only for an unowned record or\n * to force a refresh — the predicate fires again on the next paint.\n */\n rowReadOnly ?: (record: ModelRecord) => boolean;\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { normalizeComboOptions } from \"~/component/table/ColumnConfig.js\";\nimport type { ComboOption } from \"~/component/table/ColumnConfig.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A read-only renderer for constrained-choice (combo-box) cell values.\n *\n * Stores the option *value* (the code round-tripped to the record) but\n * displays the option *label*: a value of `\"AU\"` with an option set of\n * `[{ value: \"AU\", label: \"Australia\" }]` renders `\"Australia\"`. A value\n * not present in the option set falls back to rendering the raw value, so a\n * record holding an out-of-set code is still visible rather than blanked.\n * Caches the last value passed to {@link setValue} so {@link getValue}\n * returns `null` for an empty cell, distinct from the empty-string render.\n *\n * @category Components\n */\nclass ComboRenderer extends CellRenderer<String | null> {\n\n private _text: Text = new Text();\n private _map: Map<string, string> = new Map();\n private _value: String | null = null;\n\n /**\n * @param optionList - The column's option set; each entry is a plain\n * string (value === label) or a `{ value, label }` pair. Used to\n * build the value-to-label lookup the renderer displays.\n */\n constructor(optionList: Array<ComboOption | string>) {\n super();\n\n for (const option of normalizeComboOptions(optionList)) {\n this._map.set(option.value, option.label);\n }\n\n this._text.setText(\"\");\n this._text.setPointerEvents(\"none\");\n this._text.setAutoMeasure(false);\n this.addComponent(this._text);\n }\n\n /**\n * Returns the text component used to display the option label.\n *\n * @returns The underlying {@link Text}.\n */\n getText(): Text {\n return this._text;\n }\n\n /**\n * Returns the cached option value (the code stored on the record), or\n * `null` when the cell is empty. Returns the value, never the label, so\n * the round-trip back to the record stays honest.\n *\n * @returns The displayed option value, or `null`.\n */\n getValue(): String | null {\n return this._value;\n }\n\n /**\n * Caches the value and renders its label. `null` and `undefined` both\n * normalise to `null` and render the empty string; a value outside the\n * option set renders the raw value rather than blanking.\n *\n * @param value - The option value to display, or `null`/`undefined` to\n * clear the cell.\n */\n setValue(value: String | null): this {\n this._value = value ?? null;\n\n if (this._value === null) {\n this._text.setText(\"\");\n } else {\n const key = String(this._value);\n\n this._text.setText(this._map.get(key) ?? key);\n }\n\n return this;\n }\n\n /**\n * Rebuilds the value-to-label map from a new option set and re-renders\n * the cached value against it, so a shared renderer can be reused\n * across rows whose combo options differ.\n *\n * @param optionList - The new option set; same shape as the constructor\n * argument.\n * @returns This renderer, for method chaining.\n */\n setOptions(optionList: Array<ComboOption | string>): this {\n this._map.clear();\n\n for (const option of normalizeComboOptions(optionList)) {\n this._map.set(option.value, option.label);\n }\n\n this.setValue(this._value);\n\n return this;\n }\n}\n\nconst ComboRendererCallable = callable(ComboRenderer);\ntype ComboRendererCallable = ComboRenderer;\nexport {\n ComboRenderer as _ComboRenderer,\n ComboRendererCallable as ComboRenderer\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport type { CellEditor } from \"~/component/table/cell/editor/CellEditor.js\";\nimport type { ComboEditor } from \"~/component/table/cell/editor/Combo.js\";\nimport { BooleanEditor } from \"~/component/table/cell/editor/Boolean.js\";\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { StringRenderer } from \"~/component/table/cell/renderer/String.js\";\nimport { NumberRenderer } from \"~/component/table/cell/renderer/Number.js\";\nimport { DateRenderer } from \"~/component/table/cell/renderer/Date.js\";\nimport { TimeRenderer } from \"~/component/table/cell/renderer/Time.js\";\nimport { DateTimeRenderer } from \"~/component/table/cell/renderer/DateTime.js\";\nimport { GlyphRenderer } from \"~/component/table/cell/renderer/Glyph.js\";\nimport { ComboRenderer } from \"~/component/table/cell/renderer/Combo.js\";\nimport type { CellType, ColumnConfig, ComboOption } from \"~/component/table/ColumnConfig.js\";\nimport type { ModelRecord } from \"~/data/ModelRecord.js\";\nimport type { FieldType } from \"~/data/Field.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A table cell whose active renderer and editor vary per bound record,\n * driven by {@link ColumnConfig.cellType}.\n *\n * One `DynamicCell` instance lives for the life of its pool slot — every\n * built-in variant it can show (`string`, `number`, `date`, `time`,\n * `datetime`, `glyph`, `combo`, `boolean`) is built lazily and cached, then\n * swapped into view on {@link DynamicCell.bindRecord} by making it the\n * cell's active renderer. This keeps the slot's geometry cache,\n * editor-pool wiring, commit wiring, read-only state, and focus behaviour\n * valid across rebinds that change the resolved variant, unlike replacing\n * the cell instance itself.\n *\n * A `'boolean'` row reuses the checkbox-as-renderer pattern from\n * {@link BooleanCell}: a {@link BooleanEditor} is placed in the renderer\n * slot and commits immediately on toggle, with no separate edit cycle. A\n * `'combo'` row shares one pooled `combo:<field>` editor across every combo\n * row in the column; {@link ColumnConfig.cellValues} supplies that row's\n * option set, injected into the pooled editor just before it opens.\n *\n * @category Components\n */\nclass DynamicCell extends Cell<any> {\n\n private _field: string;\n private _columnType: FieldType;\n private _showSeconds: boolean;\n private _cellType: ((record: ModelRecord) => CellType | null) | undefined;\n private _cellValues: ((record: ModelRecord) => Array<ComboOption | string> | undefined) | undefined;\n private _renderers: Map<CellType, CellRenderer<any>> = new Map();\n private _activeType: CellType = 'string';\n private _currentOptions: Array<ComboOption | string> = [];\n private _checkbox: BooleanEditor | null = null;\n // Set by `ensureRenderer` / `ensureCheckbox` immediately before the call\n // to `setActiveRenderer` that consumes it, to say whether the returned\n // variant was just constructed (true) or already cached (false).\n private _justCreated: boolean = false;\n\n /**\n * @param field - The model field name this column presents.\n * @param columnType - The column's declared field type; the fallback\n * variant when {@link ColumnConfig.cellType} returns `null` for a record.\n * @param config - The column's config, supplying `cellType`, `cellValues`,\n * and `showSeconds`.\n */\n constructor(field: string, columnType: FieldType, config: ColumnConfig) {\n const placeholder = new StringRenderer();\n\n super(\"td\", placeholder);\n\n this._field = field;\n this._columnType = columnType;\n this._showSeconds = config.showSeconds ?? false;\n this._cellType = config.cellType;\n this._cellValues = config.cellValues;\n\n this._renderers.set('string', placeholder);\n }\n\n /**\n * Resolves the active variant for `record`, swaps to the matching\n * cached renderer (constructing it on first use), and pushes the\n * record's value into it.\n *\n * @param record - The record now bound to this cell's pool slot.\n */\n bindRecord(record: ModelRecord): void {\n const resolved = this._cellType?.(record) ?? this._columnType;\n\n this._activeType = resolved;\n\n if (resolved === 'boolean') {\n const checkbox = this.ensureCheckbox();\n\n this.setActiveRenderer(checkbox as unknown as CellRenderer<any>, this._justCreated);\n checkbox.setValue(record.get(this._field));\n\n return;\n }\n\n if (resolved === 'combo') {\n this._currentOptions = this._cellValues?.(record) ?? [];\n\n const renderer = this.ensureRenderer('combo') as ComboRenderer;\n renderer.setOptions(this._currentOptions);\n this.setActiveRenderer(renderer, this._justCreated);\n renderer.setValue(record.get(this._field));\n\n return;\n }\n\n const renderer = this.ensureRenderer(resolved);\n\n this.setActiveRenderer(renderer, this._justCreated);\n renderer.setValue(record.get(this._field));\n }\n\n /**\n * Returns the pool key for the active variant's shared editor.\n *\n * @returns A key registered on the {@link CellEditorPool}, or `null` for\n * variants with no pooled editor (`boolean`, `glyph`).\n */\n getEditorKey(): string | null {\n switch (this._activeType) {\n case 'number':\n return 'number';\n case 'date':\n return 'date';\n case 'time':\n return this._showSeconds ? 'time:seconds' : 'time';\n case 'datetime':\n return this._showSeconds ? 'datetime:seconds' : 'datetime';\n case 'combo':\n return `combo:${this._field}`;\n case 'boolean':\n case 'glyph':\n return null;\n default:\n return 'string'; // 'string' | 'auto'\n }\n }\n\n /**\n * Toggles the checkbox directly when the active variant is `boolean`\n * (mirroring {@link BooleanCell.startEdit}, which has no separate edit\n * cycle); otherwise defers to the base pooled-editor flow.\n */\n startEdit(): void {\n if (this._activeType === 'boolean') {\n if (this.isReadOnly()) {\n return;\n }\n\n this._checkbox?.toggle();\n\n return;\n }\n\n super.startEdit();\n }\n\n /**\n * Marks the cell read-only and, when the active variant is `boolean`,\n * also disables the checkbox so it rejects toggles.\n *\n * @param value - `true` to mark read-only, `false` to restore.\n * @returns This cell, for method chaining.\n */\n setReadOnly(value: boolean): this {\n super.setReadOnly(value);\n\n if (this._activeType === 'boolean') {\n this._checkbox?.setReadOnly(value);\n }\n\n return this;\n }\n\n /**\n * Pushes the current record's combo options into the pooled editor\n * before it receives a value, so a shared `combo:<field>` editor\n * reflects this row's option set rather than whichever row last opened it.\n *\n * @param editor - The editor about to be shown.\n */\n protected prepareEditor(editor: CellEditor<any>): void {\n if (this._activeType === 'combo') {\n (editor as ComboEditor).setOptions(this._currentOptions);\n }\n }\n\n /**\n * Returns the cached renderer for `type`, lazily constructing it on\n * first use and recording whether it was just constructed via\n * `_justCreated`.\n *\n * @param type - The built-in variant to fetch a renderer for.\n * @returns The cached (or freshly built) renderer for `type`.\n */\n private ensureRenderer(type: CellType): CellRenderer<any> {\n const cached = this._renderers.get(type);\n\n if (cached) {\n this._justCreated = false;\n\n return cached;\n }\n\n const renderer = DynamicCell.buildRenderer(type, this._showSeconds);\n\n this._renderers.set(type, renderer);\n this._justCreated = true;\n\n return renderer;\n }\n\n /**\n * Constructs the renderer for a built-in variant. `glyph` and any\n * unrecognised variant fall back to a {@link StringRenderer}.\n *\n * @param type - The variant to construct a renderer for.\n * @param showSeconds - Forwarded to `time`/`datetime` renderers.\n * @returns A freshly constructed renderer for `type`.\n */\n private static buildRenderer(type: CellType, showSeconds: boolean): CellRenderer<any> {\n switch (type) {\n case 'number':\n return new NumberRenderer();\n case 'date':\n return new DateRenderer();\n case 'time':\n return new TimeRenderer(showSeconds);\n case 'datetime':\n return new DateTimeRenderer(showSeconds);\n case 'combo':\n return new ComboRenderer([]);\n case 'glyph':\n return new GlyphRenderer();\n default:\n return new StringRenderer(); // 'string' | 'auto'\n }\n }\n\n /**\n * Returns the cached checkbox editor, lazily constructing it on first\n * use and wiring its `change` event to commit immediately (mirroring\n * {@link BooleanCell}'s immediate-commit contract). Records whether it\n * was just constructed via `_justCreated`.\n *\n * @returns The cached (or freshly built) checkbox editor.\n */\n private ensureCheckbox(): BooleanEditor {\n if (this._checkbox) {\n this._justCreated = false;\n\n return this._checkbox;\n }\n\n const checkbox = new BooleanEditor();\n\n checkbox.on(\"change\", (value) => this.emitCommit(value));\n\n this._checkbox = checkbox;\n this._justCreated = true;\n\n return checkbox;\n }\n\n /**\n * Fires the cell's `\"commit\"` event with `value`. Extracted so the\n * checkbox's `change` listener has a stable bound method to call.\n *\n * @param value - The value to commit.\n */\n private emitCommit(value: any): void {\n this.emit(\"commit\", value);\n }\n}\n\nconst DynamicCellCallable = callable(DynamicCell);\ntype DynamicCellCallable = DynamicCell;\nexport {\n DynamicCell as _DynamicCell,\n DynamicCellCallable as DynamicCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { StringRenderer } from \"~/component/table/cell/renderer/String.js\";\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A plain string-rendering cell with no editor.\n *\n * Used as the fallback cell type for fields whose type is not explicitly mapped,\n * and as the base for {@link HeaderCell}.\n *\n * @category Components\n */\nclass DefaultCell extends Cell<String | null> {\n\n constructor(tag?: string) {\n let renderer = new StringRenderer();\n\n super(tag || \"td\", renderer);\n }\n\n /**\n * Returns the renderer cast to StringRenderer.\n *\n * @returns The {@link StringRenderer} for this cell.\n */\n getRenderer() {\n return <StringRenderer>super.getRenderer();\n }\n\n /**\n * Sets the displayed text value on the string renderer. `null` and\n * `undefined` render the cell as blank.\n *\n * @param value - The string value to display, or `null`/`undefined`\n * to clear the cell.\n */\n setValue(value: String | null): this {\n this.getRenderer().setValue(value);\n\n return this;\n }\n}\n\nconst DefaultCellCallable = callable(DefaultCell);\ntype DefaultCellCallable = DefaultCell;\nexport {\n DefaultCell as _DefaultCell,\n DefaultCellCallable as DefaultCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport { StringRenderer } from \"~/component/table/cell/renderer/String.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A table cell for string values.\n *\n * Uses a [`StringRenderer`](/api/component/table/classes/StringRenderer) for display and borrows\n * a shared [`StringEditor`](/api/component/table/classes/StringEditor) from the body's\n * {@link CellEditorPool} on edit.\n *\n * @category Components\n */\nclass StringCell extends Cell<String | null> {\n\n constructor() {\n let renderer = new StringRenderer();\n\n super(\"td\", renderer);\n }\n\n /**\n * Returns the pool key for the shared {@link StringEditor}.\n *\n * @returns The string `\"string\"`.\n */\n getEditorKey(): string {\n return \"string\";\n }\n\n /**\n * Sets the displayed text value on the renderer. `null` and\n * `undefined` render the cell as blank.\n *\n * @param value - The string value to display, or `null`/`undefined`\n * to clear the cell.\n */\n setValue(value: String | null): this {\n this.getRenderer().setValue(value);\n\n return this;\n }\n}\n\nconst StringCellCallable = callable(StringCell);\ntype StringCellCallable = StringCell;\nexport {\n StringCell as _StringCell,\n StringCellCallable as StringCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { BooleanEditor } from \"~/component/table/cell/editor/Boolean.js\";\nimport { Cell, CellEvent } from \"~/component/table/cell/Cell.js\";\nimport { FillType } from \"~/layout/FillType.js\";\nimport { AnchorType } from \"~/layout/AnchorType.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A table cell for boolean values, rendered as a checkbox.\n *\n * Unlike text-based cells, the {@link BooleanEditor} doubles as the renderer; there is\n * no separate renderer or edit-mode toggle — the checkbox is always interactive.\n *\n * @category Components\n */\nclass BooleanCell extends Cell<Boolean | null> {\n\n private _checkbox: BooleanEditor;\n\n constructor() {\n let editor = new BooleanEditor();\n\n super(\"td\",\n editor,\n undefined, {\n fill: FillType.NONE,\n anchor: AnchorType.CENTER\n },\n undefined\n );\n\n this._checkbox = editor;\n }\n\n /**\n * Registers a listener for one of this cell's events. The `\"commit\"`\n * listener is wired directly to the checkbox's `\"change\"` event rather\n * than the base {@link Cell} listener bag.\n *\n * @param event - The event name.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This cell, for method chaining.\n *\n * @remarks Overrides the base class because BooleanCell has no separate\n * edit/commit cycle; changes are committed immediately on each checkbox\n * interaction. Routed through the cached editor reference rather than\n * `getRenderer()` so the wiring survives a\n * [`TreeCellRenderer`](/api/component/table/classes/TreeCellRenderer)\n * wrap when the column is the tree column. The `\"commit\"` listener only\n * fires for user interaction, which always lands on a concrete\n * `true`/`false`; the `null` branch is included for consistency with the\n * editor's signature but is never emitted by the editor.\n */\n on(event: \"commit\", listener: (value: Boolean | null) => void): this;\n on(event: \"editend\", listener: () => void): this;\n on(event: CellEvent, listener: Function): this {\n if (event === \"commit\") {\n this._checkbox.on(\"change\", listener as (value: Boolean | null) => void);\n\n return this;\n }\n\n return super.on(event, listener as () => void);\n }\n\n /**\n * Removes a previously registered listener. The `\"commit\"` listener is\n * detached from the checkbox's `\"change\"` event; other events delegate\n * to the base {@link Cell}.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This cell, for method chaining.\n */\n off(event: CellEvent, listener: Function): this {\n if (event === \"commit\") {\n this._checkbox.off(\"change\", listener);\n\n return this;\n }\n\n return super.off(event, listener);\n }\n\n /**\n * Marks the cell read-only and propagates the flag to the checkbox so it\n * rejects user toggles. The base {@link Cell.setReadOnly} only handles the\n * cell chrome and the pooled-editor lifecycle; a BooleanCell's checkbox is\n * its always-on renderer, so it must be disabled explicitly or it would\n * stay interactive in a read-only column.\n *\n * @param value - `true` to lock the checkbox, `false` to restore it.\n *\n * @returns This cell, for method chaining.\n */\n setReadOnly(value: boolean): this {\n super.setReadOnly(value);\n this._checkbox.setReadOnly(value);\n\n return this;\n }\n\n /**\n * Toggles the checkbox value and fires the commit callback. A no-op when\n * the cell is read-only — the base {@link Cell.startEdit} self-guards on\n * `isReadOnly`, and this override (reached via the renderer's dblclick and\n * the body's keyboard activation) must do the same so a locked column never\n * toggles.\n */\n startEdit() {\n if (this.isReadOnly()) {\n return;\n }\n\n this._checkbox.toggle();\n }\n\n /**\n * Sets the checkbox checked state. `null` and `undefined` put the\n * checkbox into the indeterminate (mixed) state.\n *\n * @param value - The boolean value to reflect on the checkbox, or\n * `null`/`undefined` to render the indeterminate state.\n */\n setValue(value: Boolean | null): this {\n this.getRenderer().setValue(value);\n\n return this;\n }\n}\n\nconst BooleanCellCallable = callable(BooleanCell);\ntype BooleanCellCallable = BooleanCell;\nexport {\n BooleanCell as _BooleanCell,\n BooleanCellCallable as BooleanCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport { NumberRenderer } from \"~/component/table/cell/renderer/Number.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A table cell for numeric values.\n *\n * Uses a [`NumberRenderer`](/api/component/table/classes/NumberRenderer) for display and borrows\n * a shared [`NumberEditor`](/api/component/table/classes/NumberEditor) from the body's\n * {@link CellEditorPool} on edit.\n *\n * @category Components\n */\nclass NumberCell extends Cell<Number | null> {\n\n constructor() {\n let renderer = new NumberRenderer();\n\n super(\"td\", renderer);\n }\n\n /**\n * Returns the pool key for the shared {@link NumberEditor}.\n *\n * @returns The string `\"number\"`.\n */\n getEditorKey(): string {\n return \"number\";\n }\n\n /**\n * Sets the displayed numeric value on the renderer. `null` and\n * `undefined` render the cell as blank.\n *\n * @param value - The numeric value to display, or `null`/`undefined`\n * to clear the cell.\n */\n setValue(value: Number | null): this {\n this.getRenderer().setValue(value);\n\n return this;\n }\n}\n\nconst NumberCellCallable = callable(NumberCell);\ntype NumberCellCallable = NumberCell;\nexport {\n NumberCell as _NumberCell,\n NumberCellCallable as NumberCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport { DateRenderer } from \"~/component/table/cell/renderer/Date.js\";\nimport { DateEditor } from \"~/component/table/cell/editor/Date.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A table cell for date values.\n *\n * Uses a [`DateRenderer`](/api/component/table/classes/DateRenderer) for display and borrows a\n * shared [`DateEditor`](/api/component/table/classes/DateEditor) from the body's\n * {@link CellEditorPool} on edit. Committing an empty field writes null; committing an\n * unparseable value reverts to the previous value instead of writing null.\n */\nclass DateCell extends Cell<Date | null> {\n\n constructor() {\n let renderer = new DateRenderer();\n\n super(\"td\", renderer);\n }\n\n /**\n * Returns the pool key for the shared {@link DateEditor}.\n *\n * @returns The string `\"date\"`.\n */\n getEditorKey(): string {\n return \"date\";\n }\n\n /**\n * Sets the displayed date value on the renderer.\n *\n * @param value - The date to display, or null to clear.\n */\n setValue(value: Date | null): this {\n this.getRenderer().setValue(value);\n\n return this;\n }\n\n /**\n * Commits the active edit, but reverts when the user typed something the editor could not\n * parse so the underlying record is not blanked.\n *\n * @returns This cell, for method chaining.\n */\n commitEdit(): this {\n const editor = this._activeEditor as DateEditor | null;\n\n if (editor && !editor.isEmpty() && editor.getValue() === null) {\n this.cancelEdit();\n\n return this;\n }\n\n super.commitEdit();\n\n return this;\n }\n}\n\nconst DateCellCallable = callable(DateCell);\ntype DateCellCallable = DateCell;\nexport {\n DateCell as _DateCell,\n DateCellCallable as DateCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport { TimeRenderer } from \"~/component/table/cell/renderer/Time.js\";\nimport { TimeEditor } from \"~/component/table/cell/editor/Time.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A table cell for time values.\n *\n * Uses a [`TimeRenderer`](/api/component/table/classes/TimeRenderer) for display and borrows a\n * shared [`TimeEditor`](/api/component/table/classes/TimeEditor) from the body's\n * {@link CellEditorPool} on edit. Committing an empty field writes null; committing an\n * unparseable value reverts to the previous value instead of writing null.\n */\nclass TimeCell extends Cell<Date | null> {\n\n private _showSeconds: boolean;\n\n constructor(showSeconds: boolean = false) {\n let renderer = new TimeRenderer(showSeconds);\n\n super(\"td\", renderer);\n\n this._showSeconds = showSeconds;\n }\n\n /**\n * Returns the pool key for the shared {@link TimeEditor}, differentiating the seconds variant.\n *\n * @returns `\"time:seconds\"` when the cell renders seconds, otherwise `\"time\"`.\n */\n getEditorKey(): string {\n return this._showSeconds ? \"time:seconds\" : \"time\";\n }\n\n /**\n * Sets the displayed time value on the renderer.\n *\n * @param value - The time to display, or null to clear.\n */\n setValue(value: Date | null): this {\n this.getRenderer().setValue(value);\n\n return this;\n }\n\n /**\n * Commits the active edit, but reverts when the user typed something the editor could not\n * parse so the underlying record is not blanked.\n *\n * @returns This cell, for method chaining.\n */\n commitEdit(): this {\n const editor = this._activeEditor as TimeEditor | null;\n\n if (editor && !editor.isEmpty() && editor.getValue() === null) {\n this.cancelEdit();\n\n return this;\n }\n\n super.commitEdit();\n\n return this;\n }\n}\n\nconst TimeCellCallable = callable(TimeCell);\ntype TimeCellCallable = TimeCell;\nexport {\n TimeCell as _TimeCell,\n TimeCellCallable as TimeCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport { DateTimeRenderer } from \"~/component/table/cell/renderer/DateTime.js\";\nimport { DateTimeEditor } from \"~/component/table/cell/editor/DateTime.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A table cell for date-time values.\n *\n * Uses a [`DateTimeRenderer`](/api/component/table/classes/DateTimeRenderer) for display and\n * borrows a shared [`DateTimeEditor`](/api/component/table/classes/DateTimeEditor) from the\n * body's {@link CellEditorPool} on edit. Committing an empty field writes null; committing an\n * unparseable value reverts to the previous value instead of writing null.\n */\nclass DateTimeCell extends Cell<Date | null> {\n\n private _showSeconds: boolean;\n\n constructor(showSeconds: boolean = false) {\n let renderer = new DateTimeRenderer(showSeconds);\n\n super(\"td\", renderer);\n\n this._showSeconds = showSeconds;\n }\n\n /**\n * Returns the pool key for the shared {@link DateTimeEditor}, differentiating the seconds variant.\n *\n * @returns `\"datetime:seconds\"` when the cell renders seconds, otherwise `\"datetime\"`.\n */\n getEditorKey(): string {\n return this._showSeconds ? \"datetime:seconds\" : \"datetime\";\n }\n\n /**\n * Sets the displayed date-time value on the renderer.\n *\n * @param value - The date-time to display, or null to clear.\n */\n setValue(value: Date | null): this {\n this.getRenderer().setValue(value);\n\n return this;\n }\n\n /**\n * Commits the active edit, but reverts when the user typed something the editor could not\n * parse so the underlying record is not blanked.\n *\n * @returns This cell, for method chaining.\n */\n commitEdit(): this {\n const editor = this._activeEditor as DateTimeEditor | null;\n\n if (editor && !editor.isEmpty() && editor.getValue() === null) {\n this.cancelEdit();\n\n return this;\n }\n\n super.commitEdit();\n\n return this;\n }\n}\n\nconst DateTimeCellCallable = callable(DateTimeCell);\ntype DateTimeCellCallable = DateTimeCell;\nexport {\n DateTimeCell as _DateTimeCell,\n DateTimeCellCallable as DateTimeCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport { GlyphRenderer } from \"~/component/table/cell/renderer/Glyph.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A read-only table cell whose value is a registry glyph name.\n *\n * Uses a [`GlyphRenderer`](/api/component/table/classes/GlyphRenderer) for display.\n * No editor counterpart — editing a glyph by typing into a cell is not supported.\n *\n * @category Components\n */\nclass GlyphCell extends Cell<String | null> {\n\n constructor() {\n let renderer = new GlyphRenderer();\n\n super(\"td\", renderer);\n }\n\n /**\n * Sets the displayed glyph name on the renderer. `null`, `undefined`,\n * and any falsy value remove the glyph child so the cell renders\n * blank.\n *\n * @param value - The registry glyph name to display, or\n * `null`/`undefined`/falsy to clear the cell.\n */\n setValue(value: String | null): this {\n this.getRenderer().setValue(value);\n\n return this;\n }\n}\n\nconst GlyphCellCallable = callable(GlyphCell);\ntype GlyphCellCallable = GlyphCell;\nexport {\n GlyphCell as _GlyphCell,\n GlyphCellCallable as GlyphCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport { ComboRenderer } from \"~/component/table/cell/renderer/Combo.js\";\nimport type { ComboOption } from \"~/component/table/ColumnConfig.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A table cell for constrained-choice (combo-box) columns.\n *\n * Activated when a column declares `ColumnConfig.values`, regardless of the\n * field's declared type. Displays the chosen option's label via a\n * [`ComboRenderer`](/api/component/table/classes/ComboRenderer) and borrows a\n * per-column [`ComboEditor`](/api/component/table/classes/ComboEditor) from the\n * body's {@link CellEditorPool} on edit. The editor key is namespaced by field\n * (`combo:<field>`) so each combo column keeps its own option set rather than\n * sharing one pooled editor.\n *\n * @category Components\n */\nclass ComboCell extends Cell<String | null> {\n\n private _field: string;\n\n /**\n * @param field - The model field name this column presents; used to\n * namespace the pooled editor key.\n * @param optionList - The column's option set, forwarded to the renderer\n * so it can map stored values to display labels.\n */\n constructor(field: string, optionList: Array<ComboOption | string>) {\n super(\"td\", new ComboRenderer(optionList));\n\n this._field = field;\n }\n\n /**\n * Returns the per-column pool key for this cell's shared\n * [`ComboEditor`](/api/component/table/classes/ComboEditor).\n *\n * @returns The string `` `combo:${field}` ``.\n */\n getEditorKey(): string {\n return `combo:${this._field}`;\n }\n\n /**\n * Sets the displayed value on the renderer, which maps it to the\n * matching option label. `null` and `undefined` render the cell blank.\n *\n * @param value - The option value to display, or `null`/`undefined` to\n * clear the cell.\n */\n setValue(value: String | null): this {\n this.getRenderer().setValue(value);\n\n return this;\n }\n}\n\nconst ComboCellCallable = callable(ComboCell);\ntype ComboCellCallable = ComboCell;\nexport {\n ComboCell as _ComboCell,\n ComboCellCallable as ComboCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { Absolute } from \"~/layout/Absolute.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { caret_down } from \"~/glyphs/solid/caret_down.js\";\nimport { caret_right } from \"~/glyphs/solid/caret_right.js\";\n\nGlyph.register(caret_down, caret_right);\n\n/** Width in pixels reserved for the expand/collapse toggle glyph. */\nexport const TOGGLE_WIDTH = 20;\n\n/** Default pixels of indentation added per depth level. */\nexport const DEFAULT_INDENT_PX = 16;\n\n/**\n * A {@link CellRenderer} wrapper that prepends an indent column and an\n * expand/collapse toggle in front of a delegate renderer.\n *\n * The delegate is the typed renderer that would have been used for the\n * column's field type ({@link StringRenderer},\n * [`NumberRenderer`](/api/component/table/classes/NumberRenderer), …). It is\n * adopted as the tree renderer's only data-bearing child and handles\n * `getValue` / `setValue` unchanged. The toggle is a {@link Glyph}\n * rebuilt on each {@link setTreeState} call, mirroring the swap pattern\n * used by [`TreeRow.setRowData`](/api/component/tree/classes/TreeRow#setrowdata).\n *\n * Layout is absolute: the toggle (when present) is placed at\n * `depth * indentPx`, and the delegate immediately to its right —\n * filling the remaining width. The renderer's own insets are zero; the\n * delegate carries the cell padding inherited from its own\n * {@link CellRenderer} base.\n *\n * Toggle click routing is intentionally absent here. The host\n * `TreeBody` owns one subtree-click listener for the whole body; it\n * inspects the click target and, when it matches a pool row's toggle\n * element, calls {@link TreeBody.setExpanded} directly. This keeps\n * tree-cell renderers free of click handlers and matches the way the\n * existing `Tree` component routes toggle clicks.\n *\n * @category Components\n */\nclass TreeCellRenderer<T> extends CellRenderer<T> {\n\n declare private _delegate: CellRenderer<T>;\n declare private _indentPx: number;\n private _toggle: Glyph | null = null;\n private _depth: number = 0;\n private _hasChildren: boolean = false;\n private _expanded: boolean = false;\n\n /**\n * Constructs a tree-cell renderer wrapping the given delegate.\n *\n * @param delegate - The typed renderer that draws the cell value to\n * the right of the toggle. Adopted as a child of this renderer; do\n * not also add it to another parent.\n * @param indentPx - Pixels of indentation per depth level. Defaults\n * to 16 px.\n */\n constructor(delegate: CellRenderer<T>, indentPx: number = DEFAULT_INDENT_PX) {\n super();\n\n this._delegate = delegate;\n this._indentPx = indentPx;\n\n // Override CellRenderer's default Fit layout — we place the\n // toggle and delegate at explicit x positions in `doLayout`.\n this.setLayoutManager(new Absolute());\n\n // Zero out the cell padding that CellRenderer's base set during\n // super(); the delegate is itself a CellRenderer and owns the\n // padding, so doubling it would over-indent the content by one\n // cell-padding width per row. Call the inherited setter through\n // `super` to bypass our own forwarding override.\n super.setInsets(new Insets(0, 0, 0, 0));\n\n this.addComponent(delegate);\n }\n\n /**\n * Returns the delegate renderer that draws the cell's value.\n *\n * @returns The wrapped {@link CellRenderer}.\n */\n getDelegate(): CellRenderer<T> {\n return this._delegate;\n }\n\n /**\n * Returns the toggle glyph currently rendered, or `null` for a leaf\n * row. The host `TreeBody` reads this to dispatch toggle clicks\n * from its subtree-click listener.\n *\n * @returns The toggle {@link Glyph}, or `null` when the row has no children.\n */\n getToggle(): Glyph | null {\n return this._toggle;\n }\n\n /**\n * Returns the current depth level (0 = root).\n *\n * @returns The zero-based depth.\n */\n getDepth(): number {\n return this._depth;\n }\n\n /**\n * Returns the x offset at which this renderer's actual content\n * (the wrapped delegate) begins — `depth * indentPx + TOGGLE_WIDTH`.\n * Used by the owning {@link Cell} on `doLayout` to keep the\n * editor visually aligned with the delegate when the cell enters\n * edit mode.\n *\n * @returns The pixels reserved on the left for the indent +\n * toggle column.\n */\n getContentX(): number {\n return this._depth * this._indentPx + TOGGLE_WIDTH;\n }\n\n /**\n * Returns the value rendered by the delegate.\n *\n * @returns The current cell value.\n */\n getValue(): T {\n return this._delegate.getValue();\n }\n\n /**\n * Sets the value rendered by the delegate.\n *\n * @param value - The cell value to display.\n */\n setValue(value: T): this {\n this._delegate.setValue(value);\n\n return this;\n }\n\n /**\n * Updates the tree state for this slot — depth, whether the bound\n * record has children, and whether it is currently expanded. The\n * toggle glyph is created or removed as needed, and the renderer's\n * own `doLayout` runs so the toggle and delegate snap to their new\n * positions.\n *\n * Idempotent — a call with the same triple is a no-op.\n *\n * @param depth - Zero-based nesting depth.\n * @param hasChildren - Whether the bound record has children\n * (controls whether a toggle is rendered).\n * @param expanded - Whether the bound record is currently expanded\n * (controls the toggle glyph: `caret-down` vs `caret-right`).\n *\n * @returns This renderer, for method chaining.\n */\n setTreeState(depth: number, hasChildren: boolean, expanded: boolean): this {\n if (this._depth === depth\n && this._hasChildren === hasChildren\n && this._expanded === expanded) {\n return this;\n }\n\n this._depth = depth;\n this._hasChildren = hasChildren;\n this._expanded = expanded;\n\n this.refreshToggle();\n this.doLayout();\n\n return this;\n }\n\n /**\n * Forwards insets writes to the delegate. The tree-cell wrapper\n * itself stays insets-zero — its only role is to inject indent +\n * toggle space; the delegate owns the cell-padding contract that\n * CellRenderer's base assumes.\n *\n * The first call lands during `super()` of this renderer's\n * constructor (`CellRenderer`'s `applyPadding` runs before our\n * `_delegate` field is assigned). That call is swallowed by the\n * optional chain; subsequent calls — including theme-change\n * re-applications and explicit consumer writes — propagate to\n * the delegate.\n *\n * @param insets - The insets to apply to the delegate.\n */\n setInsets(insets: Insets): this {\n this._delegate?.setInsets(insets);\n\n return this;\n }\n\n /**\n * Rebuilds the toggle glyph from `_hasChildren` and `_expanded`.\n * A leaf row has no toggle. A branch row swaps in a fresh\n * `caret-down` (expanded) or `caret-right` (collapsed) glyph —\n * matches [`TreeRow.setRowData`](/api/component/tree/classes/TreeRow#setrowdata)'s\n * swap pattern (no in-place name mutation API on `Glyph`).\n */\n private refreshToggle(): void {\n if (this._toggle) {\n this.removeComponent(this._toggle);\n this._toggle = null;\n }\n\n if (!this._hasChildren) {\n return;\n }\n\n const toggle = new Glyph(this._expanded ? \"caret-down\" : \"caret-right\");\n\n toggle.setCursor(\"pointer\");\n toggle.clearInsets();\n toggle.getAria().setHidden(true);\n\n this._toggle = toggle;\n this.addComponent(toggle);\n }\n\n /**\n * Positions the toggle (when present) at `depth * indentPx` and\n * places the delegate immediately to its right, filling the\n * remaining width. Runs the inherited `super.doLayout` so the\n * Absolute layout manager commits the bounds we set on the\n * children.\n *\n * @returns This renderer, for method chaining.\n */\n doLayout(): this {\n const width = this.getWidth() || 0;\n const height = this.getHeight() || 0;\n const indent = this._depth * this._indentPx;\n\n if (this._toggle) {\n // The caret is a rigid SVG Glyph (min/max pinned to its 16×16\n // preferredSize), so it cannot fill the taller row — centre it\n // geometrically instead of relying on line-height (a no-op for SVG).\n const glyphHeight = this._toggle.getPreferredSize()?.height ?? height;\n this._toggle.setAutoCommitStyle(false);\n this._toggle.setX(indent);\n this._toggle.setY(Math.max(0, (height - glyphHeight) / 2));\n this._toggle.setWidth(TOGGLE_WIDTH);\n this._toggle.setHeight(glyphHeight);\n this._toggle.setAutoCommitStyle(true);\n }\n\n const delegateX = indent + TOGGLE_WIDTH;\n const delegateW = Math.max(0, width - delegateX);\n\n this._delegate.setAutoCommitStyle(false);\n this._delegate.setX(delegateX);\n this._delegate.setY(0);\n this._delegate.setWidth(delegateW);\n this._delegate.setHeight(height);\n this._delegate.setAutoCommitStyle(true);\n\n // The inherited `super.doLayout` (CellRenderer → Component →\n // Absolute layout manager) commits each child's bounds via\n // `LayoutManager.commitBounds`, which already calls\n // `component.doLayout()` on every placed child. The delegate\n // and the toggle therefore receive their own `doLayout` once\n // during this call — no need to call `_delegate.doLayout()`\n // again here.\n super.doLayout();\n\n return this;\n }\n}\n\nconst TreeCellRendererCallable = callable(TreeCellRenderer);\ntype TreeCellRendererCallable<T> = TreeCellRenderer<T>;\nexport {\n TreeCellRenderer as _TreeCellRenderer,\n TreeCellRendererCallable as TreeCellRenderer\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport { AbstractModel } from \"~/data/AbstractModel.js\";\nimport { Field } from \"~/data/Field.js\";\nimport { ModelRecord } from \"~/data/ModelRecord.js\";\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport { DynamicCell } from \"~/component/table/cell/Dynamic.js\";\nimport { DefaultCell } from \"~/component/table/cell/Default.js\";\nimport { StringCell } from \"~/component/table/cell/String.js\";\nimport { BooleanCell } from \"~/component/table/cell/Boolean.js\";\nimport { NumberCell } from \"~/component/table/cell/Number.js\";\nimport { DateCell } from \"~/component/table/cell/Date.js\";\nimport { TimeCell } from \"~/component/table/cell/Time.js\";\nimport { DateTimeCell } from \"~/component/table/cell/DateTime.js\";\nimport { GlyphCell } from \"~/component/table/cell/Glyph.js\";\nimport { ComboCell } from \"~/component/table/cell/Combo.js\";\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { TreeCellRenderer } from \"~/component/table/cell/renderer/TreeCell.js\";\nimport type { ColumnConfig } from \"~/component/table/ColumnConfig.js\";\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A single data row in the table, rendered as a `<tr>` element.\n *\n * Creates one typed cell ({@link StringCell}, {@link NumberCell}, {@link BooleanCell},\n * or {@link DefaultCell}) per model field and binds each cell's commit callback to the\n * corresponding field on the bound {@link ModelRecord}.\n *\n * Re-exported as `TableRow` from the package barrel.\n *\n * @category Components\n */\nclass Row extends Component {\n\n private _model?: AbstractModel;\n private _data?: ModelRecord;\n private _onCellCommit?: (record: ModelRecord) => void;\n private _fieldNames: string[] = [];\n private _treeCell: Cell<any> | null = null;\n private _stripe: boolean = false;\n\n constructor(\n model?: AbstractModel,\n data?: ModelRecord,\n hiddenColumns: Set<string> = new Set(),\n columnConfigs: Map<string, ColumnConfig> = new Map(),\n onCellCommit?: (record: ModelRecord) => void,\n treeFieldName?: string,\n ) {\n super({ tag: \"tr\" });\n\n this.getAria().setRole(\"row\");\n\n this._model = model;\n this._data = data;\n this._onCellCommit = onCellCommit;\n\n if (this._model) {\n let fields = this._model.getFields()\n .filter(f => !hiddenColumns.has(f.getName()))\n .sort((f1, f2) => f1.getOrder() - f2.getOrder());\n\n this._fieldNames = fields.map(f => f.getName());\n\n for (let idx in fields) {\n let field = fields[idx];\n let cell = Row.createCellForField(field, columnConfigs);\n\n this.bindCell(cell, this._data, field.getName());\n cell.on(\"commit\", (newValue) => {\n if (this._data) {\n this._data.set(field.getName(), newValue);\n this._onCellCommit?.(this._data);\n }\n this.updateVisualState();\n });\n\n // Tint the cell with the column's `groupColor` so a body\n // cell visually belongs to the same group as the parent\n // header above it. `Cell` base only re-applies the\n // border on theme change, so this background sticks.\n const groupColor = columnConfigs.get(field.getName())?.groupColor;\n if (groupColor) {\n cell.setBaseBackground(groupColor);\n }\n\n // Read-only wins over groupColor: this write lands\n // after the groupColor block so the read-only tint\n // overrides any group tint and the cell refuses inline\n // editing.\n const readOnly = columnConfigs.get(field.getName())?.readOnly;\n if (readOnly) {\n cell.setReadOnly(true);\n }\n\n // For the tree column, wrap the typed renderer in a\n // `TreeCellRenderer` so the cell draws an indent + an\n // expand/collapse toggle to the left of the value. The\n // host `TreeBody` keeps a reference via `getTreeCell()`\n // and pushes per-row depth + expansion state through\n // `setTreeState` on each bind.\n if (treeFieldName !== undefined && field.getName() === treeFieldName) {\n cell.wrapRenderer((delegate: CellRenderer<any>) => new TreeCellRenderer(delegate));\n this._treeCell = cell;\n }\n\n this.addComponent(cell, {\n data: field\n });\n }\n }\n }\n\n /**\n * Returns the cell on the row's tree column, or `null` when the row\n * was constructed without a `treeFieldName`. The host `TreeBody`\n * reads this to find each row's `TreeCellRenderer` for depth /\n * toggle updates and toggle-click routing.\n *\n * @returns The tree-column {@link Cell}, or `null`.\n */\n getTreeCell(): Cell<any> | null {\n return this._treeCell;\n }\n\n /**\n * Returns the field names backing this row's cells, in the same\n * order as `getComponents()`. Hidden columns are excluded.\n *\n * @returns The field names, in cell order.\n *\n * @remarks Used by the host `Body` to align cell index → field name\n * → {@link ColumnConfig} lookup when resolving per-cell read-only\n * state on each rebind. Not for consumer use.\n */\n getFieldNames(): string[] {\n return this._fieldNames;\n }\n\n /**\n * Returns the ModelRecord currently bound to this row.\n *\n * @returns The bound {@link ModelRecord}, or undefined if none has been set.\n */\n getData() {\n return this._data;\n }\n\n /**\n * Rebinds all cells to a new record, updating their displayed values.\n *\n * @param record - The new record to bind to this row.\n */\n setData(record: ModelRecord) : this {\n this._data = record;\n\n const cells = this.getComponents() as Cell<any>[];\n const names = this._fieldNames;\n\n for (let i = 0; i < names.length; i++) {\n this.bindCell(cells[i], record, names[i]);\n }\n\n this.updateVisualState();\n\n return this;\n }\n\n /**\n * Marks whether this row sits on a striped (odd) logical index, so the\n * resting background paints the zebra stripe.\n *\n * @param striped - True when this row's logical data index is odd.\n *\n * @remarks Set by the host Body on each rebind from `dataIndex % 2`; it only\n * updates the backing flag, so call `updateVisualState` afterwards to repaint.\n * Not for consumer use.\n */\n setStripe(striped: boolean): void {\n this._stripe = striped;\n }\n\n /**\n * Applies a background color based on the record's new/dirty/clean state.\n *\n * @remarks New records get a green tint, dirty records an orange tint, and clean records the zebra stripe (odd rows) or no tint (even rows).\n */\n updateVisualState(): void {\n const el = this.getElement();\n if (!el) {\n return;\n }\n\n // Per-record ephemeral tint on a pooled row re-bound to a new record on\n // every render. Going through a cached Component setter (setBackgroundColor)\n // would persist this into _options and replay it onto the next record bound\n // to this reused row, so write/remove the inline style directly instead.\n if (this._data?.isNew()) {\n DOM.sink.apply(el, { style: { 'background-color': 'var(--ts-ui-table-row-new, rgba(70, 200, 70, 0.15))' } });\n } else if (this._data?.isDirty()) {\n DOM.sink.apply(el, { style: { 'background-color': 'var(--ts-ui-table-row-dirty, rgba(255, 165, 0, 0.15))' } });\n } else if (this._stripe) {\n DOM.sink.apply(el, { style: { 'background-color': 'var(--ts-ui-table-row-stripe, rgba(0, 0, 0, 0.035))' } });\n } else {\n DOM.sink.apply(el, { style: { 'background-color': null } });\n }\n }\n\n /**\n * Appends a cell component to this row.\n *\n * @param cell - The cell to append.\n * @param constraints - Optional. Layout constraints for the cell.\n */\n addColumn(cell: Cell<any>, constraints?: LayoutConstraints) : this {\n this.addComponent(cell, constraints);\n\n return this;\n }\n\n /**\n * Adds a cell as a child component of this row.\n *\n * @param cell - The cell component to add.\n * @param constraints - Optional. Layout constraints for the cell.\n *\n * @returns This component, for method chaining.\n */\n addComponent(cell: Cell<any>, constraints?: LayoutConstraints): this {\n super.addComponent(cell, constraints);\n\n return this;\n }\n\n /**\n * Rebuilds this row's cell set in place to match `model`'s currently-\n * visible fields. Cells whose field is still visible are preserved\n * (along with their renderer, editor, theme listener, sort state,\n * group tint, etc.); cells whose field is now hidden are committed\n * (if editing) and removed; cells for newly-visible fields are\n * constructed via the same typed switch as the constructor.\n *\n * The child order is re-sorted to match the visible-field display\n * order. The tree column's cell, if any, is wrapped in a\n * {@link TreeCellRenderer} only on first creation — surviving tree\n * cells keep the renderer they already have.\n *\n * @param model - The model whose visible fields drive the cell list.\n * @param hiddenColumns - The set of field names to exclude.\n * @param columnConfigs - Per-field configs (carries `showSeconds`,\n * `groupColor`, etc.).\n * @param treeFieldName - Optional. Field name of the column that\n * carries the tree-cell renderer; matches the constructor's\n * parameter of the same name.\n *\n * @returns This row, for method chaining.\n */\n syncCells(\n model: AbstractModel,\n hiddenColumns: Set<string>,\n columnConfigs: Map<string, ColumnConfig>,\n treeFieldName?: string,\n ): this {\n this._model = model;\n\n const targetFields = model.getFields()\n .filter(f => !hiddenColumns.has(f.getName()))\n .sort((f1, f2) => f1.getOrder() - f2.getOrder());\n\n const existing = this.getComponents().slice() as Cell<any>[];\n const byName = new Map<string, Cell<any>>();\n\n for (const cell of existing) {\n const lc = this.getLayoutConstraints(cell);\n const field = lc?.data as Field | undefined;\n\n if (field) {\n byName.set(field.getName(), cell);\n }\n }\n\n // Remove cells whose field is no longer visible. Commit any\n // in-flight edit before discarding so user keystrokes land on\n // the record (mirrors the blur-commits-edit contract).\n const targetNames = new Set(targetFields.map(f => f.getName()));\n\n for (const cell of existing) {\n const lc = this.getLayoutConstraints(cell);\n const field = lc?.data as Field | undefined;\n\n if (!field || !targetNames.has(field.getName())) {\n if (cell.isEditing()) {\n cell.commitEdit();\n }\n\n this.removeComponent(cell);\n }\n }\n\n // Walk target fields in display order. Build any missing cell;\n // re-apply the commit wire and group tint on every (new and\n // surviving) cell so a config swap that changed the tint also\n // takes effect.\n this._treeCell = null;\n\n for (const field of targetFields) {\n const fieldName = field.getName();\n let cell = byName.get(fieldName);\n\n // A config change can flip a column between its combo cell and\n // its field-type-driven cell. Discard a surviving cell whose\n // kind no longer matches the current config so it rebuilds with\n // the right renderer + editor; commit any in-flight edit first.\n const wantsCombo = (columnConfigs.get(fieldName)?.values?.length ?? 0) > 0;\n const wantsDynamic = !!columnConfigs.get(fieldName)?.cellType;\n\n if (cell && (wantsCombo !== (cell instanceof ComboCell) || wantsDynamic !== (cell instanceof DynamicCell))) {\n if (cell.isEditing()) {\n cell.commitEdit();\n }\n\n this.removeComponent(cell);\n cell = undefined;\n }\n\n const isNew = !cell;\n\n if (!cell) {\n cell = Row.createCellForField(field, columnConfigs);\n }\n\n cell.on(\"commit\", (newValue) => {\n if (this._data) {\n this._data.set(fieldName, newValue);\n this._onCellCommit?.(this._data);\n }\n this.updateVisualState();\n });\n\n const groupColor = columnConfigs.get(fieldName)?.groupColor;\n\n if (groupColor) {\n cell.setBaseBackground(groupColor);\n }\n\n // Wrap the tree-column cell only when it's newly created —\n // a surviving tree cell already has its `TreeCellRenderer`,\n // and re-wrapping would chain renderers and double-register\n // a theme listener per toggle.\n if (isNew && treeFieldName !== undefined && fieldName === treeFieldName) {\n cell.wrapRenderer((delegate: CellRenderer<any>) => new TreeCellRenderer(delegate));\n }\n\n if (treeFieldName !== undefined && fieldName === treeFieldName) {\n this._treeCell = cell;\n }\n\n if (isNew) {\n this.addComponent(cell, { data: field });\n this.bindCell(cell, this._data, fieldName);\n }\n }\n\n // Re-order children to the new visible-field order. Mirrors\n // `Header.sortColumns` — same `Field` payload from the layout\n // constraints drives both.\n this.sortComponents((c1, c2) => {\n const f1 = (this.getLayoutConstraints(c1)?.data as Field).getOrder();\n const f2 = (this.getLayoutConstraints(c2)?.data as Field).getOrder();\n\n return f1 - f2;\n });\n\n this._fieldNames = targetFields.map(f => f.getName());\n\n return this;\n }\n\n /**\n * Routes a cell's value-set for one field: a {@link DynamicCell} resolves\n * its per-record variant via `bindRecord`, while every other cell keeps\n * the plain `setValue` path.\n *\n * @param cell - The cell to bind.\n * @param record - The record to bind, or undefined to clear the cell.\n * @param fieldName - The field this cell presents.\n */\n private bindCell(cell: Cell<any>, record: ModelRecord | undefined, fieldName: string): void {\n if (cell instanceof DynamicCell && record) {\n cell.bindRecord(record);\n } else {\n cell.setValue(record ? record.get(fieldName) : undefined);\n }\n }\n\n /**\n * Builds the typed `Cell` for `field`, applying any field-specific\n * options from `columnConfigs` (e.g. `showSeconds` on time cells).\n *\n * @param field - The field whose typed cell to construct.\n * @param columnConfigs - Per-field configs keyed by field name.\n *\n * @returns A new typed cell matching `field.getType()`.\n */\n private static createCellForField(field: Field, columnConfigs: Map<string, ColumnConfig>): Cell<any> {\n const config = columnConfigs.get(field.getName());\n\n // A custom renderer wins over both the combo (`values`) routing and the\n // field-type switch. The cell is built with no editor, so it stays\n // display-only — a click never enters edit mode (Cell.startEdit bails\n // with no editor and no pool key). The base Cell.setValue forwards each\n // rebind to the renderer.\n if (config?.renderer) {\n return new Cell(\"td\", config.renderer());\n }\n\n if (config?.cellType) {\n return new DynamicCell(field.getName(), field.getType(), config);\n }\n\n const values = config?.values;\n\n if (values && values.length > 0) {\n return new ComboCell(field.getName(), values);\n }\n\n switch (field.getType()) {\n case \"string\":\n return new StringCell();\n case \"number\":\n return new NumberCell();\n case \"boolean\":\n return new BooleanCell();\n case \"date\":\n return new DateCell();\n case \"time\":\n return new TimeCell(columnConfigs.get(field.getName())?.showSeconds ?? false);\n case \"datetime\":\n return new DateTimeCell(columnConfigs.get(field.getName())?.showSeconds ?? false);\n case \"glyph\":\n return new GlyphCell();\n default:\n return new DefaultCell();\n }\n }\n\n /**\n * No-op; cell layout is driven by the Body's renderWindow.\n *\n * @returns This component, for method chaining.\n */\n doLayout(): this {\n return this;\n }\n}\n\nconst RowCallable = callable(Row);\ntype RowCallable = Row;\nexport {\n Row as _Row,\n RowCallable as Row\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { StyleRule } from \"~/core/StyleTarget.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * String-literal union of the events emitted by {@link ResizeHandle}.\n *\n * @category Components\n */\ntype ResizeHandleEvent = \"dragstart\" | \"dragmove\" | \"dragend\";\n\n/**\n * Construction-time options for {@link ResizeHandle}.\n *\n * @category Components\n */\ninterface ResizeHandleOptions extends ComponentOptions {\n /**\n * Multi-event listener bag dispatched to {@link ResizeHandle.on} at\n * construction time. Each entry is appended; calling `on(event, fn)`\n * later registers another listener for the same event.\n */\n listeners?: {\n dragstart?: (event: MouseEvent) => void;\n dragmove?: (clientX: number) => void;\n dragend?: () => void;\n };\n}\n\n/**\n * The handle's themeable resize cursor. Exported so the host header can hold the\n * same cursor for the duration of a column drag, when the handle itself is out\n * of hit-testing and can no longer resolve it.\n */\nconst RESIZE_HANDLE_CURSOR = \"var(--ts-ui-table-resize-handle-cursor, ew-resize)\";\n\nlet _classRule: StyleRule | null = null;\n\n/**\n * Registers the shared `.ResizeHandle` class rule once on first use. The rule\n * holds the absolute-position box geometry (position, top, right, width,\n * height, z-index) so per-instance setters only carry per-instance state.\n *\n * Idempotent and module-local; safe across hot reloads.\n */\nfunction ensureResizeHandleClassRule(): void {\n if (_classRule) {\n return;\n }\n\n _classRule = new StyleRule({\n scope: \"class\",\n name: \"ResizeHandle\",\n styles: {\n position: \"absolute\",\n top: \"0\",\n right: \"0\",\n width: \"var(--ts-ui-table-resize-handle-width,5px)\",\n height: \"100%\",\n zIndex: \"1\",\n },\n });\n}\n\n/**\n * A thin draggable handle anchored to the right edge of a table header cell.\n *\n * Lives as a side-loaded overlay on the `<th>` (its `position:absolute` plus\n * the host cell's [`Card`](/api/layout/classes/Card) layout keep it out of the\n * cell renderer's flow). Owns its own click + mousedown listeners via\n * {@link Event.addListener}. Drag-phase mousemove/mouseup listeners live with\n * the host so that the host can suppress the synthesized post-drag click.\n *\n * The static box geometry (position, top, right, width, height, z-index)\n * lives in a shared `.ResizeHandle` class rule registered on first use. The\n * cursor and the indicator gradient are per-instance values written through\n * typed Component setters in the constructor.\n *\n * @category Components\n */\nclass ResizeHandle extends Component<ResizeHandleOptions> {\n\n private _listeners: ListenerBag<ResizeHandleEvent> = new ListenerBag<ResizeHandleEvent>();\n\n /**\n * Constructs a resize handle. Callbacks default to none; consumers\n * register them via {@link on} or pass them through `options.listeners`.\n *\n * @param options - Optional configuration bag (drag listeners plus common\n * Component fields).\n */\n constructor(options?: ResizeHandleOptions) {\n ensureResizeHandleClassRule();\n\n super({ tag: \"div\", ...(options ?? {}) });\n\n this.setCursor(RESIZE_HANDLE_CURSOR);\n // 5 px-wide drag target with a 1 px colored stripe at the right\n // edge — `80%` of `5 px = 4 px` transparent, the remaining `20%`\n // is `1 px` of the resize-handle colour. Pairs with the 1 px\n // dividers on ParentHeaderCell so every visible cell separator\n // in the header band reads at the same thickness.\n this.setBackgroundImage(\n \"linear-gradient(to right,transparent 80%,\" +\n \"var(--ts-ui-table-resize-handle-color,rgba(0,0,0,0.2)) 80%)\");\n this.setZIndex(1);\n\n // Listener wiring must happen here, NOT inside `applyOptions` —\n // Component's constructor invokes `applyOptions` synchronously\n // from inside super(), at which point the class-field\n // `_listeners` initializer has not yet run (class-field\n // initialisers fire between super() returning and the next\n // statement here). Wiring after super() guarantees `_listeners`\n // is the live `ListenerBag` instance.\n this.applyListeners(options?.listeners);\n }\n\n /**\n * Wires the framework-routed mousedown + click listeners after the element\n * has rendered. Mousedown fires the registered drag-start listeners; click\n * is intercepted to prevent a sort from firing on the host header cell.\n *\n * @param element - Optional element passed from the framework init chain.\n * @returns This component, for method chaining.\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n Event.addListener(this, \"mousedown\", this._onMouseDown);\n Event.addListener(this, \"click\", this._onClick);\n\n return this;\n }\n\n /**\n * Registers a listener for one of this handle's drag events.\n *\n * @param event - `\"dragstart\"` fires on mousedown over the handle, carrying\n * the originating `MouseEvent` (whose `clientX` seeds the drag origin);\n * `\"dragmove\"` fires on each viewport mousemove during a drag with the\n * absolute pointer `clientX`; `\"dragend\"` fires when the viewport mouseup\n * observes the release.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This handle, for method chaining.\n */\n on(event: \"dragstart\", listener: (e: MouseEvent) => void): this;\n on(event: \"dragmove\", listener: (clientX: number) => void): this;\n on(event: \"dragend\", listener: () => void): this;\n on(event: ResizeHandleEvent, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered listener. The exact callback reference\n * must match.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This handle, for method chaining.\n */\n off(event: ResizeHandleEvent, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every listener registered for `event` with `payload`, in\n * registration order. Internal use only — outer hosts call the public\n * verbs {@link dragMove} / {@link dragEnd} which forward to `emit`.\n *\n * @param event - The event to emit.\n * @param payload - Forwarded to each listener.\n */\n protected emit(event: \"dragstart\", e: MouseEvent): void;\n protected emit(event: \"dragmove\", clientX: number): void;\n protected emit(event: \"dragend\"): void;\n protected emit(event: ResizeHandleEvent, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Drives the `\"dragmove\"` event from the host's viewport-mousemove\n * listener. The host extracts `clientX` and forwards it here; this\n * method fires `\"dragmove\"` listeners with that absolute coordinate.\n *\n * @param clientX - The absolute pointer `clientX` for this mousemove tick.\n */\n dragMove(clientX: number): void {\n this.emit(\"dragmove\", clientX);\n }\n\n /**\n * Drives the `\"dragend\"` event from the host's viewport-mouseup\n * listener.\n */\n dragEnd(): void {\n this.emit(\"dragend\");\n }\n\n /**\n * Bound mousedown handler — fires the `\"dragstart\"` event with the\n * originating MouseEvent, then consumes the mousedown. A mousedown on\n * the handle always starts a drag, so this always returns `true`; that\n * also preserves the walk-skip that `Header.onResizeDragStart` (reached\n * through the `\"dragstart\"` event) relied on the removed\n * `stopPropagation` monkey-patch to produce.\n *\n * @returns `true`, consuming the press so it does not also reach the header's own sort handler.\n */\n private _onMouseDown = (e: MouseEvent): Event.ListenerResult => {\n this.emit(\"dragstart\", e);\n\n return true;\n };\n\n /**\n * Bound click handler — swallows the click so the host header cell does\n * not interpret it as a sort.\n *\n * @returns `true`, consuming the click so a resize gesture never registers as a sort.\n */\n private _onClick = (): Event.ListenerResult => {\n return true;\n };\n}\n\nconst ResizeHandleCallable = callable(ResizeHandle);\ntype ResizeHandleCallable = ResizeHandle;\nexport {\n ResizeHandle as _ResizeHandle,\n ResizeHandleCallable as ResizeHandle,\n RESIZE_HANDLE_CURSOR\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { StyleRule } from \"~/core/StyleTarget.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\n\n/**\n * Construction-time options for {@link SortPriorityBadge}.\n *\n * @category Components\n */\ninterface SortPriorityBadgeOptions extends ComponentOptions {\n priority?: number | null;\n}\n\nlet _classRule: StyleRule | null = null;\n\n/**\n * Registers the shared `.SortPriorityBadge` class rule once on first use. The\n * rule holds the absolute-position overlay geometry (position, top, right,\n * font-size, line-height, border-radius, padding, pointer-events) so\n * per-instance setters only carry per-instance state (background, color,\n * visibility, text).\n *\n * Idempotent and module-local; safe across hot reloads.\n */\nfunction ensureSortBadgeClassRule(): void {\n if (_classRule) {\n return;\n }\n\n _classRule = new StyleRule({\n scope: \"class\",\n name: \"SortPriorityBadge\",\n styles: {\n position: \"absolute\",\n top: \"2px\",\n right: \"8px\",\n fontSize: \"var(--ts-ui-sort-badge-font-size, var(--ts-ui-font-size))\",\n lineHeight: \"1\",\n borderRadius: \"3px\",\n padding: \"1px 3px\",\n pointerEvents: \"none\",\n },\n });\n}\n\n/**\n * A small numeric badge anchored to the top-right corner of a table header\n * cell, used to surface the multi-sort priority (1-based) of that column.\n *\n * Lives as a side-loaded overlay on the `<th>` (its `position:absolute` plus\n * the host cell's [`Card`](/api/layout/classes/Card) layout keep it out of\n * the cell renderer's flow). The badge stays hidden for priority `null`,\n * `0`, and `1` — the leading sort needs no number because the arrow on the\n * header label already conveys direction.\n *\n * The static overlay geometry lives in a shared `.SortPriorityBadge` class\n * rule registered on first use. The background and foreground colours are\n * per-instance values written through typed Component setters in the\n * constructor.\n *\n * @category Components\n */\nclass SortPriorityBadge extends Component<SortPriorityBadgeOptions> {\n\n declare private _priority: number | null;\n\n /**\n * Constructs a sort-priority badge. The badge starts hidden until\n * {@link setPriority} writes a priority of 2 or greater.\n *\n * @param options - Optional configuration bag (initial priority plus\n * common Component fields).\n */\n constructor(options?: SortPriorityBadgeOptions) {\n ensureSortBadgeClassRule();\n\n super({ tag: \"span\", ...(options ?? {}) });\n\n this._priority ??= null;\n\n this.setBackgroundColor(\"var(--ts-ui-sort-badge-bg, rgba(0,0,0,0.15))\");\n this.setForegroundColor(\"var(--ts-ui-sort-badge-color, inherit)\");\n this.setVisible(this._shouldShow(this._priority));\n }\n\n /**\n * Applies a {@link SortPriorityBadgeOptions} bag. The priority field is\n * written pure to the backing field here; inherited Component fields\n * cascade through `super.applyOptions`.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: SortPriorityBadgeOptions): this {\n super.applyOptions(options);\n\n if (options.priority !== undefined) {\n this._priority = options.priority;\n }\n\n return this;\n }\n\n /**\n * Renders the root span and flushes the cached priority into\n * `textContent` and visibility.\n *\n * @returns The rendered root element handle.\n */\n protected render(): Handle {\n const element = super.render();\n\n DOM.sink.apply(element, { text: this._priorityText() });\n\n return element;\n }\n\n /**\n * Returns the priority last written via {@link setPriority}, or `null`.\n *\n * @returns The current priority, or null when none has been set.\n */\n getPriority(): number | null {\n return this._priority;\n }\n\n /**\n * Sets the multi-sort priority. Priorities below 2 collapse to hidden\n * because the leading sort indicator on the header label is sufficient\n * without a numeric badge.\n *\n * @param value - The 1-based priority, or `null` to clear.\n * @returns This component, for method chaining.\n */\n setPriority(value: number | null): this {\n this._priority = value;\n\n const element = this.getElement();\n if (element) {\n DOM.sink.apply(element, { text: this._priorityText() });\n }\n\n this.setVisible(this._shouldShow(value));\n\n return this;\n }\n\n /**\n * Clears the priority (equivalent to `setPriority(null)`).\n *\n * @returns This component, for method chaining.\n */\n clearPriority(): this {\n return this.setPriority(null);\n }\n\n /**\n * Returns the priority rendered as a string, or the empty string when\n * the badge should not display text.\n *\n * @returns The text content for the current priority value.\n */\n private _priorityText(): string {\n return this._shouldShow(this._priority) ? String(this._priority) : \"\";\n }\n\n /**\n * Returns `true` when the given priority should make the badge visible.\n *\n * @param value - The priority value to evaluate.\n * @returns True if a badge with this priority should display.\n */\n private _shouldShow(value: number | null): boolean {\n return value != null && value >= 2;\n }\n}\n\nconst SortPriorityBadgeCallable = callable(SortPriorityBadge);\ntype SortPriorityBadgeCallable = SortPriorityBadge;\nexport {\n SortPriorityBadge as _SortPriorityBadge,\n SortPriorityBadgeCallable as SortPriorityBadge\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { DefaultCell } from \"~/component/table/cell/Default.js\";\nimport { ResizeHandle, RESIZE_HANDLE_CURSOR } from \"~/component/table/cell/ResizeHandle.js\";\nimport { SortPriorityBadge } from \"~/component/table/cell/SortPriorityBadge.js\";\nimport { CellEvent } from \"~/component/table/cell/Cell.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { beginPointerDrag, endPointerDrag } from \"~/core/PointerDrag.js\";\nimport { StyleRule } from \"~/core/StyleTarget.js\";\nimport { Tooltip } from \"~/overlay/Tooltip.js\";\nimport { ThemeManager } from \"~/core/Theme.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * String-literal union of the events emitted by {@link HeaderCell}. Extends\n * the inherited `CellEvent` union with the header-specific events.\n *\n * @category Components\n */\nexport type HeaderCellEvent = CellEvent | \"sortclick\" | \"contextmenu\" | \"resizestart\" | \"resizedrag\";\n\n/**\n * Width (px) used both for the side-loaded `Glyph`'s preferred size and for\n * computing the renderer's left inset when a glyph is mounted.\n */\nconst GLYPH_W = 16;\n\n/**\n * Height (px) used for the side-loaded `Glyph`'s preferred size.\n */\nconst GLYPH_H = 16;\n\n/**\n * Default gap (px) between the cell's left edge and the side-loaded glyph.\n * Mirrors the `--ts-ui-table-header-glyph-gap` token default and is used as\n * the fallback term when computing the renderer's left inset.\n */\nconst GLYPH_GAP = 4;\n\nlet _glyphClassRule: StyleRule | null = null;\n\n/**\n * Registers the shared `.HeaderCellGlyph` class rule once on first use. The\n * rule holds the side-loaded glyph's static placement (position, left, top)\n * so per-instance Component setters only carry transform, size, color, and\n * pointer-events.\n *\n * Idempotent and module-local; safe across hot reloads.\n */\nfunction ensureHeaderCellGlyphClassRule(): void {\n if (_glyphClassRule) {\n return;\n }\n\n _glyphClassRule = new StyleRule({\n scope: \"class\",\n name: \"HeaderCellGlyph\",\n styles: {\n position: \"absolute\",\n left: \"var(--ts-ui-table-header-glyph-gap, 4px)\",\n top: \"50%\",\n },\n });\n}\n\n/**\n * A non-editable header cell rendered as a `<th>` element.\n *\n * Extends {@link DefaultCell} with a sort state indicator (▲/▼ suffix on the\n * label), a click-to-sort callback, and a right-edge drag handle for column\n * resizing. The drag handle and the multi-sort priority badge are dedicated\n * Component subclasses ([`ResizeHandle`](/api/component/table/classes/ResizeHandle)\n * and [`SortPriorityBadge`](/api/component/table/classes/SortPriorityBadge))\n * added through the framework's render lifecycle; both use absolute\n * positioning so they overlay the renderer without disturbing its layout.\n *\n * @category Components\n */\nclass HeaderCell extends DefaultCell {\n\n private _text: String;\n private _fieldName: string;\n private _isDragging: boolean = false;\n private _tooltipText: string = '';\n declare private _resizeHandle: ResizeHandle;\n declare private _priorityBadge: SortPriorityBadge;\n private _sortState: { state: 'asc' | 'desc', priority: number | null } | null = null;\n private _headerGlyph: string | null = null;\n private _headerGlyphInstance: Glyph | null = null;\n private _columnFocused: boolean = false;\n private _required: boolean = false;\n\n /**\n * Creates a header cell with bold text and wires up the sort click listener.\n *\n * @param text - The column title to display.\n * @param fieldName - The model field name used when triggering sort callbacks.\n * @param headerGlyph - Optional registry glyph name mounted to the left of the text.\n */\n constructor(text: String, fieldName: string, headerGlyph?: string | null) {\n super(\"th\");\n\n this.getAria().setRole(\"columnheader\");\n this.getAria().setSort(\"none\");\n\n this._text = text;\n this._fieldName = fieldName;\n this._headerGlyph = headerGlyph ?? null;\n\n let renderer = this.getRenderer();\n renderer.getText().setFontSize(\"--ts-ui-table-header-font-size\");\n renderer.getText().setFontWeight(\"bold\");\n renderer.getText().setText(text);\n\n // DefaultCell's `(tag?: string)` super-signature cannot carry the\n // `styleRules` options bag, so the rule is allocated here via the\n // protected `createStyleRule` builder — same dedupe-and-defer path\n // the options-bag dispatch uses, just spelled imperatively. The\n // render-time `applyStyle` flushes the queued `boxShadow` onto the\n // stylesheet, matching ARCHITECTURE.md's \"construction stays\n // JS-only\" rule.\n this.createStyleRule(\":active\").set(\n \"boxShadow\",\n \"var(--ts-ui-button-pressed-shadow, 1px 2px 5px 0 rgba(0,0,0,0.2) inset)\",\n );\n\n // Wire the resize-handle drag lifecycle: mousedown installs viewport\n // mousemove/mouseup listeners that forward through the handle's\n // events. The `_isDragging` flag straddles the handle and the\n // host's click listener (which suppresses the synthetic post-drag\n // click) so the bookkeeping must live on the host.\n this._resizeHandle = new ResizeHandle({\n listeners: {\n dragstart: (e: MouseEvent) => this.onResizeDragStart(e),\n dragmove : (clientX: number) => this.emit(\"resizedrag\", clientX),\n },\n });\n this._priorityBadge = new SortPriorityBadge();\n }\n\n /**\n * Wires the host click + contextmenu listeners, attaches the tooltip,\n * and mounts the side-loaded glyph if one was supplied at construction.\n *\n * @param element - Optional element passed from the framework init chain.\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement();\n\n if (!el) {\n return this;\n }\n\n // Subtree listener so clicks on any child element (e.g. the Label) bubble up here.\n Event.addSubtreeListener(this, 'click', (e: MouseEvent) => this.onSortClick(e.shiftKey));\n\n Event.addSubtreeListener(this, 'contextmenu', (e: MouseEvent) => {\n e.preventDefault();\n\n this.emit(\"contextmenu\", this._fieldName, e.clientX, e.clientY);\n });\n\n // Side-load the resize handle and sort-priority badge as overlays.\n // Their `position:absolute` means they don't disturb the cell's `Card`\n // layout flow, and side-loading (instead of `addComponent`) keeps the\n // Card from hiding them as non-visible siblings of the renderer.\n DOM.sink.appendChild(el, this._resizeHandle.getElement(true)!);\n DOM.sink.appendChild(el, this._priorityBadge.getElement(true)!);\n\n if (this._tooltipText) {\n Tooltip.attachToElement(el, this._tooltipText);\n }\n\n if (this._headerGlyph) {\n this.setHeaderGlyph(this._headerGlyph);\n }\n\n return this;\n }\n\n /**\n * Returns the currently mounted header glyph registry name, or `null` if none.\n *\n * @returns The glyph registry name, or `null`.\n */\n getHeaderGlyph(): string | null {\n return this._headerGlyph;\n }\n\n /**\n * Mounts (or replaces) the leading header glyph. Pass `null` to remove it.\n *\n * The glyph is absolutely positioned at the cell's left edge using the\n * `--ts-ui-table-header-glyph-gap` / `--ts-ui-table-header-glyph-color`\n * tokens. The text renderer's left inset is shifted right to clear the\n * glyph when one is mounted, and restored to the theme default when cleared.\n *\n * @param name - A registered glyph name, or `null` to remove the glyph.\n * @returns This cell, for method chaining.\n */\n setHeaderGlyph(name: string | null): this {\n this._headerGlyph = name;\n this._mountHeaderGlyph(this.getElement());\n\n return this;\n }\n\n /**\n * Mounts or replaces the leading glyph against the given host element.\n * Called from {@link setHeaderGlyph} (post-init via cached element) and\n * from {@link init} (during render via the element parameter, before\n * `Component._element` has been cached).\n *\n * @param el - The owning `<th>` element, or undefined when the cell is\n * still pre-render. When undefined the renderer insets are reset but\n * no glyph is mounted; the next render's {@link init} call will mount\n * the glyph using its element parameter.\n */\n private _mountHeaderGlyph(el: Handle | undefined): void {\n if (this._headerGlyphInstance) {\n const glyphEl = this._headerGlyphInstance.getElement();\n if (glyphEl) {\n DOM.sink.removeElement(glyphEl);\n }\n this._headerGlyphInstance = null;\n }\n\n const themePad = ThemeManager.getTheme().table.cell.padding;\n const name = this._headerGlyph;\n\n if (!name || !el) {\n this.getRenderer().setInsets(new Insets(0, themePad, 0, themePad));\n\n return;\n }\n\n ensureHeaderCellGlyphClassRule();\n\n const glyph = new Glyph(name);\n\n glyph.setTransform(\"translateY(-50%)\");\n glyph.setSize({ width: GLYPH_W, height: GLYPH_H });\n glyph.setForegroundColor(\"var(--ts-ui-table-header-glyph-color, currentColor)\");\n glyph.setPointerEvents(\"none\");\n\n const gEl = glyph.getElement(true)!;\n DOM.sink.apply(gEl, { addClass: [\"HeaderCellGlyph\"] });\n\n DOM.sink.appendChild(el, gEl);\n this._headerGlyphInstance = glyph;\n\n const offset = GLYPH_W + GLYPH_GAP + themePad;\n this.getRenderer().setInsets(new Insets(0, themePad, 0, offset));\n }\n\n /**\n * Updates the label to show a sort direction arrow suffix and toggles the\n * multi-sort priority badge.\n *\n * @param state - 'asc' or 'desc'. Use `clearSortState()` to remove the indicator.\n * @param priority - Optional 1-based position of this sorter in a multi-sort.\n * The badge is only shown when priority is at least 2.\n */\n setSortState(state: 'asc' | 'desc', priority?: number | null): this {\n this._sortState = { state, priority: priority ?? null };\n\n this._renderTitle();\n this.getAria().setSort(state === 'asc' ? 'ascending' : 'descending');\n\n this._priorityBadge.setPriority(priority ?? null);\n\n return this;\n }\n\n /**\n * Returns the cached sort indicator state last passed to {@link setSortState},\n * or `null` if no sort indicator is active.\n *\n * @returns An object describing the sort direction and multi-sort priority,\n * or null.\n */\n getSortState(): { state: 'asc' | 'desc', priority: number | null } | null {\n return this._sortState;\n }\n\n /**\n * Clears the sort indicator arrow and hides the multi-sort priority badge.\n *\n * @returns This cell, for method chaining.\n */\n clearSortState(): this {\n this._sortState = null;\n\n this._renderTitle();\n this.getAria().setSort('none');\n\n this._priorityBadge.clearPriority();\n\n return this;\n }\n\n /**\n * Sets whether this header cell shows the required-column asterisk\n * suffix. Driven by the column's static `ColumnConfig.required`\n * flag only — the header has no bound record to evaluate a\n * per-record `requiredPredicate` against.\n *\n * @param value - `true` to show the asterisk, `false` to hide it.\n * @returns This cell, for method chaining.\n */\n setRequired(value: boolean): this {\n this._required = value;\n this._renderTitle();\n\n return this;\n }\n\n /**\n * Composes the header label from the base title plus the required\n * asterisk (` *`, when `_required`) and the sort arrow (` ▲`/` ▼`,\n * when a sort state is active), and writes it to the renderer.\n * Shared by {@link setSortState}, {@link clearSortState}, and\n * {@link setRequired} so the two suffixes never clobber each other.\n */\n private _renderTitle(): void {\n const arrow = this._sortState\n ? (this._sortState.state === 'asc' ? ' ▲' : ' ▼')\n : '';\n const req = this._required ? ' *' : '';\n\n this.getRenderer().getText().setText(this._text + req + arrow);\n }\n\n /**\n * Registers a listener for one of this header cell's events.\n *\n * @param event - `\"commit\"` / `\"editend\"` inherit from {@link Cell} (they\n * are unused on the non-editable header surface but kept on the typed\n * signature for inheritance compatibility); `\"sortclick\"` fires when\n * the user clicks the header, receiving the field name and the\n * shift-key state; `\"contextmenu\"` fires on right-click, receiving the\n * field name and the viewport x/y; `\"resizestart\"` fires on mousedown\n * over the resize handle, receiving the absolute pointer `clientX` at the\n * moment the drag began; `\"resizedrag\"` fires on each mousemove during a\n * resize drag, receiving the absolute pointer `clientX`.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This cell, for method chaining.\n */\n on(event: \"commit\", listener: (value: String | null) => void): this;\n on(event: \"editend\", listener: () => void): this;\n on(event: \"sortclick\", listener: (fieldName: string, shiftKey: boolean) => void): this;\n on(event: \"contextmenu\", listener: (fieldName: string, x: number, y: number) => void): this;\n on(event: \"resizestart\", listener: (clientX: number) => void): this;\n on(event: \"resizedrag\", listener: (clientX: number) => void): this;\n on(event: HeaderCellEvent, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered listener. The exact callback reference\n * must match.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This cell, for method chaining.\n */\n off(event: HeaderCellEvent, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every listener registered for `event` with `payload`, in\n * registration order.\n *\n * @param event - The event to emit.\n * @param payload - Forwarded to each listener.\n */\n protected emit(event: \"commit\", value: String | null): void;\n protected emit(event: \"editend\"): void;\n protected emit(event: \"sortclick\", fieldName: string, shiftKey: boolean): void;\n protected emit(event: \"contextmenu\", fieldName: string, x: number, y: number): void;\n protected emit(event: \"resizestart\", clientX: number): void;\n protected emit(event: \"resizedrag\", clientX: number): void;\n protected emit(event: HeaderCellEvent, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Sets the tooltip text shown when hovering this header cell.\n *\n * Safe to call before or after the cell has rendered: pre-init calls are\n * picked up by `init` when it wires the tooltip attachment; post-init\n * calls re-attach against the live element so the visible tooltip\n * updates immediately.\n *\n * @param text - The text to display in the tooltip.\n */\n setTooltip(text: string): this {\n this._tooltipText = text;\n\n const el = this.getElement();\n\n if (el) {\n Tooltip.attachToElement(el, text);\n }\n\n return this;\n }\n\n /**\n * Returns the tooltip text shown when hovering this header cell.\n *\n * @returns The current tooltip string (empty when no tooltip has been set).\n */\n getTooltip(): string {\n return this._tooltipText;\n }\n\n /**\n * Routes a click on the header to the registered sort callback,\n * unless a resize drag has just finished.\n *\n * @param shiftKey - Whether the shift key was held when the click fired.\n */\n private onSortClick(shiftKey: boolean): void {\n if (this._isDragging) {\n this._isDragging = false;\n return;\n }\n\n this.emit(\"sortclick\", this._fieldName, shiftKey);\n }\n\n private onResizeDragStart(e: MouseEvent): void {\n e.stopPropagation();\n\n this._isDragging = true;\n\n this.emit(\"resizestart\", e.clientX);\n\n Event.addViewportListener(this, 'mousemove', this.onResizeDrag);\n Event.addViewportListener(this, 'mouseup', this.onResizeDragStop);\n\n beginPointerDrag(RESIZE_HANDLE_CURSOR);\n }\n\n private onResizeDrag(e: MouseEvent): Event.ListenerResult {\n this._resizeHandle.dragMove(e.clientX);\n\n return true;\n }\n\n private onResizeDragStop(): Event.ListenerResult {\n Event.removeViewportListener(this, 'mousemove', this.onResizeDrag);\n Event.removeViewportListener(this, 'mouseup', this.onResizeDragStop);\n\n endPointerDrag();\n\n this._resizeHandle.dragEnd();\n\n // clear flag after synthesized click fires\n setTimeout(() => { this._isDragging = false; }, 0);\n\n return true;\n }\n\n /**\n * Toggles the column-focused visual indicator on this header cell.\n *\n * @param focused - True to paint the focus underline, false to clear it.\n *\n * @returns This component, for method chaining.\n *\n * @remarks The indicator is a 2 px inset bottom box-shadow in the\n * `--ts-ui-focus-ring` theme token, matching the colour of the body cell's\n * focus outline so the eye reads the header cue and the body cue as one\n * affordance. Driven by `Body._updateFocusStyle` whenever the body's\n * focused column changes.\n */\n setColumnFocused(focused: boolean): this {\n this._columnFocused = focused;\n\n if (focused) {\n this.setShadow(\"inset 0 -2px 0 0 var(--ts-ui-focus-ring, rgba(30, 100, 200, 0.6))\");\n } else {\n this.clearShadow();\n }\n\n return this;\n }\n\n /**\n * Returns whether this header cell currently shows the column-focused indicator.\n *\n * @returns True when `setColumnFocused(true)` was the last call; false otherwise.\n */\n isColumnFocused(): boolean {\n return this._columnFocused;\n }\n}\n\nconst HeaderCellCallable = callable(HeaderCell);\ntype HeaderCellCallable = HeaderCell;\nexport {\n HeaderCell as _HeaderCell,\n HeaderCellCallable as HeaderCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { DefaultCell } from \"~/component/table/cell/Default.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { Tooltip } from \"~/overlay/Tooltip.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * A non-interactive header cell that labels a contiguous run of columns in\n * the parent-header row. Constructed once per visible group by the\n * [`Header`](/api/component/table/classes/Header) host (private\n * `rebuildParentCells`) and laid out via the table layout manager: x\n * and width are summed from the underlying column widths so the parent\n * cell visually spans its children.\n *\n * Empty-text instances render as blank spanning cells over ungrouped\n * columns; they exist so the parent header band has a continuous\n * surface with no gaps where the body background would otherwise leak\n * through.\n *\n * Sort / resize wiring is intentionally absent — parent cells do not\n * participate in the per-column sort cycle or carry resize handles.\n * They do support a right-click context menu (forwarded through the\n * same callback the column cells use, so the column-toggle menu opens\n * on either row), and a tooltip.\n *\n * @category Components\n */\nclass ParentHeaderCell extends DefaultCell {\n\n private _text: string;\n private _color: string | null;\n private _tooltipText: string = \"\";\n\n /**\n * Constructs a parent header cell over a contiguous run of grouped\n * columns.\n *\n * @param text - The group label to display. Empty string renders a\n * blank spanning cell over ungrouped columns.\n * @param color - Optional CSS color string for the cell's background;\n * `null` falls through to the header-band gradient inherited from\n * the `Header` parent.\n */\n constructor(text: string, color: string | null) {\n super(\"th\");\n\n this._text = text;\n this._color = color;\n\n this.getAria().setRole(\"columnheader\");\n\n const renderer = this.getRenderer();\n renderer.getText().setFontSize(\"--ts-ui-table-header-font-size\");\n renderer.getText().setFontWeight(\"bold\");\n renderer.getText().setTextAlign(\"center\");\n renderer.getText().setText(text);\n\n // The cell's `Cell` base writes `var(--ts-ui-table-cell-bg, …)` into\n // backgroundColor; override to either the consumer-supplied\n // `groupColor` or `transparent` so the `Header` band's gradient\n // shows through unaltered when no color is set. The base class only\n // re-applies the *border* on theme changes (Cell.ts:51), not the\n // background, so this write survives a theme swap.\n this.setBackgroundColor(color ?? \"transparent\");\n\n // Inter-group divider (right edge) and parent-row separator\n // (bottom edge) — two inset shadows in the same resize-handle\n // gray that paints the standard cell separators in the column\n // row beneath. Composing both into a single `setShadow` call\n // sidesteps `Cell`'s theme-change listener (which re-runs\n // `setBorder('var(--ts-ui-table-cell-border, none)')` and would\n // otherwise wipe a border-based divider on every theme toggle).\n this.setShadow([\n \"inset -1px 0 0 0 var(--ts-ui-table-resize-handle-color, rgba(0, 0, 0, 0.2))\",\n \"inset 0 -1px 0 0 var(--ts-ui-table-resize-handle-color, rgba(0, 0, 0, 0.2))\",\n ].join(\", \"));\n }\n\n /**\n * Registers a listener for this parent header cell's `\"contextmenu\"`\n * event, fired when the user right-clicks the cell. Mirrors\n * `HeaderCell.on(\"contextmenu\", fn)` but elides the per-column\n * `fieldName` — parent cells span multiple columns, so the host only\n * needs the viewport coordinates to anchor the menu.\n *\n * Wiring: the subtree `contextmenu` DOM listener is installed once at\n * `init` time and fires every registered listener at right-click time —\n * registering after `init` runs is supported (the next right-click\n * picks up the new listener), but the DOM listener itself is not\n * re-installed.\n *\n * @param event - The event name. Only `\"contextmenu\"` is accepted.\n * @param listener - Receives the viewport x and y coordinates of the event.\n *\n * @returns This cell, for method chaining.\n */\n on(event: \"contextmenu\", listener: (x: number, y: number) => void): this;\n on(event: \"commit\", listener: (value: String | null) => void): this;\n on(event: \"editend\", listener: () => void): this;\n on(event: string, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered listener. The exact callback\n * reference must match the one passed to {@link on}.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This cell, for method chaining.\n */\n off(event: string, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every registered listener for `event`, in registration order.\n * Widens the inherited {@link Cell} emitter with the `\"contextmenu\"`\n * event carrying the right-click's viewport coordinates.\n *\n * @param event - The event to emit.\n * @param payload - Forwarded to each listener.\n */\n protected emit(event: \"contextmenu\", x: number, y: number): void;\n protected emit(event: \"commit\", value: String | null): void;\n protected emit(event: \"editend\"): void;\n protected emit(event: string, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Sets the tooltip shown when hovering this parent header cell.\n * Wired through the framework's shared [`Tooltip`](/api/overlay/classes/Tooltip)\n * attach path, same as `HeaderCell.setTooltip`. Safe to call before or\n * after the cell has rendered: pre-init calls are picked up by `init`\n * when it wires the tooltip attachment; post-init calls re-attach\n * against the live element so the visible tooltip updates immediately.\n *\n * @param text - The text to display in the tooltip.\n */\n setTooltip(text: string): this {\n this._tooltipText = text;\n\n const el = this.getElement();\n\n if (el) {\n Tooltip.attachToElement(el, text);\n }\n\n return this;\n }\n\n /**\n * Returns the tooltip text shown when hovering this cell.\n *\n * @returns The current tooltip string (empty when no tooltip has been set).\n */\n getTooltip(): string {\n return this._tooltipText;\n }\n\n /**\n * Returns the group label rendered in this cell.\n *\n * @returns The label string; empty when the cell is a blank ungrouped span.\n */\n getText(): string {\n return this._text;\n }\n\n /**\n * Returns the optional background color this cell adopts, or `null`\n * when the header-band gradient shows through unaltered.\n *\n * @returns The CSS color string, or `null`.\n */\n getColor(): string | null {\n return this._color;\n }\n\n /**\n * Wires the host-supplied context-menu listener and attaches the\n * tooltip after the framework has mounted the element. Sub-tree\n * listener so right-clicks on the rendered label bubble up here.\n *\n * @param element - Optional element forwarded by the framework init chain.\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement();\n\n if (!el) {\n return this;\n }\n\n Event.addSubtreeListener(this, \"contextmenu\", this.onContextMenu);\n\n if (this._tooltipText) {\n Tooltip.attachToElement(el, this._tooltipText);\n }\n\n return this;\n }\n\n /**\n * Subtree contextmenu handler. Suppresses the browser's native menu\n * and fires the `\"contextmenu\"` event with the viewport coordinates\n * (the host's listener typically opens the table's column-toggle menu).\n *\n * @param e - The contextmenu event captured from a descendant.\n */\n private onContextMenu(e: MouseEvent): void {\n e.preventDefault();\n\n this.emit(\"contextmenu\", e.clientX, e.clientY);\n }\n}\n\nconst ParentHeaderCellCallable = callable(ParentHeaderCell);\ntype ParentHeaderCellCallable = ParentHeaderCell;\nexport {\n ParentHeaderCell as _ParentHeaderCell,\n ParentHeaderCellCallable as ParentHeaderCell\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { Row } from \"~/component/table/Row.js\";\nimport { AbstractModel } from \"~/data/AbstractModel.js\";\nimport { AbstractStore, SortDescriptor } from \"~/data/AbstractStore.js\";\nimport { Field } from \"~/data/Field.js\";\nimport { Column } from \"~/component/table/Column.js\";\nimport { HeaderCell } from \"~/component/table/cell/Header.js\";\nimport { ParentHeaderCell } from \"~/component/table/cell/ParentHeader.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n// The header surface, themed via `--ts-ui-table-header-bg`. The value is\n// applied as BOTH a background-color and a background-image because the token\n// is a flat colour in some themes (e.g. ModernTheme) and a gradient in others\n// (e.g. ClassicTheme): a colour is invalid as a background-image (resolves to\n// `none`) and a gradient is invalid as a background-color (resolves to\n// transparent), so setting both lets whichever form the active theme supplies\n// paint while the other harmlessly drops out. Setting only background-image —\n// the previous behaviour — left the surface transparent under a flat-colour\n// theme, which surfaced as a see-through scrollbar-cover band.\nconst TABLE_HEADER_BG = \"var(--ts-ui-table-header-bg, var(--ts-ui-button-bg, linear-gradient(rgb(241, 241, 241), rgb(200, 200, 200))))\";\n\n/**\n * String-literal union of the events emitted by the table {@link TableHeader}.\n *\n * @category Components\n */\nexport type TableHeaderEvent = \"columnresizestart\" | \"columnresize\" | \"columncontextmenu\";\n\n/**\n * The header section of a table, rendered as a `<thead>` element.\n *\n * Builds one {@link HeaderCell} per visible field from the supplied model. Each cell is\n * wired with a sort-click callback (cycles asc → desc → clear), a resize-drag\n * callback (forwarded to the owner via the `\"columnresize\"` event), and a\n * context-menu callback (forwarded via the `\"columncontextmenu\"` event); see\n * {@link TableHeader.on}.\n *\n * Re-exported as `TableHeader` from the package barrel.\n *\n * @category Components\n */\nclass TableHeader extends Component {\n\n private _model: AbstractModel;\n private _store: AbstractStore;\n private _hiddenColumns: Set<string> = new Set();\n private _columns: Column[] = [];\n private _listeners: ListenerBag<TableHeaderEvent> = new ListenerBag<TableHeaderEvent>();\n private _scrollbarCover: Handle | null = null;\n\n constructor(model: AbstractModel, store: AbstractStore) {\n super({ tag: \"thead\" });\n\n this.getAria().setRole(\"rowgroup\");\n this.setBorder({ borderBottom: \"1px solid var(--ts-ui-table-header-border, black)\" });\n this.setBackgroundColor(TABLE_HEADER_BG);\n this.setBackgroundImage(TABLE_HEADER_BG);\n // Clip cells that would otherwise extend past the header's right\n // edge when the inner rows are translated horizontally.\n this.setOverflow(\"hidden\");\n\n this._model = model;\n this._store = store;\n\n // Two `Row` children — parent row at index 0, column row at index 1.\n // The parent row collapses to zero height when no visible column\n // declares a group; existing no-group tables remain byte-identical\n // at runtime because `rebuildParentCells` produces no cells in\n // that case and the layout manager zeroes the parent-row height.\n const parentRow = new Row();\n const row = new Row();\n this.addRow(parentRow);\n this.addRow(row);\n\n this.rebuildCells();\n this.rebuildParentCells();\n }\n\n /**\n * Returns the model driving this header's columns.\n *\n * @returns The {@link AbstractModel} currently bound to this header.\n */\n getModel() {\n return this._model;\n }\n\n /**\n * Replaces the model, rebuilding header cells only when the visible field list changes.\n *\n * @param model - The new model to bind to the header.\n *\n * @remarks If the new model has the same visible fields in the same order as the current\n * model, the existing cells are left in place and sort indicators are re-synced.\n */\n setModel(model: AbstractModel): this {\n const toNames = (model: AbstractModel) =>\n model.getFields()\n .slice()\n .filter(f => !this._hiddenColumns.has(f.getName()))\n .sort((a, b) => a.getOrder() - b.getOrder())\n .map(f => f.getName());\n\n const oldNames = toNames(this._model);\n const newNames = toNames(model);\n\n const same = oldNames.length === newNames.length\n && oldNames.every((n, i) => n === newNames[i]);\n\n if (!same) {\n this._model = model;\n this.rebuildCells();\n this.rebuildParentCells();\n }\n\n this.syncSortIndicators();\n\n return this;\n }\n\n /**\n * Updates the set of hidden column field names and rebuilds both header\n * rows immediately.\n *\n * Field names belonging to {@link Column.isUnhideable} columns are stripped\n * from the set so a direct caller cannot bypass the unhideable contract.\n *\n * @param hidden - The new set of field names to hide.\n */\n setHiddenColumns(hidden: Set<string>): this {\n const filtered = new Set<string>();\n\n for (const name of hidden) {\n const col = this._columns.find(c => c.getField().getName() === name);\n\n if (!col || !col.isUnhideable()) {\n filtered.add(name);\n }\n }\n\n this._hiddenColumns = filtered;\n\n this.rebuildCells();\n this.rebuildParentCells();\n\n return this;\n }\n\n /**\n * Supplies the resolved {@link Column} list so that per-column header metadata\n * (e.g. `headerGlyph`, `group`) is available when cells are rebuilt.\n *\n * @param columns - The resolved columns in display order.\n * @returns This header, for method chaining.\n */\n setColumns(columns: Column[]): this {\n this._columns = columns;\n\n this.rebuildCells();\n this.rebuildParentCells();\n\n return this;\n }\n\n /**\n * Registers a listener for one of this header's events.\n *\n * @param event - `\"columnresizestart\"` fires on mousedown over a column\n * resize handle, receiving the zero-based column index and the absolute\n * pointer `clientX` at the moment the drag began; `\"columnresize\"` fires\n * when the user drags a column resize handle, receiving the zero-based\n * column index and the absolute pointer `clientX`; `\"columncontextmenu\"`\n * fires on a right-click anywhere in the header band, receiving the field\n * name (empty string when the click landed on a parent-header cell) and\n * the viewport x/y coordinates.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This header, for method chaining.\n */\n on(event: \"columnresizestart\", listener: (colIndex: number, clientX: number) => void): this;\n on(event: \"columnresize\", listener: (colIndex: number, clientX: number) => void): this;\n on(event: \"columncontextmenu\", listener: (fieldName: string, x: number, y: number) => void): this;\n on(event: TableHeaderEvent, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered listener. The exact callback reference\n * must match.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This header, for method chaining.\n */\n off(event: TableHeaderEvent, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every listener registered for `event` with `payload`, in\n * registration order.\n *\n * @param event - The event to emit.\n * @param payload - Forwarded to each listener.\n */\n protected emit(event: \"columnresizestart\", colIndex: number, clientX: number): void;\n protected emit(event: \"columnresize\", colIndex: number, clientX: number): void;\n protected emit(event: \"columncontextmenu\", fieldName: string, x: number, y: number): void;\n protected emit(event: TableHeaderEvent, ...payload: unknown[]): void {\n this._listeners.fire(event, ...payload);\n }\n\n /**\n * Returns the header cell components in column order. The column row\n * lives at child index 1 — the parent-header row sits at index 0.\n *\n * @returns An array of cell components from the column-header row.\n */\n getColumns() {\n return this.getComponents()[1].getComponents();\n }\n\n /**\n * Returns the parent-header row hosting the {@link ParentHeaderCell}\n * instances. Always present, even when no visible column declares a\n * group — in that case the row has zero cells and the layout manager\n * collapses it to zero height.\n *\n * @returns The parent row.\n */\n getParentRow(): Row {\n return this.getComponents()[0] as Row;\n }\n\n /**\n * Returns `true` when at least one visible column declares a group —\n * i.e. the parent-header row is rendered with non-zero height.\n * Driven by {@link Column.getGroup} across the resolved visible\n * column list.\n *\n * @returns `true` when the parent row should be laid out, `false`\n * when it should collapse.\n */\n hasParentRow(): boolean {\n return this._columns\n .filter(c => !this._hiddenColumns.has(c.getField().getName()))\n .some(c => c.getGroup() !== null);\n }\n\n /**\n * Returns the cover element that masks the vertical-scrollbar reservation\n * band at the header's right edge. Created lazily on first access; sized\n * and positioned by the table layout. Carries the same gradient as the\n * header so cells translated horizontally appear to clip at the trackW\n * boundary while the reservation band stays visually continuous with\n * the rest of the header.\n */\n getScrollbarCover(): Handle {\n if (this._scrollbarCover === null) {\n // `cover` is a raw presentational `<div>` owned by this header, not a\n // Component, so the Component style setters don't apply and direct\n // `.style` writes are correct here.\n const cover = DOM.sink.createElement(\"div\");\n DOM.sink.apply(cover, { style: {\n \"position\": \"absolute\",\n \"top\": \"0\",\n \"boxSizing\": \"border-box\",\n // Inner rows are Components with `z-index: 0`, which creates a\n // stacking context that paints AFTER positioned siblings with\n // `z-index: auto`. Without an explicit z-index here the cover\n // would be painted beneath the rows and cells could be seen\n // bleeding into the scrollbar-reservation band.\n \"zIndex\": \"1\",\n // Presentational only; don't intercept pointer events so column\n // resize handles whose cells happen to be horizontally scrolled\n // under the cover still receive clicks.\n \"pointerEvents\": \"none\",\n \"backgroundColor\": TABLE_HEADER_BG,\n \"backgroundImage\": TABLE_HEADER_BG,\n // Left border matches the column-cell right border so the cover\n // reads as a visual continuation of the column separators\n // rather than a seam in the gradient.\n \"borderLeft\": \"1px solid var(--ts-ui-table-resize-handle-color, rgba(0, 0, 0, 0.2))\",\n } });\n DOM.sink.appendChild(this.getElement(true)!, cover);\n // Track the header-owned cover so it is released with the header\n // (on destructor or GC), not left pinned in the registry.\n this.trackHandle(cover);\n this._scrollbarCover = cover;\n }\n\n return this._scrollbarCover;\n }\n\n /**\n * Reorders header cells by field order using their layout constraints.\n */\n sortColumns() {\n const row = this.getComponents()[1];\n\n row.sortComponents((c1, c2) => {\n const lc1 = row.getLayoutConstraints(c1);\n const lc2 = row.getLayoutConstraints(c2);\n\n if (!lc1) {\n return -1;\n }\n\n if (!lc2) {\n return 1;\n }\n\n return (lc1.data as Field).getOrder() - (lc2.data as Field).getOrder();\n });\n }\n\n /**\n * Appends a row to the header.\n *\n * @param row - The row to append.\n */\n addRow(row: Row) : this {\n this.addComponent(row);\n\n return this;\n }\n\n /**\n * Adds a row as a child component of the header.\n *\n * @param row - The row component to add.\n *\n * @returns This component, for method chaining.\n */\n addComponent(row: Row): this {\n super.addComponent(row);\n\n return this;\n }\n\n /**\n * Sets the header width and propagates it to both the parent and\n * column rows so the full header band shares the same width.\n *\n * @param width - The width in pixels.\n *\n * @returns This component, for method chaining.\n */\n setWidth(width: number): this {\n super.setWidth(width);\n\n const rows = this.getComponents();\n rows[0].setWidth(width);\n rows[1].setWidth(width);\n\n return this;\n }\n\n /**\n * Sets the header height. Row-level heights (parent row + column row)\n * are set independently by the table layout manager — `TableHeader` itself\n * just stores the total band height. The per-row height assignments\n * live in `layout/Table.doLayout` because the split depends on\n * {@link hasParentRow}.\n *\n * @param height - The total header band height in pixels.\n *\n * @returns This component, for method chaining.\n */\n setHeight(height: number): this {\n super.setHeight(height);\n\n return this;\n }\n\n /**\n * Reconciles the column-row's header cells against the current\n * model's visible fields. Surviving cells (whose field is still\n * visible) keep their sort indicator, resize-handle wiring,\n * tooltip, theme listener, and `setColumnFocused` state; cells for\n * newly-visible fields are constructed and wired; cells for\n * now-hidden fields are removed.\n *\n * Operates on the column row at child index 1; the parent row at\n * index 0 is rebuilt separately in {@link rebuildParentCells}.\n */\n private rebuildCells(): void {\n const row = this.getComponents()[1] as Row;\n\n const targetFields = this._model.getFields()\n .slice()\n .filter(f => !this._hiddenColumns.has(f.getName()))\n .sort((a, b) => a.getOrder() - b.getOrder());\n\n const columnMap = new Map(this._columns.map(c => [c.getField().getName(), c]));\n const existing = row.getComponents().slice() as HeaderCell[];\n const byName = new Map<string, HeaderCell>();\n\n for (const cell of existing) {\n const lc = row.getLayoutConstraints(cell);\n const field = lc?.data as Field | undefined;\n\n if (field) {\n byName.set(field.getName(), cell);\n }\n }\n\n const targetNames = new Set(targetFields.map(f => f.getName()));\n\n for (const cell of existing) {\n const lc = row.getLayoutConstraints(cell);\n const field = lc?.data as Field | undefined;\n\n if (!field || !targetNames.has(field.getName())) {\n row.removeComponent(cell);\n }\n }\n\n for (let i = 0; i < targetFields.length; i++) {\n const field = targetFields[i];\n const col = columnMap.get(field.getName());\n let cell = byName.get(field.getName());\n\n if (!cell) {\n const glyph = col?.getHeaderGlyph() ?? null;\n\n cell = new HeaderCell(field.getName(), field.getName(), glyph);\n cell.setTooltip(field.getDescription());\n\n row.addComponent(cell, { data: field });\n\n // Wire exactly once, at creation. The resize/sort/context\n // closures resolve the cell's visible-column index live (via\n // getColumns) at emit time, so a later hide/show that shifts\n // indices needs no re-wiring. Re-wiring a surviving cell would\n // stack duplicate listeners on its ListenerBag — making a\n // single drag emit `columnresize` several times with mismatched\n // indices, and a single header click cycle the sort twice.\n this.wireCell(cell);\n }\n\n // Tint the column header with the group's `groupColor` so\n // the header band reads as one visual group above the\n // matching body-cell tint applied in Row.ts. `Cell`'s\n // theme-change listener only re-applies the border, so\n // this background survives a theme swap. Re-applied on\n // every sync so a config swap that changed the tint also\n // takes effect.\n const groupColor = col?.getGroupColor();\n\n if (groupColor) {\n cell.setBackgroundColor(groupColor);\n }\n\n // Re-applied on every sync (unconditionally, not gated on\n // an `isRequired()` truthy check) so a config swap that\n // clears `required` also clears the asterisk on a\n // surviving cell — mirrors the group-tint cadence above.\n cell.setRequired(col?.isRequired() ?? false);\n }\n\n // Re-order children to the new visible-field display order so\n // sibling iteration (e.g. `syncSortIndicators`) matches the\n // visible-field list index.\n row.sortComponents((c1, c2) => {\n const f1 = (row.getLayoutConstraints(c1)?.data as Field).getOrder();\n const f2 = (row.getLayoutConstraints(c2)?.data as Field).getOrder();\n\n return f1 - f2;\n });\n\n this.syncSortIndicators();\n }\n\n /**\n * Removes all existing parent-header cells and recreates one\n * {@link ParentHeaderCell} per contiguous run of visible columns\n * sharing the same group key. Ungrouped columns each produce a\n * blank spanning cell so the parent row's surface stays continuous.\n *\n * The visible-column order is read from `_columns` filtered by\n * `_hiddenColumns` and sorted by {@link Field.getOrder} — same\n * resolution path used by {@link rebuildCells}. Each cell's\n * `spanFrom` / `spanTo` indices are stored in its layout constraints'\n * `data` slot; the table layout manager reads them to position the\n * cell as the sum of underlying column widths.\n */\n private rebuildParentCells(): void {\n const row = this.getParentRow();\n\n row.removeAllComponents();\n\n const visibleCols = this._columns\n .filter(c => !this._hiddenColumns.has(c.getField().getName()))\n .sort((a, b) => a.getField().getOrder() - b.getField().getOrder());\n\n if (visibleCols.length === 0) {\n return;\n }\n\n let runStart = 0;\n let runKey = visibleCols[0].getGroup();\n let runColor = visibleCols[0].getGroupColor();\n\n const flush = (endExclusive: number): void => {\n const cell = new ParentHeaderCell(runKey ?? \"\", runColor);\n\n // Field names spanned by this cell — drives the tooltip\n // and is also useful context if a future column-toggle\n // menu wants to operate on a whole group.\n const fieldNames = visibleCols\n .slice(runStart, endExclusive)\n .map(c => c.getField().getName());\n\n if (runKey !== null && fieldNames.length > 0) {\n cell.setTooltip(`${runKey}: ${fieldNames.join(\", \")}`);\n }\n\n cell.on(\"contextmenu\", (x, y) => {\n this.emit(\"columncontextmenu\", \"\", x, y);\n });\n\n row.addComponent(cell, { data: { spanFrom: runStart, spanTo: endExclusive - 1 } });\n };\n\n for (let i = 1; i < visibleCols.length; i++) {\n const nextKey = visibleCols[i].getGroup();\n // Ungrouped columns (group === null) each get their own blank\n // span — they never merge with adjacent ungrouped columns.\n // A run continues only when both sides share the same\n // non-null group key.\n const runContinues = runKey !== null && nextKey === runKey;\n\n if (!runContinues) {\n flush(i);\n\n runStart = i;\n runKey = nextKey;\n runColor = visibleCols[i].getGroupColor();\n } else if (runColor === null && visibleCols[i].getGroupColor() !== null) {\n // First non-null `groupColor` in a run wins; pick it up\n // when an earlier column omitted the field and a later\n // one supplied it.\n runColor = visibleCols[i].getGroupColor();\n }\n }\n\n flush(visibleCols.length);\n }\n\n /**\n * Wires the sort, resize, and context-menu callbacks for one cell. Called\n * exactly once per cell, at creation.\n *\n * @param cell - The header cell whose listeners are being attached.\n *\n * @remarks The resize callbacks report the cell's *current* visible-column\n * index by looking it up live through {@link getColumns} when the event\n * fires, rather than capturing an index at wiring time. This keeps the\n * index correct after a hide/show/reorder shuffles the columns without\n * re-wiring — re-wiring would stack duplicate listeners on the surviving\n * cell's `ListenerBag`.\n */\n private wireCell(cell: HeaderCell): void {\n cell.on(\"sortclick\", (fieldName, shiftKey) => this.handleSortClick(fieldName, shiftKey));\n cell.on(\"resizestart\", (clientX) => this.emit(\"columnresizestart\", this.getColumns().indexOf(cell), clientX));\n cell.on(\"resizedrag\", (clientX) => this.emit(\"columnresize\", this.getColumns().indexOf(cell), clientX));\n cell.on(\"contextmenu\", (fieldName, x, y) => this.emit(\"columncontextmenu\", fieldName, x, y));\n }\n\n /**\n * Handles a click on a header cell, cycling sort state on the underlying store.\n *\n * @param fieldName - The field associated with the clicked column.\n * @param shiftKey - Whether the shift key was held; toggles multi-column sort composition.\n *\n * @remarks\n * Without shift, behaviour is asc → desc → cleared on the clicked column.\n * With shift, the column is appended/toggled within the existing sort list:\n * not present → append asc; asc → flip to desc; desc → remove from the list.\n */\n private handleSortClick(fieldName: string, shiftKey: boolean): void {\n if (shiftKey) {\n const sorters = this._store.getActiveSorters();\n const idx = sorters.findIndex(s => s.field === fieldName);\n let next: SortDescriptor[];\n\n if (idx === -1) {\n next = [...sorters, { field: fieldName, dir: 'asc' }];\n } else if (sorters[idx].dir === 'asc') {\n next = sorters.map((s, i) =>\n i === idx ? { field: s.field, dir: 'desc' } : s\n );\n } else {\n next = sorters.filter((_, i) => i !== idx);\n }\n\n if (next.length === 0) {\n this._store.clearSort();\n } else {\n this._store.sort(next);\n }\n } else {\n const sorters = this._store.getActiveSorters();\n const current = sorters.length === 1 && sorters[0].field === fieldName\n ? sorters[0] : null;\n\n if (!current) {\n this._store.sort(fieldName, 'asc');\n } else if (current.dir === 'asc') {\n this._store.sort(fieldName, 'desc');\n } else {\n this._store.clearSort();\n }\n }\n\n this.syncSortIndicators();\n }\n\n /**\n * Refreshes every visible header cell's sort arrow and priority badge\n * to match the store's current `activeSorters` list.\n */\n private syncSortIndicators(): void {\n const cells = this.getColumns() as HeaderCell[];\n const visibleFields = this._model.getFields()\n .slice()\n .filter(f => !this._hiddenColumns.has(f.getName()))\n .sort((a, b) => a.getOrder() - b.getOrder());\n\n const sorters = this._store.getActiveSorters();\n const fieldToSorter = new Map(sorters.map((s, i) => [s.field, { dir: s.dir, priority: i + 1 }]));\n const showPriority = sorters.length > 1;\n\n cells.forEach((cell, i) => {\n const fieldName = visibleFields[i]?.getName();\n const entry = fieldName ? fieldToSorter.get(fieldName) : undefined;\n\n if (entry) {\n cell.setSortState(entry.dir, showPriority ? entry.priority : null);\n } else {\n cell.clearSortState();\n }\n });\n }\n}\n\nconst TableHeaderCallable = callable(TableHeader);\ntype TableHeaderCallable = TableHeader;\nexport {\n TableHeader as _TableHeader,\n TableHeaderCallable as TableHeader\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellEditor } from \"~/component/table/cell/editor/CellEditor.js\";\nimport { TextField } from \"~/component/input/TextField.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * An in-place editor for string cell values.\n *\n * Wraps a {@link TextField} and proxies blur and keydown events up to the parent\n * cell so the standard commit/cancel lifecycle works. Caches the typed\n * text on each input event; an empty field commits as `null`, mirroring\n * the cell-stack convention that \"no value\" is `null` and not `\"\"`.\n *\n * @category Components\n */\nclass StringEditor extends CellEditor<String | null> {\n\n private _textField: TextField = new TextField();\n private _value: String | null = null;\n\n constructor() {\n super();\n\n // Internal cell-editor wiring: listens on a privately-owned child;\n // see the cell-editor carve-out in ARCHITECTURE.md.\n Event.addListener(this._textField, \"blur\", (evnt: UIEvent) => {\n Event.fireEvent(this, \"blur\", evnt);\n });\n Event.addListener(this._textField, \"keydown\", (evnt: KeyboardEvent) => {\n Event.fireEvent(this, \"keydown\", { detail: {\n key : evnt.key , code : evnt.code , keyCode: evnt.keyCode,\n shiftKey: evnt.shiftKey, ctrlKey: evnt.ctrlKey,\n altKey : evnt.altKey , metaKey: evnt.metaKey\n } });\n });\n Event.addListener(this._textField, \"input\", () => this.onInput());\n\n this._textField.setMaxSize({ width: Number.MAX_SAFE_INTEGER, height: Number.MAX_SAFE_INTEGER });\n this._textField.clearPadding();\n this._textField.setBorderRadius('0');\n this._textField.setBorder({ border: \"0px solid transparent\" });\n this._textField.setShadow('inset 0 0 0 1px var(--ts-ui-table-cell-editor-border, rgba(30, 100, 200, 0.6))');\n this._textField.setOutline('none');\n this._textField.setText(\"\");\n this.addComponent(this._textField);\n }\n\n /**\n * Returns the cached string value, or `null` when the field is\n * empty. The distinction between a typed `\"\"` and `null` is\n * intentionally dropped — empty input is empty input.\n *\n * @returns The current string value, or `null`.\n */\n getValue(): String | null {\n return this._value;\n }\n\n /**\n * Populates the text field and caches the value. `null` and\n * `undefined` populate an empty field.\n *\n * @param value - The string value to set in the text field, or\n * `null`/`undefined` to leave the field empty.\n */\n setValue(value: String | null): this {\n this._value = value ?? null;\n this._textField.setText(this._value === null ? \"\" : String(this._value));\n\n return this;\n }\n\n /**\n * Focuses the text field and selects all its content.\n *\n * @param preventScroll - Forwarded to the field's focus so a native\n * focus-scroll doesn't desync the table body's own scroll model.\n * @returns This component, for method chaining.\n */\n focus(preventScroll: boolean = false): this {\n this._textField.focus(preventScroll);\n this._textField.select();\n\n return this;\n }\n\n /**\n * Reads the live text field content into the cached value. An\n * empty string becomes `null`.\n */\n private onInput(): void {\n const raw = this._textField.getText();\n this._value = raw ? raw : null;\n }\n}\n\nconst StringEditorCallable = callable(StringEditor);\ntype StringEditorCallable = StringEditor;\nexport {\n StringEditor as _StringEditor,\n StringEditorCallable as StringEditor\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellEditor } from \"~/component/table/cell/editor/CellEditor.js\";\nimport { TextField } from \"~/component/input/TextField.js\";\nimport { AnchorType } from \"~/layout/AnchorType.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * An in-place editor for numeric cell values.\n *\n * Wraps a right-aligned {@link TextField} and proxies blur and keydown events\n * up to the parent cell so the standard commit/cancel lifecycle works.\n * Caches the parsed numeric value on each input event so an empty or\n * unparseable field commits as `null` rather than silently coercing to\n * `0` or `NaN`.\n *\n * @category Components\n */\nclass NumberEditor extends CellEditor<Number | null> {\n\n private _textField: TextField = new TextField();\n private _value: Number | null = null;\n\n constructor() {\n super();\n\n // Internal cell-editor wiring: listens on a privately-owned child;\n // see the cell-editor carve-out in ARCHITECTURE.md.\n Event.addListener(this._textField, \"blur\", (evnt: UIEvent) => {\n Event.fireEvent(this, \"blur\", evnt);\n });\n Event.addListener(this._textField, \"keydown\", (evnt: KeyboardEvent) => {\n Event.fireEvent(this, \"keydown\", { detail: {\n key : evnt.key , code : evnt.code , keyCode: evnt.keyCode,\n shiftKey: evnt.shiftKey, ctrlKey: evnt.ctrlKey,\n altKey : evnt.altKey , metaKey: evnt.metaKey\n } });\n });\n Event.addListener(this._textField, \"input\", () => this.onInput());\n\n this.setMaxSize({ width: Number.MAX_SAFE_INTEGER, height: Number.MAX_SAFE_INTEGER });\n this._textField.clearPadding();\n this.setBorderRadius(\"0\");\n this._textField.setBorder({ border: \"0px solid transparent\" });\n this._textField.setShadow('inset 0 0 0 1px var(--ts-ui-table-cell-editor-border, rgba(30, 100, 200, 0.6))');\n this._textField.setOutline('none');\n this._textField.setTextAlign(\"right\");\n this._textField.setText(\"\");\n\n this.addComponent(this._textField, {\n anchor: AnchorType.NORTHEAST\n });\n }\n\n /**\n * Returns the cached numeric value, or `null` when the field is\n * empty or contains unparseable text. Reads the private cache\n * rather than re-parsing the text on demand so an empty field\n * commits as `null` instead of `0`, and unparseable text commits\n * as `null` instead of `NaN`.\n *\n * @returns The parsed numeric value, or `null`.\n */\n getValue(): Number | null {\n return this._value;\n }\n\n /**\n * Populates the text field with the number as a string and caches\n * the value. `null` and `undefined` populate an empty field so the\n * user sees a blank input on first edit instead of the literal\n * text `\"undefined\"` / `\"null\"`.\n *\n * @param value - The numeric value to set in the text field, or\n * `null`/`undefined` to leave the field empty.\n */\n setValue(value: Number | null): this {\n this._value = value ?? null;\n this._textField.setText(this._value === null ? \"\" : String(this._value));\n\n return this;\n }\n\n /**\n * Focuses the text field and selects all its content.\n *\n * @param preventScroll - Forwarded to the field's focus so a native\n * focus-scroll doesn't desync the table body's own scroll model.\n * @returns This component, for method chaining.\n */\n focus(preventScroll: boolean = false): this {\n this._textField.focus(preventScroll);\n this._textField.select();\n\n return this;\n }\n\n /**\n * Parses the live text field content into the cached value. An\n * empty string or unparseable text becomes `null`; otherwise the\n * cached value is the numeric `Number(raw)`.\n */\n private onInput(): void {\n const raw = this._textField.getText();\n\n if (!raw) {\n this._value = null;\n\n return;\n }\n\n const n = Number(raw);\n this._value = isNaN(n) ? null : n;\n }\n}\n\nconst NumberEditorCallable = callable(NumberEditor);\ntype NumberEditorCallable = NumberEditor;\nexport {\n NumberEditor as _NumberEditor,\n NumberEditorCallable as NumberEditor\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellEditor } from \"~/component/table/cell/editor/CellEditor.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\n\n/**\n * Base class for cell editors backed by a native `<input>` element.\n *\n * Owns the typed setters for the three input attributes that every\n * bare-input editor needs to declare its identity — `type`, `inputmode`,\n * and `autocomplete`. Subclasses call these from their constructor instead\n * of routing through `Component.setElementAttribute` directly, which keeps\n * the rule that behaviour-affecting attributes never reach the string-keyed\n * attribute API at the call site.\n *\n * Cannot reuse `Input`'s typed setters because [`CellEditor`](/api/component/table/classes/CellEditor)\n * extends [`Component`](/api/core/classes/Component) (not `Input`):\n * `Input` carries form-submission `name` plumbing and a default font CSS\n * rule that cell editors do not need, and most non-text editors prefer a\n * `<div>` root.\n *\n * @category Components\n */\nexport abstract class TextInputCellEditor<T> extends CellEditor<T> {\n\n private _type: string | null = null;\n private _inputMode: string | null = null;\n private _autoComplete: string | null = null;\n\n constructor() {\n super(\"input\");\n }\n\n /**\n * Sets the HTML `type` attribute on the underlying input element. Called\n * once from a subclass constructor to declare the editor's input shape.\n *\n * @param value - The input type (e.g. `\"text\"`, `\"number\"`).\n *\n * @returns This component, for method chaining.\n */\n protected setType(value: string): this {\n this._type = value;\n this.setElementAttribute(\"type\", value);\n\n return this;\n }\n\n /**\n * Sets the HTML `inputmode` attribute, which controls the on-screen\n * keyboard surface on mobile. Cell editors typically pass `\"none\"` so\n * the virtual keyboard does not pop while a picker dropdown is open.\n *\n * @param value - A valid `inputmode` value.\n *\n * @returns This component, for method chaining.\n */\n protected setInputMode(value: string): this {\n this._inputMode = value;\n this.setElementAttribute(\"inputmode\", value);\n\n return this;\n }\n\n /**\n * Sets the HTML `autocomplete` attribute, which controls browser\n * autofill. Cell editors typically pass `\"off\"` to keep autofill\n * suggestions from interfering with the picker dropdown.\n *\n * @param value - A valid `autocomplete` token.\n *\n * @returns This component, for method chaining.\n */\n protected setAutoComplete(value: string): this {\n this._autoComplete = value;\n this.setElementAttribute(\"autocomplete\", value);\n\n return this;\n }\n\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement()!;\n\n if (this._type !== null) {\n DOM.sink.apply(el, { setAttr: { \"type\": this._type } });\n }\n\n if (this._inputMode !== null) {\n DOM.sink.apply(el, { setAttr: { \"inputmode\": this._inputMode } });\n }\n\n if (this._autoComplete !== null) {\n DOM.sink.apply(el, { setAttr: { \"autocomplete\": this._autoComplete } });\n }\n\n return this;\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { TextInputCellEditor } from \"~/component/table/cell/editor/TextInputCellEditor.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport { DatePickerDropdown } from \"~/component/input/DatePickerDropdown.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * An in-place editor for date cell values.\n *\n * Renders a focusable `<input type=\"text\" inputmode=\"none\">` element and\n * pops a [`DatePickerDropdown`](/api/component/input/classes/DatePickerDropdown)\n * (the same class used by the form-field\n * [`DateField`](/api/component/input/classes/DateField)) on focus. The\n * dropdown's row `pointerdown` handler calls `preventDefault()` so the input\n * keeps focus while the user clicks a day — preserving the\n * [`CellEditorPool`](/api/component/table/classes/CellEditorPool)'s\n * blur-to-commit contract without modifying the pool.\n *\n * @category Components\n */\nclass DateEditor extends TextInputCellEditor<Date | null> {\n\n private _value: Date | null = null;\n private _dropdown: DatePickerDropdown | null = null;\n private _animated: boolean = true;\n private _text: string = \"\";\n\n constructor() {\n super();\n\n this.setMaxSize({ width: Number.MAX_SAFE_INTEGER, height: Number.MAX_SAFE_INTEGER });\n this.setBorderRadius('0');\n this.setBorder({ border: \"0px solid transparent\" });\n this.setShadow('inset 0 0 0 1px var(--ts-ui-table-cell-editor-border, rgba(30, 100, 200, 0.6))');\n this.setOutline('none');\n\n Event.addListener(this, \"focus\", () => this.openDropdown());\n Event.addListener(this, \"blur\", () => this.closeDropdown());\n Event.addListener(this, \"input\", () => this.onInput());\n\n this.setType(\"text\");\n this.setInputMode(\"none\");\n this.setAutoComplete(\"off\");\n }\n\n /**\n * Returns whether the input is empty (no typed text, no committed value).\n *\n * @returns true when both the rendered text and the cached value are empty.\n */\n isEmpty(): boolean {\n return !this.getText() && this._value === null;\n }\n\n /**\n * Returns the currently committed `Date`, or null when the field is empty\n * or contains an unparseable value.\n */\n getValue(): Date | null {\n return this._value;\n }\n\n /**\n * Sets the field value and renders the formatted string in the input.\n *\n * @param value - The Date to display, or null to clear the field.\n */\n setValue(value: Date | null): this {\n this._value = value;\n this.setText(value ? this.toInputString(value) : \"\");\n\n return this;\n }\n\n /**\n * Writes `text` into the underlying `<input>` element's value through a\n * typed setter so call sites never touch `element.value` directly.\n *\n * @param text - The string to render in the input.\n */\n private setText(text: string): this {\n this._text = text;\n\n const el = this.getElement();\n if (el) {\n DOM.sink.setValue(el, text);\n }\n\n return this;\n }\n\n /**\n * Returns the cached input text. Stays in sync via {@link syncTextFromDom}\n * which is called from the input listener before `onInput`.\n */\n private getText(): string {\n return this._text;\n }\n\n /**\n * Reads the live value from the `<input>` element into the text cache.\n * Confines the raw `element.value` read to a single typed helper so\n * `onInput` can work off {@link getText}.\n */\n private syncTextFromDom(): void {\n const el = this.getElement();\n this._text = el ? DOM.source.getValue(el) : \"\";\n }\n\n /**\n * Enables or disables the fade animation on the dropdown.\n *\n * @param value - true to fade, false for instant open/close.\n */\n setDropdownAnimated(value: boolean): this {\n this._animated = value;\n\n if (this._dropdown) {\n this._dropdown.setAnimated(value);\n }\n\n return this;\n }\n\n /**\n * Returns whether the dropdown fade is enabled.\n *\n * @returns true when the dropdown fades; false when it opens/closes instantly.\n */\n isDropdownAnimated(): boolean {\n return this._animated;\n }\n\n /**\n * Returns or lazily creates the picker dropdown.\n */\n private ensureDropdown(): DatePickerDropdown {\n if (!this._dropdown) {\n this._dropdown = new DatePickerDropdown(date => this.onDateSelected(date));\n this._dropdown.setAnimated(this._animated);\n }\n\n return this._dropdown;\n }\n\n /**\n * Opens the picker dropdown anchored to the input element.\n */\n private openDropdown(): void {\n const dropdown = this.ensureDropdown();\n if (dropdown.isOpen()) {\n return;\n }\n\n const el = this.getElement();\n if (!el) {\n return;\n }\n\n dropdown.showAt(el, this._value);\n }\n\n /**\n * Hides the picker dropdown if it is currently open. Called when the\n * editor's input element loses focus (the cell exits edit mode).\n */\n private closeDropdown(): void {\n if (this._dropdown?.isOpen()) {\n this._dropdown.hideAnimated();\n }\n }\n\n /**\n * Updates the cached value from a typed text edit.\n */\n private onInput(): void {\n this.syncTextFromDom();\n const raw = this.getText();\n\n if (!raw) {\n this._value = null;\n return;\n }\n\n const d = new Date(raw + 'T00:00:00');\n this._value = isNaN(d.getTime()) ? null : d;\n }\n\n /**\n * Called when the user picks a day in the dropdown. Sets the value,\n * hides the dropdown, and fires the editor's blur to drive\n * `CellEditorPool`'s commit path.\n */\n private onDateSelected(date: Date): void {\n this.setValue(date);\n this._dropdown?.hideAnimated();\n\n const el = this.getElement();\n\n if (el) {\n DOM.sink.blur(el);\n }\n }\n\n private toInputString(date: Date): string {\n const y = date.getFullYear();\n const m = String(date.getMonth() + 1).padStart(2, '0');\n const d = String(date.getDate()).padStart(2, '0');\n return `${y}-${m}-${d}`;\n }\n}\n\nconst DateEditorCallable = callable(DateEditor);\ntype DateEditorCallable = DateEditor;\nexport {\n DateEditor as _DateEditor,\n DateEditorCallable as DateEditor\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { TextInputCellEditor } from \"~/component/table/cell/editor/TextInputCellEditor.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport { TimePickerDropdown } from \"~/component/input/TimePickerDropdown.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * An in-place editor for time cell values.\n *\n * Renders a focusable text input and pops a\n * [`TimePickerDropdown`](/api/component/input/classes/TimePickerDropdown)\n * (the same class used by [`TimeField`](/api/component/input/classes/TimeField))\n * on focus. The dropdown suppresses focus loss on `pointerdown` so the\n * [`CellEditorPool`](/api/component/table/classes/CellEditorPool)'s blur-to-commit\n * path is not invoked prematurely.\n *\n * The value is represented as a `Date` whose time portion is meaningful; the\n * date portion is normalised to 1970-01-01 local.\n *\n * @category Components\n */\nclass TimeEditor extends TextInputCellEditor<Date | null> {\n\n private _showSeconds: boolean;\n private _value: Date | null = null;\n private _dropdown: TimePickerDropdown | null = null;\n private _animated: boolean = true;\n private _text: string = \"\";\n\n constructor(showSeconds: boolean = false) {\n super();\n this._showSeconds = showSeconds;\n\n this.setMaxSize({ width: Number.MAX_SAFE_INTEGER, height: Number.MAX_SAFE_INTEGER });\n this.setBorderRadius('0');\n this.setBorder({ border: \"0px solid transparent\" });\n this.setShadow('inset 0 0 0 1px var(--ts-ui-table-cell-editor-border, rgba(30, 100, 200, 0.6))');\n this.setOutline('none');\n\n Event.addListener(this, \"focus\", () => this.openDropdown());\n Event.addListener(this, \"blur\", () => this.closeDropdown());\n Event.addListener(this, \"input\", () => this.onInput());\n\n this.setType(\"text\");\n this.setInputMode(\"none\");\n this.setAutoComplete(\"off\");\n }\n\n /**\n * Returns whether the input is empty.\n *\n * @returns true when both the rendered text and the cached value are empty.\n */\n isEmpty(): boolean {\n return !this.getText() && this._value === null;\n }\n\n /**\n * Returns the currently committed `Date`, or null.\n */\n getValue(): Date | null {\n return this._value;\n }\n\n /**\n * Sets the field value and renders the formatted string in the input.\n *\n * @param value - The Date to display, or null to clear the field.\n */\n setValue(value: Date | null): this {\n this._value = value;\n this.setText(value ? this.toInputString(value) : \"\");\n\n return this;\n }\n\n /**\n * Writes `text` into the underlying `<input>` element's value through a\n * typed setter so call sites never touch `element.value` directly.\n *\n * @param text - The string to render in the input.\n */\n private setText(text: string): this {\n this._text = text;\n\n const el = this.getElement();\n if (el) {\n DOM.sink.setValue(el, text);\n }\n\n return this;\n }\n\n /**\n * Returns the cached input text. Stays in sync via {@link syncTextFromDom}\n * which is called from the input listener before `onInput`.\n */\n private getText(): string {\n return this._text;\n }\n\n /**\n * Reads the live value from the `<input>` element into the text cache.\n * Confines the raw `element.value` read to a single typed helper so\n * `onInput` can work off {@link getText}.\n */\n private syncTextFromDom(): void {\n const el = this.getElement();\n this._text = el ? DOM.source.getValue(el) : \"\";\n }\n\n /**\n * Enables or disables the fade animation on the dropdown.\n *\n * @param value - true to fade, false for instant open/close.\n */\n setDropdownAnimated(value: boolean): this {\n this._animated = value;\n\n if (this._dropdown) {\n this._dropdown.setAnimated(value);\n }\n\n return this;\n }\n\n /**\n * Returns whether the dropdown fade is enabled.\n *\n * @returns true when the dropdown fades; false when it opens/closes instantly.\n */\n isDropdownAnimated(): boolean {\n return this._animated;\n }\n\n /**\n * Returns or lazily creates the picker dropdown.\n */\n private ensureDropdown(): TimePickerDropdown {\n if (!this._dropdown) {\n this._dropdown = new TimePickerDropdown(\n (h: number, m: number, s: number) => this.onTimeSelected(h, m, s),\n { showSeconds: this._showSeconds },\n );\n this._dropdown.setAnimated(this._animated);\n }\n\n return this._dropdown;\n }\n\n /**\n * Opens the picker dropdown anchored to the input element.\n */\n private openDropdown(): void {\n const dropdown = this.ensureDropdown();\n if (dropdown.isOpen()) {\n return;\n }\n\n const el = this.getElement();\n if (!el) {\n return;\n }\n\n dropdown.showAt(el, this._value);\n }\n\n /**\n * Hides the picker dropdown if it is currently open. Called when the\n * editor's input element loses focus (the cell exits edit mode).\n */\n private closeDropdown(): void {\n if (this._dropdown?.isOpen()) {\n this._dropdown.hideAnimated();\n }\n }\n\n /**\n * Updates the cached value from a typed text edit.\n */\n private onInput(): void {\n this.syncTextFromDom();\n const raw = this.getText();\n\n if (!raw) {\n this._value = null;\n return;\n }\n\n const parts = raw.split(':').map(Number);\n if (parts.some(isNaN)) {\n this._value = null;\n return;\n }\n this._value = new Date(1970, 0, 1, parts[0], parts[1] ?? 0, parts[2] ?? 0);\n }\n\n /**\n * Called when the user picks a time in the dropdown. Updates the value\n * and re-renders the formatted text but does NOT hide the dropdown — the\n * user typically tweaks the minute after picking the hour. The\n * [`CellEditorPool`](/api/component/table/classes/CellEditorPool) commits when\n * focus eventually leaves the editor (e.g. Tab, Enter, click-outside).\n *\n * @param hours - The chosen hour (0-23).\n * @param minutes - The chosen minute (0-59).\n * @param seconds - The chosen second (0-59). Always 0 when `showSeconds` is false.\n */\n private onTimeSelected(hours: number, minutes: number, seconds: number): void {\n const d = new Date(1970, 0, 1, hours, minutes, seconds, 0);\n this.setValue(d);\n }\n\n private toInputString(date: Date): string {\n const h = String(date.getHours()).padStart(2, '0');\n const m = String(date.getMinutes()).padStart(2, '0');\n if (this._showSeconds) {\n const s = String(date.getSeconds()).padStart(2, '0');\n return `${h}:${m}:${s}`;\n }\n return `${h}:${m}`;\n }\n}\n\nconst TimeEditorCallable = callable(TimeEditor);\ntype TimeEditorCallable = TimeEditor;\nexport {\n TimeEditor as _TimeEditor,\n TimeEditorCallable as TimeEditor\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { TextInputCellEditor } from \"~/component/table/cell/editor/TextInputCellEditor.js\";\nimport { blurRelatedTargetHandle } from \"~/component/table/cell/editor/CellEditor.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { LayerManager } from \"~/core/LayerManager.js\";\nimport { DateTimePickerDropdown } from \"~/component/input/DateTimePickerDropdown.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * An in-place editor for date-time cell values.\n *\n * Renders a focusable text input and pops a\n * [`DateTimePickerDropdown`](/api/component/input/classes/DateTimePickerDropdown)\n * (the same class used by [`DateTimeField`](/api/component/input/classes/DateTimeField))\n * on focus. The dropdown suppresses focus loss via `pointerdown`\n * preventDefault so the\n * [`CellEditorPool`](/api/component/table/classes/CellEditorPool)'s blur-to-commit\n * path is not invoked prematurely.\n *\n * @category Components\n */\nclass DateTimeEditor extends TextInputCellEditor<Date | null> {\n\n private _showSeconds: boolean;\n private _value: Date | null = null;\n private _dropdown: DateTimePickerDropdown | null = null;\n private _animated: boolean = true;\n private _text: string = \"\";\n\n constructor(showSeconds: boolean = false) {\n super();\n this._showSeconds = showSeconds;\n\n this.setMaxSize({ width: Number.MAX_SAFE_INTEGER, height: Number.MAX_SAFE_INTEGER });\n this.setBorderRadius('0');\n this.setBorder({ border: \"0px solid transparent\" });\n this.setShadow('inset 0 0 0 1px var(--ts-ui-table-cell-editor-border, rgba(30, 100, 200, 0.6))');\n this.setOutline('none');\n\n Event.addListener(this, \"focus\", () => this.openDropdown());\n Event.addListener(this, \"blur\", (e: FocusEvent) => this.onEditorBlur(e));\n Event.addListener(this, \"input\", () => this.onInput());\n\n this.setType(\"text\");\n this.setInputMode(\"none\");\n this.setAutoComplete(\"off\");\n }\n\n /**\n * Keeps the edit alive while focus moves from the editor input into the\n * picker dropdown — including the embedded time field and its own popped\n * time-picker layer. Without this, focusing the embedded field would blur\n * the editor and the {@link CellEditorPool} would commit + tear down the\n * dropdown mid-interaction.\n *\n * @param relatedTarget - The node handle receiving focus, or null.\n * @returns True when focus landed inside the dropdown's layer tree.\n */\n retainsFocus(relatedTarget: Handle | null): boolean {\n return this._dropdown !== null\n && this._dropdown.isOpen()\n && LayerManager.containsAcrossLayers(this._dropdown, relatedTarget);\n }\n\n /**\n * Returns whether the input is empty.\n *\n * @returns true when both the rendered text and the cached value are empty.\n */\n isEmpty(): boolean {\n return !this.getText() && this._value === null;\n }\n\n /**\n * Returns the currently committed `Date`, or null.\n */\n getValue(): Date | null {\n return this._value;\n }\n\n /**\n * Sets the field value and renders the formatted string in the input.\n *\n * @param value - The Date to display, or null to clear the field.\n */\n setValue(value: Date | null): this {\n this._value = value;\n this.setText(value ? this.toInputString(value) : \"\");\n\n return this;\n }\n\n /**\n * Writes `text` into the underlying `<input>` element's value through a\n * typed setter so call sites never touch `element.value` directly.\n *\n * @param text - The string to render in the input.\n */\n private setText(text: string): this {\n this._text = text;\n\n const el = this.getElement();\n if (el) {\n DOM.sink.setValue(el, text);\n }\n\n return this;\n }\n\n /**\n * Returns the cached input text. Stays in sync via {@link syncTextFromDom}\n * which is called from the input listener before `onInput`.\n */\n private getText(): string {\n return this._text;\n }\n\n /**\n * Reads the live value from the `<input>` element into the text cache.\n * Confines the raw `element.value` read to a single typed helper so\n * `onInput` can work off {@link getText}.\n */\n private syncTextFromDom(): void {\n const el = this.getElement();\n this._text = el ? DOM.source.getValue(el) : \"\";\n }\n\n /**\n * Enables or disables the fade animation on the dropdown.\n *\n * @param value - true to fade, false for instant open/close.\n */\n setDropdownAnimated(value: boolean): this {\n this._animated = value;\n\n if (this._dropdown) {\n this._dropdown.setAnimated(value);\n }\n\n return this;\n }\n\n /**\n * Returns whether the dropdown fade is enabled.\n *\n * @returns true when the dropdown fades; false when it opens/closes instantly.\n */\n isDropdownAnimated(): boolean {\n return this._animated;\n }\n\n /**\n * Returns or lazily creates the picker dropdown.\n */\n private ensureDropdown(): DateTimePickerDropdown {\n if (!this._dropdown) {\n this._dropdown = new DateTimePickerDropdown(\n date => this.onDateTimeSelected(date),\n { showSeconds: this._showSeconds },\n );\n this._dropdown.setAnimated(this._animated);\n }\n\n return this._dropdown;\n }\n\n /**\n * Opens the picker dropdown anchored to the input element. The dropdown's\n * own `\"click-outside\"` mode (driven by\n * [`LayerManager`](/api/core/namespaces/LayerManager)) commits the edit\n * when a pointerdown lands outside the editing surface — including when\n * focus has moved into the embedded time field — via the close thunk\n * installed here.\n */\n private openDropdown(): void {\n const dropdown = this.ensureDropdown();\n if (dropdown.isOpen()) {\n return;\n }\n\n const el = this.getElement();\n if (!el) {\n return;\n }\n\n // The manager dismisses the dropdown on an outside click; route that\n // through the editor so it also commits. The editor input is excluded\n // as the anchor so opening the editor doesn't immediately dismiss.\n dropdown.setCloseHandler(() => this.onOutsideDismiss());\n dropdown.setAnchorElement(el);\n\n dropdown.showAt(el, this._value);\n }\n\n /**\n * Hides the picker dropdown if it is currently open.\n */\n private closeDropdown(): void {\n if (this._dropdown?.isOpen()) {\n this._dropdown.hideAnimated();\n }\n }\n\n /**\n * Closes the dropdown and commits the edit when the manager reports an\n * outside click. This is the path the editor's blur-commit can no longer\n * cover once focus sits in the embedded time field rather than the\n * editor's own input.\n */\n private onOutsideDismiss(): void {\n this.closeDropdown();\n this.requestCommit();\n }\n\n /**\n * Closes the dropdown when the editor input blurs — unless focus moved into\n * the picker surface (the embedded time field or its time-picker layer), in\n * which case the edit stays open. The {@link CellEditorPool}'s blur-commit\n * is suppressed for the same case via {@link retainsFocus}.\n *\n * @param e - The blur event.\n */\n private onEditorBlur(e: FocusEvent): void {\n if (this.retainsFocus(blurRelatedTargetHandle(e))) {\n return;\n }\n\n this.closeDropdown();\n }\n\n /**\n * Updates the cached value from a typed text edit. The display format is\n * \"YYYY-MM-DD HH:MM[:SS]\" with a space separator; `Date.parse` only\n * accepts ISO-8601 reliably with a `T`, so we re-introduce it before\n * parsing.\n */\n private onInput(): void {\n this.syncTextFromDom();\n const raw = this.getText();\n\n if (!raw) {\n this._value = null;\n return;\n }\n\n const d = new Date(raw.replace(' ', 'T'));\n this._value = isNaN(d.getTime()) ? null : d;\n }\n\n /**\n * Called when the user picks a date+time in the dropdown.\n *\n * @param date - The chosen Date.\n */\n private onDateTimeSelected(date: Date): void {\n this.setValue(date);\n }\n\n private toInputString(date: Date): string {\n const y = date.getFullYear();\n const mo = String(date.getMonth() + 1).padStart(2, '0');\n const d = String(date.getDate()).padStart(2, '0');\n const h = String(date.getHours()).padStart(2, '0');\n const mi = String(date.getMinutes()).padStart(2, '0');\n if (this._showSeconds) {\n const s = String(date.getSeconds()).padStart(2, '0');\n return `${y}-${mo}-${d} ${h}:${mi}:${s}`;\n }\n return `${y}-${mo}-${d} ${h}:${mi}`;\n }\n}\n\nconst DateTimeEditorCallable = callable(DateTimeEditor);\ntype DateTimeEditorCallable = DateTimeEditor;\nexport {\n DateTimeEditor as _DateTimeEditor,\n DateTimeEditorCallable as DateTimeEditor\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Event } from \"~/core/Event.js\";\nimport type { Cell } from \"~/component/table/cell/Cell.js\";\nimport { CellEditor, blurRelatedTargetHandle } from \"~/component/table/cell/editor/CellEditor.js\";\nimport type { ForwardedKeyDetail } from \"~/component/table/cell/editor/CellEditor.js\";\nimport { StringEditor } from \"~/component/table/cell/editor/String.js\";\nimport { NumberEditor } from \"~/component/table/cell/editor/Number.js\";\nimport { DateEditor } from \"~/component/table/cell/editor/Date.js\";\nimport { TimeEditor } from \"~/component/table/cell/editor/Time.js\";\nimport { DateTimeEditor } from \"~/component/table/cell/editor/DateTime.js\";\n\n/**\n * Factory callable that constructs a fresh {@link CellEditor} for a given pool key.\n *\n * @category Components\n */\nexport type CellEditorFactory = () => CellEditor<unknown>;\n\n/**\n * Per-table registry that holds at most one shared editor instance per editor variant.\n *\n * Every cell in [`Body`](/api/component/table/classes/Body) used to allocate its own editor up\n * front, but only one cell can be edited at a time. The pool keeps the renderer-per-row model\n * untouched while collapsing the N editors down to one instance per cell-provided key returned\n * from {@link Cell.getEditorKey}. On {@link CellEditorPool.acquire} the editor is lazily\n * constructed (and its blur/keydown listeners wired once), then re-used across every subsequent\n * edit of the same variant.\n *\n * Built-in keys are seeded by the constructor:\n *\n * - `\"string\"`, `\"number\"`, `\"date\"`\n * - `\"time\"` / `\"time:seconds\"`\n * - `\"datetime\"` / `\"datetime:seconds\"`\n *\n * Custom cells can opt in by overriding {@link Cell.getEditorKey} and registering a factory via\n * {@link CellEditorPool.register} before the first edit. Cells that return `null` from\n * `getEditorKey` (e.g. `BooleanCell`, `GlyphCell`, `DefaultCell`) keep their legacy behaviour.\n *\n * @category Components\n */\nexport class CellEditorPool {\n\n private _editors : Map<string, CellEditor<unknown>> = new Map();\n private _factories : Map<string, CellEditorFactory> = new Map();\n private _activeCell: Cell<any> | null = null;\n\n /**\n * Constructs a pool pre-seeded with factories for every built-in typed cell.\n */\n constructor() {\n this._factories.set(\"string\", () => new StringEditor());\n this._factories.set(\"number\", () => new NumberEditor());\n this._factories.set(\"date\", () => new DateEditor());\n this._factories.set(\"time\", () => new TimeEditor(false));\n this._factories.set(\"time:seconds\", () => new TimeEditor(true));\n this._factories.set(\"datetime\", () => new DateTimeEditor(false));\n this._factories.set(\"datetime:seconds\", () => new DateTimeEditor(true));\n }\n\n /**\n * Registers or overrides a factory for the given pool key.\n *\n * @param key - The editor variant key, matching {@link Cell.getEditorKey}.\n * @param factory - A factory that constructs a fresh editor instance.\n * @returns This pool, for method chaining.\n *\n * @remarks If an editor was already cached for the key it is dropped so the new factory runs\n * on the next call to {@link CellEditorPool.acquire}.\n */\n register(key: string, factory: CellEditorFactory): this {\n this._factories.set(key, factory);\n this._editors.delete(key);\n\n return this;\n }\n\n /**\n * Returns the shared editor for `key`, lazily constructing it on first call and marking\n * `cell` as the active edit target.\n *\n * @param key - The editor variant key returned by {@link Cell.getEditorKey}.\n * @param cell - The cell that is starting an edit; receives subsequent blur/keydown events.\n * @returns The shared editor instance, or `null` if no factory is registered for `key`.\n */\n acquire(key: string, cell: Cell<any>): CellEditor<unknown> | null {\n const factory = this._factories.get(key);\n if (!factory) {\n return null;\n }\n\n let editor = this._editors.get(key);\n if (!editor) {\n editor = factory();\n this.wireListeners(editor);\n this._editors.set(key, editor);\n }\n\n this._activeCell = cell;\n\n return editor;\n }\n\n /**\n * Clears the active-cell pointer. Called by {@link Cell} when an edit commits or cancels.\n */\n release(): void {\n this._activeCell = null;\n }\n\n /**\n * Wires `blur` and `keydown` listeners on the shared editor exactly once at construction,\n * dispatching through `activeCell` so the same listener serves every cell that borrows the\n * editor.\n *\n * @param editor - The freshly constructed editor to wire.\n */\n private wireListeners(editor: CellEditor<unknown>): void {\n editor.setCommitRequestHandler(() => this._activeCell?.commitEdit());\n\n // Internal cell-editor wiring: listens on a privately-owned child;\n // see the cell-editor carve-out in ARCHITECTURE.md.\n Event.addListener(editor, \"blur\", (e: FocusEvent) => {\n if (editor.retainsFocus(blurRelatedTargetHandle(e))) {\n return;\n }\n\n this._activeCell?.commitEdit();\n });\n Event.addListener(editor, \"keydown\", (e: CustomEvent<ForwardedKeyDetail>) => {\n this._activeCell?.onKeyDown(e);\n });\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellEditor } from \"~/component/table/cell/editor/CellEditor.js\";\nimport { ComboBox } from \"~/component/input/ComboBox.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { normalizeComboOptions } from \"~/component/table/ColumnConfig.js\";\nimport type { ComboOption } from \"~/component/table/ColumnConfig.js\";\nimport type { SelectableListItem } from \"~/component/list/AbstractSelectableList.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * An in-place editor for constrained-choice (combo-box) cell values.\n *\n * Wraps a [`ComboBox`](/api/component/input/classes/ComboBox) whose option\n * list is fixed to the column's declared values. The combo box keeps DOM\n * focus on its own surface and forwards keystrokes into its dropdown\n * without a focus shift, so a row click never blurs the editor. Both a\n * selection (`\"action\"`) and a genuine blur — focus leaving the editor\n * entirely, e.g. an outside click — commit the edit through the cell's\n * commit-request hook; keydown is proxied to the parent cell so Escape\n * cancels and Enter ends the edit.\n *\n * The editor stores the option *value* (the code round-tripped to the\n * record); an empty selection commits as `null`, mirroring the cell-stack\n * convention that \"no value\" is `null` and not `\"\"`.\n *\n * @category Components\n */\nclass ComboEditor extends CellEditor<String | null> {\n\n private _combo: ComboBox;\n private _value: String | null = null;\n\n /**\n * @param optionList - The column's option set; each entry is a plain\n * string (value === label) or a `{ value, label }` pair. Built into\n * explicit `{ key, label }` dropdown items so the combo box's value\n * is the option value, not its array index.\n */\n constructor(optionList: Array<ComboOption | string>) {\n super(\"div\");\n\n const items: Array<SelectableListItem> = normalizeComboOptions(optionList).map(\n option => ({ key: option.value, label: option.label }),\n );\n\n this._combo = new ComboBox();\n this._combo.setItems(items);\n\n // Strip the combo box's own chrome and apply the shared inset\n // editor border so the surface reads as a cell editor, matching\n // the text-input editors.\n this._combo.setBorder({ border: \"0px solid transparent\" });\n this._combo.setBorderRadius('0');\n this._combo.setShadow('inset 0 0 0 1px var(--ts-ui-table-cell-editor-border, rgba(30, 100, 200, 0.6))');\n\n // A selection (mouse or keyboard) commits the edit, and so does a\n // genuine blur: the combo box keeps DOM focus on its own surface\n // throughout the dropdown's lifetime (row clicks `preventDefault`\n // pointerdown), so a blur means focus left the editor entirely —\n // commit the current value. Both route through the cell's\n // commit-request hook directly rather than re-dispatching a blur\n // for the pool to re-interpret.\n this._combo.on(\"action\", () => this.commitFromCombo());\n // Internal cell-editor wiring: listens on a privately-owned child;\n // see the cell-editor carve-out in ARCHITECTURE.md.\n Event.addListener(this._combo, \"blur\", () => this.commitFromCombo());\n\n // Proxy keydown to the parent cell so Escape cancels and Enter\n // ends the edit, mirroring the text editors' `Cell.onKeyDown` path.\n // Internal cell-editor wiring: listens on a privately-owned child;\n // see the cell-editor carve-out in ARCHITECTURE.md.\n Event.addListener(this._combo, \"keydown\", (evnt: KeyboardEvent) => {\n Event.fireEvent(this, \"keydown\", { detail: {\n key : evnt.key , code : evnt.code , keyCode: evnt.keyCode,\n shiftKey: evnt.shiftKey, ctrlKey: evnt.ctrlKey,\n altKey : evnt.altKey , metaKey: evnt.metaKey\n } });\n });\n\n this.addComponent(this._combo);\n }\n\n /**\n * Returns the cached option value, or `null` when nothing is selected.\n *\n * @returns The current option value, or `null`.\n */\n getValue(): String | null {\n return this._value;\n }\n\n /**\n * Selects the option whose value matches and caches it. `null` and\n * `undefined` clear the selection. A value outside the option set is\n * cached verbatim so the cell round-trips it unchanged.\n *\n * @param value - The option value to select, or `null`/`undefined` to\n * clear the selection.\n */\n setValue(value: String | null): this {\n this._value = value ?? null;\n this._combo.setValue(this._value === null ? \"\" : String(this._value));\n\n return this;\n }\n\n /**\n * Focuses the combo box surface and pops its dropdown so a double-click\n * on the cell lands the user straight in the option list.\n *\n * @param preventScroll - Forwarded to the combo box's focus so a native\n * focus-scroll doesn't desync the table body's own scroll model.\n * @returns This component, for method chaining.\n */\n focus(preventScroll: boolean = false): this {\n this._combo.focus(preventScroll);\n this._combo.openDropdown();\n\n return this;\n }\n\n /**\n * Rebuilds the dropdown's item list from a new option set, preserving\n * the currently selected value, so a shared editor can be reused across\n * rows whose combo options differ.\n *\n * @param optionList - The new option set; same shape as the constructor\n * argument.\n * @returns This editor, for method chaining.\n */\n setOptions(optionList: Array<ComboOption | string>): this {\n const items: Array<SelectableListItem> = normalizeComboOptions(optionList).map(\n option => ({ key: option.value, label: option.label }),\n );\n\n this._combo.setItems(items);\n this._combo.setValue(this._value === null ? \"\" : String(this._value));\n\n return this;\n }\n\n /**\n * Caches the combo box's current value and asks the active cell to\n * commit. Shared by the selection (`\"action\"`) and blur paths; an empty\n * selection commits as `null`. Re-entrant-safe: once the cell has\n * committed it releases the editor, so a follow-on blur is a no-op.\n */\n private commitFromCombo(): void {\n const selected = this._combo.getValue();\n\n this._value = selected ? selected : null;\n this.requestCommit();\n }\n}\n\nconst ComboEditorCallable = callable(ComboEditor);\ntype ComboEditorCallable = ComboEditor;\nexport {\n ComboEditor as _ComboEditor,\n ComboEditorCallable as ComboEditor\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { AbstractStore } from \"~/data/AbstractStore.js\";\nimport { ModelRecord } from \"~/data/ModelRecord.js\";\nimport { Row } from \"~/component/table/Row.js\";\nimport { Cell } from \"~/component/table/cell/Cell.js\";\nimport { CellEditorPool } from \"~/component/table/cell/editor/CellEditorPool.js\";\nimport { ComboEditor } from \"~/component/table/cell/editor/Combo.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { VirtualRowView } from \"~/component/shared/VirtualRowView.js\";\nimport { reduceModifierSelection } from \"~/component/shared/reduceModifierSelection.js\";\nimport { ThemeManager } from \"~/core/Theme.js\";\nimport { Util } from \"~/core/Util.js\";\nimport type { ColumnConfig } from \"~/component/table/ColumnConfig.js\";\nimport { Column } from \"~/component/table/Column.js\";\nimport type { TableHeader } from \"~/component/table/Header.js\";\nimport type { HeaderCell } from \"~/component/table/cell/Header.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * String-literal union of the events emitted by the table {@link Body}.\n * `\"verticalscroll\"` / `\"horizontalscroll\"` fire after the body's virtual\n * scroll position changes, carrying the new pixel offset. `\"selection\"`\n * fires when the selected-record set changes; `\"cellclick\"` fires when a data\n * cell is clicked.\n */\nexport type BodyEvent = \"verticalscroll\" | \"horizontalscroll\" | \"selection\" | \"cellclick\";\n\n/**\n * Payload delivered to a `\"cellclick\"` listener when a data cell is clicked.\n *\n * `field` (the model field name) is the stable column identity; `columnIndex`\n * matches the visible-column order the body exposes via a row's cells and the\n * keyboard-focused column. `rowIndex` is the record's position in the filtered +\n * sorted view (`getVisibleRecords()`), the same basis selection uses — never a\n * pool-slot index. `value` is read live from the record at click time.\n */\nexport interface CellClickEvent {\n /** The clicked row's bound record. */\n record: ModelRecord;\n /** The clicked column's model field name. */\n field: string;\n /** The clicked column's index in visible-column order. */\n columnIndex: number;\n /** `record.get(field)` at click time. */\n value: unknown;\n /** The record's index into the body's visible-records list. */\n rowIndex: number;\n /** The raw DOM mouse event that triggered the click. */\n event: MouseEvent;\n}\n\n/**\n * Returns the index of the cell in `cells` whose element is, or contains, the\n * clicked `target` handle; `-1` when the target lies outside every cell (or is\n * null). Pure with respect to the interned handles — no component state.\n *\n * @param cells - The clicked row's cells, in visible-column order.\n * @param target - The interned click-target handle, or null.\n *\n * @returns The matching cell index, or `-1`.\n *\n * @internal\n */\nexport function resolveClickedColumn(cells: Component[], target: Handle | null): number {\n for (let ci = 0; ci < cells.length; ci++) {\n const cellEl = cells[ci].getElement();\n\n if (cellEl && (cellEl === target || DOM.source.contains(cellEl, target))) {\n return ci;\n }\n }\n\n return -1;\n}\n\nfunction columnWidthsEqual(a: number[], b: number[] | undefined): boolean {\n if (!b) return a.length === 0;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/**\n * Virtual-scrolling body for the Table component.\n *\n * Only the rows visible in the viewport plus SCROLL_BUFFER rows above and below\n * are ever in the DOM. The store is queried on every render so the body always\n * reflects current store state without maintaining a duplicate data array.\n *\n * A fixed pool of Row components (`rowPool`) is reused as the user scrolls.\n * Each pool slot is tracked in `boundIndices`: when a slot is mapped to a new\n * data index, `row.setData()` is called to rebind cell values; if the index\n * hasn't changed (e.g. during a pure resize) the call is skipped, avoiding the\n * text-measurement reflow inside `setText()`.\n *\n * Scrolling is delegated to a `VirtualScroller` that owns the\n * rows-container transform, two custom scrollbar overlays, and the wheel/touch\n * handlers with fling momentum.\n *\n * Re-exported as `TableBody` from the package barrel.\n *\n * @category Components\n */\nclass Body extends VirtualRowView<Row> {\n\n private _store : AbstractStore;\n private _hiddenColumns : Set<string> = new Set();\n private _columns : Column[] = [];\n private _columnConfigs : Map<string, ColumnConfig> = new Map();\n private _rowReadOnly : ((record: ModelRecord) => boolean) | null = null;\n private _cellGeom : Array<Array<{ x: number, w: number, h: number } | null>> = [];\n private _lastBodyWidth : number = 0;\n private _lastColumnWidths: number[] = [];\n private _lastAriaRowCount: number = -1;\n private _rowHeight : number;\n private _storeRefresh : (() => void) | null = null;\n private _selectedRecords : Set<ModelRecord> = new Set();\n private _anchorRecord : ModelRecord | null = null;\n private _focusedColIndex: number = 0;\n private _editorPool : CellEditorPool = new CellEditorPool();\n private _header : TableHeader | null = null;\n private _listeners : ListenerBag<BodyEvent> = new ListenerBag<BodyEvent>();\n\n constructor(store: AbstractStore) {\n super({ tag: \"tbody\" });\n\n this.setOverflow(\"hidden\");\n this.setBackgroundColor(\"var(--ts-ui-input-bg, rgb(255, 255, 255))\");\n this.getAria().setTabIndex(0);\n this.getAria().setRole(\"rowgroup\");\n\n this._store = store;\n this.bindStore(store);\n\n this._rowHeight = this.computeRowHeight();\n\n this.subscribeTheme(() => {\n this._rowHeight = this.computeRowHeight();\n this._boundIndices.fill(-1);\n this.invalidateGeom();\n this.renderWindow();\n });\n }\n\n /**\n * Derives the row height from the shared px line box plus top+bottom cell\n * padding.\n *\n * @remarks `theme.table.cell.height` is intentionally ignored: a fixed pixel\n * height ignores the active line box and clips text when the theme changes\n * the leading. The line box is the additive `font-size + --ts-ui-line-padding`\n * value `Util.lineHeightPx` derives at the root font size, keeping row\n * height in sync with the line box the cells are actually rendered at.\n */\n private computeRowHeight(): number {\n const theme = ThemeManager.getTheme();\n const lineHeight = Util.lineHeightPx();\n const padding = theme.table.cell.padding ?? 2;\n\n return lineHeight + 2 * padding;\n }\n\n /**\n * Returns the live row height. The base's window / geometry math reads it\n * on every call so a theme-driven `_rowHeight` recompute takes effect\n * immediately.\n */\n protected getRowHeight(): number {\n return this._rowHeight;\n }\n\n /**\n * Constructs one pool row and wires each of its cells to the shared editor\n * pool + horizontal scroll-into-view handler. The base's `growRowPool` owns\n * the append + parallel-array bookkeeping.\n *\n * @returns The wired, un-appended pool row.\n */\n protected createPoolRow(): Row {\n const row = this.createRow();\n\n for (const cell of row.getComponents() as Cell<any>[]) {\n cell.setEditorPool(this._editorPool);\n cell.setScrollIntoViewHandler(() => this.scrollColumnIntoView(this._focusedColIndex));\n }\n\n return row;\n }\n\n /**\n * Extends the Body-only `_cellGeom` cache in lockstep with the base pool\n * arrays as each new slot is added.\n */\n protected onPoolRowAdded(): void {\n this._cellGeom.push([]);\n }\n\n /**\n * Subscribes to all relevant store events to trigger a renderWindow refresh.\n *\n * @param store - The store whose events to subscribe to.\n *\n * @remarks The single store-event refresh callback is routed through\n * {@link onStoreChange}, a protected hook that subclasses (e.g.\n * `TreeBody`) override to rebuild per-row indexes before the\n * inherited rebind + render runs.\n */\n private bindStore(store: AbstractStore): void {\n const refresh = () => this.onStoreChange();\n\n this._storeRefresh = refresh;\n\n store.on('load', refresh);\n store.on('add', refresh);\n store.on('remove', refresh);\n store.on('datachange', refresh);\n store.on('beforesync', refresh);\n store.on('sync', refresh);\n }\n\n /**\n * Hook invoked from {@link bindStore}'s store-event callbacks before\n * the row pool is rebound and rendered. Default behaviour clears the\n * bound-index cache and re-renders.\n *\n * @remarks Subclassing seam — `TreeBody` overrides this to rebuild\n * its parent/child index and flatten the visible subtree before\n * delegating to `super.onStoreChange()`. Not for consumer use.\n */\n protected onStoreChange(): void {\n this._boundIndices.fill(-1);\n this.renderWindow();\n }\n\n /**\n * Returns the records visible in the current scroll window. Default\n * behaviour delegates to the store's view (filtered + sorted master\n * collection).\n *\n * @returns The records the row pool should bind to, in display order.\n *\n * @remarks Subclassing seam — `TreeBody` overrides this to return its\n * depth-flattened, expansion-aware visible subtree. Every internal\n * site that needs the visible records — virtual-window math, click\n * dispatch, focus + active-descendant tracking, keyboard nav,\n * scroll-into-view — goes through this method. Not for consumer use.\n */\n protected getVisibleRecords(): ModelRecord[] {\n return this._store.getRecords();\n }\n\n /**\n * Constructs one pool row. Default behaviour returns a plain `Row`\n * bound to the store's model with the current hidden-column and\n * column-config maps.\n *\n * @returns A new `Row` instance.\n *\n * @remarks Subclassing seam — `TreeBody` overrides this to pass the\n * `treeFieldName` so the tree column's cell gets a\n * `TreeCellRenderer`. Not for consumer use.\n */\n protected createRow(): Row {\n return new Row(\n this._store.model,\n undefined,\n this._hiddenColumns,\n this._columnConfigs,\n (record) => this._store.notifyRecordChanged(record),\n );\n }\n\n /**\n * Returns the field name of the column carrying a\n * {@link TreeCellRenderer}, or `undefined` when no column is the\n * tree column. The base returns `undefined`; `TreeBody` overrides\n * to return its `_treeColumn`.\n *\n * @returns The tree column's field name, or `undefined`.\n *\n * @remarks Subclassing seam — forwarded into {@link Row.syncCells}\n * from {@link setHiddenColumns} / {@link setColumnConfigs} so an\n * incremental column-toggle preserves the tree renderer on the\n * surviving cell. Not for consumer use.\n */\n protected getTreeFieldName(): string | undefined {\n return undefined;\n }\n\n /**\n * Updates the ARIA attributes that depend on a row's current data\n * index. Default behaviour writes only `aria-rowindex` (the +2\n * accounts for the 1-based ARIA spec plus the header band).\n *\n * @param row - The pool row whose ARIA attributes to update.\n * @param dataIndex - The row's index into the visible-records list.\n *\n * @remarks Subclassing seam — `TreeBody` overrides this to additionally\n * set `aria-level`, `aria-expanded`, `aria-setsize`, and\n * `aria-posinset` from the flat record entry. Not for consumer use.\n */\n protected computeRowAria(row: Row, dataIndex: number): void {\n row.getAria().setRowIndex(dataIndex + 2);\n }\n\n /**\n * Hook invoked once per pool slot inside the bind loop, after the\n * row has been rebound (when needed) but before the geometry-driven\n * cell layout runs. Default behaviour is a no-op.\n *\n * @param row - The pool row being processed.\n * @param dataIndex - The row's index into the visible-records list.\n * @param wasRebound - `true` when the row was just rebound to a new\n * record on this pass; `false` for a pure scroll where the slot's\n * data index is unchanged.\n *\n * @remarks Subclassing seam — `TreeBody` overrides this to push\n * depth + expansion state through {@link TreeCellRenderer.setTreeState}\n * on the row's tree cell. Not for consumer use.\n */\n protected afterRowBound(_row: Row, _dataIndex: number, _wasRebound: boolean): void {\n // Default implementation is a no-op; subclasses provide behaviour.\n }\n\n /**\n * Resets every pool slot's data-index cache so the next render pass\n * forces a full rebind. Use after the visible-records list has\n * changed shape (sort, expand/collapse, etc.) but the store itself\n * hasn't fired one of the events {@link bindStore} subscribes to.\n *\n * @remarks Subclassing seam — `TreeBody` calls this from\n * {@link TreeBody.setExpanded} before triggering a re-render.\n * Not for consumer use.\n */\n protected invalidateRowBindings(): void {\n this._boundIndices.fill(-1);\n }\n\n /**\n * Returns the data store this body is bound to.\n *\n * @returns The current {@link AbstractStore}.\n *\n * @remarks Exposed at protected scope for subclasses (e.g. `TreeBody`)\n * that need to read model fields when reconstructing pool rows or\n * walking records to rebuild a depth index.\n */\n protected getStore(): AbstractStore {\n return this._store;\n }\n\n /**\n * Returns the set of column field names currently hidden from\n * render.\n *\n * @returns The hidden-column set (do not mutate).\n *\n * @remarks Exposed at protected scope so subclasses can pass the\n * same set into custom pool-row construction.\n */\n protected getHiddenColumns(): Set<string> {\n return this._hiddenColumns;\n }\n\n /**\n * Returns the column-config map keyed by field name.\n *\n * @returns The column-config map (do not mutate).\n *\n * @remarks Exposed at protected scope so subclasses can pass the\n * same map into custom pool-row construction.\n */\n protected getColumnConfigs(): Map<string, ColumnConfig> {\n return this._columnConfigs;\n }\n\n /**\n * Returns the row pool used by the virtual scroll. Each entry is a\n * `Row` whose `data` may be bound to a record or `undefined` when\n * the slot is hidden.\n *\n * @returns The row-pool array (do not mutate the array; mutating\n * individual rows is the caller's responsibility).\n *\n * @remarks Exposed at protected scope so subclasses can walk the\n * pool — e.g. `TreeBody` does this from its `onSubtreeClick`\n * override to find the row whose tree-cell toggle was clicked.\n */\n protected getRowPool(): Row[] {\n return this._rowPool;\n }\n\n /**\n * Clears the cached row geometry (via the base) and additionally the\n * Body-only per-cell geometry so the next renderWindow re-applies positions\n * and sizes for every visible row.\n */\n protected invalidateGeom(): void {\n super.invalidateGeom();\n\n for (let i = 0; i < this._cellGeom.length; i++) {\n this._cellGeom[i] = [];\n }\n }\n\n /**\n * Updates the set of hidden column field names, syncs each pooled\n * row's cells in place to match the new visible-field list, and\n * re-renders.\n *\n * Field names belonging to {@link Column.isUnhideable} columns are stripped\n * from the set so a direct caller cannot bypass the unhideable contract.\n *\n * @param hidden - The new set of field names to hide.\n *\n * @remarks The previous implementation dropped the entire row pool\n * and rebuilt it via `growRowPool`. The in-place sync preserves\n * each row's existing cells (and their renderer / editor / theme\n * listener / sort state / group tint), constructing or removing\n * only the cells whose visibility actually changed.\n */\n setHiddenColumns(hidden: Set<string>): this {\n const filtered = new Set<string>();\n\n for (const name of hidden) {\n const col = this._columns.find(c => c.getField().getName() === name);\n\n if (!col || !col.isUnhideable()) {\n filtered.add(name);\n }\n }\n\n this._hiddenColumns = filtered;\n this.syncPoolCells();\n this.renderWindow();\n\n return this;\n }\n\n /**\n * Supplies the resolved {@link Column} list so the body can read per-column\n * metadata (e.g. `isUnhideable()`) when filtering hidden-column sets.\n *\n * @param columns - The resolved columns in display order.\n *\n * @returns This body, for method chaining.\n */\n setColumns(columns: Column[]): this {\n this._columns = columns;\n this.syncPoolCells();\n this.renderWindow();\n\n return this;\n }\n\n /**\n * Sets the table-wide row-level read-only predicate forwarded from\n * {@link ColumnSpec.rowReadOnly}. Cleared by passing `null`.\n *\n * @param predicate - Returns `true` to mark every cell in the\n * record's row read-only. Called on every rebind; must be O(1)\n * and pure.\n * @returns This body, for method chaining.\n *\n * @remarks Internal wiring called by {@link Table} — not for\n * consumer use. Consumers declare the predicate in the spec.\n */\n setRowReadOnly(predicate: ((record: ModelRecord) => boolean) | null): this {\n this._rowReadOnly = predicate;\n\n return this;\n }\n\n /**\n * Updates the per-field column-config map and re-syncs each pooled\n * row's cells in place so any field-type-driven cell options\n * (e.g. `showSeconds`) and group tints take effect immediately.\n *\n * @param configs - The new column-config map keyed by field name.\n */\n setColumnConfigs(configs: Map<string, ColumnConfig>): this {\n this._columnConfigs = configs;\n this.registerComboEditors(configs);\n this.syncPoolCells();\n this.renderWindow();\n\n return this;\n }\n\n /**\n * Registers a per-column [`ComboEditor`](/api/component/table/classes/ComboEditor)\n * factory on the editor pool for every column declaring `values`, plus\n * every column declaring `cellValues` (a per-record `DynamicCell` combo\n * column). The factory closes over that column's resolved option set —\n * an empty seed for `cellValues` columns, since the real per-row options\n * are injected at edit time by `DynamicCell.prepareEditor` — so each\n * combo column borrows an editor wired to its own choices under the\n * `combo:<field>` key returned by\n * [`ComboCell.getEditorKey`](/api/component/table/classes/ComboCell#geteditorkey)\n * or `DynamicCell.getEditorKey`.\n *\n * `register` overwrites and drops any cached editor, so re-applying\n * configs with new options rebuilds the editor on the next edit.\n *\n * @param configs - The column-config map keyed by field name.\n */\n private registerComboEditors(configs: Map<string, ColumnConfig>): void {\n for (const [field, config] of configs) {\n const values = config.values;\n\n if (values && values.length > 0) {\n this._editorPool.register(`combo:${field}`, () => new ComboEditor(values));\n } else if (config.cellValues) {\n this._editorPool.register(`combo:${field}`, () => new ComboEditor([]));\n }\n }\n }\n\n /**\n * Walks every pool row and asks it to reconcile its cell set\n * against the current `_hiddenColumns` + `_columnConfigs`, then\n * invalidates the per-slot geometry caches so the next\n * `renderWindow` re-positions cells against the new column count.\n */\n private syncPoolCells(): void {\n const treeFieldName = this.getTreeFieldName();\n\n for (let i = 0; i < this._rowPool.length; i++) {\n const row = this._rowPool[i];\n\n row.syncCells(\n this._store.model,\n this._hiddenColumns,\n this._columnConfigs,\n treeFieldName,\n );\n\n // Newly-shown cells need the editor pool wired so in-place\n // editing keeps working. `setEditorPool` is idempotent for\n // surviving cells.\n for (const cell of row.getComponents() as Cell<any>[]) {\n cell.setEditorPool(this._editorPool);\n cell.setScrollIntoViewHandler(() => this.scrollColumnIntoView(this._focusedColIndex));\n }\n\n // Per-slot geometry is keyed by cell position; both the\n // column count and per-cell (x, w, h) have changed.\n this._rowGeom[i] = null;\n this._cellGeom[i] = [];\n }\n }\n\n /**\n * Swaps the store, unsubscribing from the old one and rebinding to the new one.\n *\n * @param store - The new store to bind to the body.\n */\n setStore(store: AbstractStore): this {\n if (this._storeRefresh) {\n const old = this._store;\n\n (['load', 'add', 'remove', 'datachange', 'beforesync', 'sync'] as const).forEach(e =>\n old.off(e, this._storeRefresh!)\n );\n }\n\n this._store = store;\n this.bindStore(store);\n this.invalidateGeom();\n\n if (this.getElement()) {\n // Route through `onStoreChange` so subclasses (e.g. `TreeBody`)\n // can rebuild their per-row index against the new store before\n // the inherited rebind + render runs. The base implementation\n // is equivalent to the previous `_boundIndices.fill(-1) +\n // renderWindow()` inline pair.\n this.onStoreChange();\n }\n\n return this;\n }\n\n /**\n * Initializes the body element, constructs the `VirtualScroller`, and\n * wires keyboard and focus listeners.\n *\n * @param element - Optional. The element handle to initialize with; falls back to `getElement()`.\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element || this.getElement();\n if (!el) {\n return this;\n }\n\n this.initScroller(el);\n\n Event.addListener(this, \"focus\", this.onFocus);\n Event.addListener(this, \"keydown\", this.onKeyDown);\n\n // One subtree click listener replaces the per-row listener that\n // growRowPool used to install. Walk up from the event target to find\n // the matching pool row; identical complexity per click, one window\n // registration regardless of pool size. Routed through\n // `onSubtreeClick` so subclasses (e.g. `TreeBody`) can intercept\n // clicks on subtree-owned widgets like the expand/collapse toggle.\n Event.addSubtreeListener(this, \"click\", this.onSubtreeClick);\n\n this.renderWindow();\n\n return this;\n }\n\n /**\n * On every scroller tick, re-renders the window and then emits the\n * unconditional scroll events consumers mirror (the header translate and\n * the pinned-side body). Overrides the base default, which only re-renders.\n */\n protected onScrollerTick(): void {\n this.renderWindow();\n\n if (this._scroller) {\n this.emit(\"verticalscroll\", this._scroller.getScrollY());\n this.emit(\"horizontalscroll\", this._scroller.getScrollX());\n }\n }\n\n /** Refreshes the active-descendant pointer and focus ring when the body gains focus. */\n private onFocus(): void {\n this._updateActiveDescendant();\n this._updateFocusStyle();\n }\n\n /**\n * Returns the shared editor pool that backs in-place editing for every cell in this body.\n *\n * @returns The {@link CellEditorPool} owned by this body.\n *\n * @remarks Use this to register a custom editor factory for a cell-specific key. Built-in\n * factories for the seven standard typed cells are seeded automatically.\n */\n getEditorPool(): CellEditorPool {\n return this._editorPool;\n }\n\n /**\n * Recomputes the visible row window, rebinds changed rows from the pool, and hides excess rows.\n *\n * @param bodyWidth - Optional. The total body width in pixels; cached and reused on scroll updates.\n * @param columnWidths - Optional. The per-column widths in pixels; derived from bodyWidth when omitted.\n */\n renderWindow(bodyWidth?: number, columnWidths?: number[]) {\n const element = this.getElement();\n if (!element || !this._scroller) {\n return;\n }\n\n const scroller = this._scroller;\n const records = this.getVisibleRecords();\n const totalRows = records.length;\n\n // Capture scroll positions before clampToContent / layoutScrollbars\n // (called below) potentially shrink them in place. Those calls don't\n // go through setScrollX/Y, so the VirtualScroller's onScroll hook\n // never fires — without an explicit notification here the header's\n // horizontal translate would stay stuck at the pre-clamp value when\n // a widen-to-fit layout drops scrollX back toward 0.\n const prevScrollX = scroller.getScrollX();\n const prevScrollY = scroller.getScrollY();\n\n this.updateColumnWidthCache(bodyWidth, columnWidths);\n\n // Loose-clamp scroll positions against the new content sizes before\n // reading them for the window calc.\n const totalHeight = totalRows * this._rowHeight;\n const totalColumnWidth = this._lastColumnWidths.reduce((s, w) => s + w, 0);\n const totalContentWidth = Math.max(this._lastBodyWidth, totalColumnWidth);\n\n scroller.clampToContent(totalContentWidth, totalHeight);\n\n const visibleHeight = this.getHeight() || 0;\n const win = this.computeVisibleWindow(scroller.getScrollY(), visibleHeight, totalRows);\n\n const poolTarget = this.computePoolTarget(win.windowSize, visibleHeight, totalRows);\n this.growRowPool(poolTarget);\n\n const rowWidth = Math.max(this._lastBodyWidth, totalColumnWidth);\n const fieldCount = this._store.model.getFields()\n .filter(f => !this._hiddenColumns.has(f.getName()))\n .length;\n const fallback = fieldCount > 0 ? rowWidth / fieldCount : rowWidth;\n\n this.bindAndPositionRows(win.firstRow, win.windowSize, rowWidth, fallback, records);\n this.hideExcessPoolRows(win.windowSize);\n\n if (totalRows !== this._lastAriaRowCount) {\n this.getAria().setRowCount(totalRows);\n this._lastAriaRowCount = totalRows;\n }\n\n scroller.layoutScrollbars(totalContentWidth, totalHeight);\n\n const newScrollX = scroller.getScrollX();\n const newScrollY = scroller.getScrollY();\n if (newScrollX !== prevScrollX) {\n this.emit(\"horizontalscroll\", newScrollX);\n }\n if (newScrollY !== prevScrollY) {\n this.emit(\"verticalscroll\", newScrollY);\n }\n\n this._updateFocusStyle();\n }\n\n /**\n * Caches incoming bodyWidth / columnWidths from a layout-driven call and\n * invalidates the per-row geometry cache when either changes.\n *\n * @param bodyWidth - The new body width in pixels, or undefined to leave the cache untouched.\n * @param columnWidths - The new per-column widths in pixels.\n */\n private updateColumnWidthCache(bodyWidth?: number, columnWidths?: number[]): void {\n if (bodyWidth === undefined) {\n return;\n }\n\n const widthsChanged = this._lastBodyWidth !== bodyWidth\n || !columnWidthsEqual(this._lastColumnWidths, columnWidths);\n\n this._lastBodyWidth = bodyWidth;\n this._lastColumnWidths = columnWidths ?? [];\n\n if (widthsChanged) {\n this.invalidateGeom();\n }\n }\n\n /**\n * Binds visible pool slots to their data records and positions each row +\n * its cells. Skips data rebind when the slot's bound index hasn't changed;\n * skips geometry writes when the row geometry hasn't changed; skips cell\n * layout when the cell geometry hasn't changed.\n *\n * @param firstRow - The first data index covered by the visible window.\n * @param windowSize - The number of rows in the window.\n * @param rowWidth - The horizontal extent of each row in pixels.\n * @param fallback - Fallback column width for fields without an explicit width.\n * @param records - The current store records (passed in so this helper doesn't re-query).\n *\n * @remarks `protected` so subclasses (e.g. `TreeBody`) can wrap the\n * standard bind + position pass with their own post-bind work\n * (depth / toggle updates). Not for consumer use.\n */\n protected bindAndPositionRows(firstRow: number, windowSize: number, rowWidth: number, fallback: number, records: ModelRecord[]): void {\n const rowHeight = this._rowHeight;\n\n for (let i = 0; i < windowSize; i++) {\n const row = this._rowPool[i];\n const dataIndex = firstRow + i;\n const wasRebound = this._boundIndices[i] !== dataIndex;\n\n if (wasRebound) {\n row.setData(records[dataIndex]);\n\n this._boundIndices[i] = dataIndex;\n row.setStripe(dataIndex % 2 === 1); // odd logical rows carry the zebra stripe; set before the paint below\n this.updateRowVisualState(i);\n this.computeRowAria(row, dataIndex);\n this.applyReadOnlyState(row, records[dataIndex]);\n }\n\n this.afterRowBound(row, dataIndex, wasRebound);\n\n this.applyRequiredEmptyState(row, records[dataIndex]);\n\n this.positionRow(i, dataIndex * rowHeight, rowWidth);\n\n const cells = row.getComponents();\n const cellRow = this._cellGeom[i];\n let x = 0;\n\n for (let ci = 0; ci < cells.length; ci++) {\n const cell = cells[ci];\n const colW = this._lastColumnWidths[ci] ?? fallback;\n const prevCell = cellRow[ci];\n const cellChanged = !prevCell || prevCell.x !== x || prevCell.w !== colW || prevCell.h !== rowHeight;\n\n if (cellChanged) {\n cell.setAutoCommitStyle(false);\n cell.setX(x);\n cell.setY(0);\n cell.setWidth(colW);\n cell.setHeight(rowHeight);\n cell.setAutoCommitStyle(true);\n cellRow[ci] = { x: x, w: colW, h: rowHeight };\n if (!prevCell) {\n cell.getAria().setColIndex(ci + 1);\n }\n // Geometry change requires a full layout pass so the\n // renderer/editor (Card-layout siblings) re-fit. Pure data\n // rebinds don't need this because renderers with\n // setAutoMeasure(false) don't resize on text changes.\n cell.doLayout();\n }\n\n x += colW;\n }\n }\n }\n\n /**\n * Default subtree-click handler — walks up from the event target to\n * find the pool row that owns the click, then dispatches to\n * {@link onRowClick}. Subclasses (e.g. `TreeBody`) override this to\n * intercept clicks on subtree-owned widgets such as the\n * expand/collapse toggle.\n *\n * @param e - The bubbled click event.\n *\n * @remarks Subclassing seam — not for consumer use.\n */\n protected onSubtreeClick(e: MouseEvent): void {\n // Filter synthetic \"click\" events. `Checkbox.setSelected` dispatches\n // a `CustomEvent(\"click\")` on its root for backward-compat with\n // `on(\"action\", fn)` consumers; during a scroll rebind, the Active\n // column's cell receives a programmatic `setValue` for every pool\n // slot, so a flurry of synthetic clicks bubbles up here and would\n // each fire `onRowClick` — selecting whichever record happens to be\n // bound to that slot at the moment, effectively dragging the\n // selection downward with the scroll.\n if (!(e instanceof MouseEvent)) {\n return;\n }\n\n let node: Handle | null = e.target === null ? null : DOM.source.intern(e.target);\n\n while (node) {\n const row = this._rowPool.find(r => r.getElement() === node);\n\n if (row) {\n this.onRowClick(row, e);\n return;\n }\n\n node = DOM.source.getParentElement(node);\n }\n }\n\n /**\n * Handles a row click, updating selection with support for ctrl/cmd and shift modifiers.\n *\n * @param row - The pool row that was clicked.\n * @param e - The mouse event.\n */\n private onRowClick(row: Row, e: MouseEvent): void {\n const record = row.getData() ?? null;\n if (!record) return;\n\n const records = this.getVisibleRecords();\n\n this._anchorRecord = reduceModifierSelection(\n this._selectedRecords,\n this._anchorRecord,\n record,\n r => records.indexOf(r),\n i => records[i],\n { ctrl: e.ctrlKey || e.metaKey, shift: e.shiftKey },\n );\n\n this._boundIndices.forEach((dataIdx, i) => {\n if (dataIdx !== -1) this.updateRowVisualState(i);\n });\n\n this.notifySelectionChange();\n\n // Determine which column was clicked and update focused cell\n const targetHandle = e.target === null ? null : DOM.source.intern(e.target);\n const cells = row.getComponents();\n const columnIndex = resolveClickedColumn(cells, targetHandle);\n\n if (columnIndex >= 0) {\n this._focusedColIndex = columnIndex;\n }\n\n // Don't steal focus from an active cell editor (e.g. <input type=\"date\">).\n const targetTag = targetHandle === null ? \"\" : DOM.source.getTagName(targetHandle);\n if (targetTag !== 'INPUT' && targetTag !== 'TEXTAREA' && targetTag !== 'SELECT') {\n this.focus();\n }\n\n this._updateFocusStyle();\n this._updateActiveDescendant();\n\n // Fire the column-aware cell-click event last, after selection and\n // focus have settled, so it is purely additive. Skip clicks that land\n // inside the row but outside any cell (should not happen for a <td>\n // grid, but keeps the emit total).\n if (columnIndex >= 0) {\n const field = row.getFieldNames()[columnIndex];\n\n this.emit(\"cellclick\", {\n record,\n field,\n columnIndex,\n value: record.get(field),\n rowIndex: this.getVisibleRecords().indexOf(record),\n event: e,\n });\n }\n }\n\n /**\n * Sets the selected record set to contain exactly the given record (or clears selection).\n *\n * @param record - The record to select, or null to clear the selection.\n */\n selectRecord(record: ModelRecord | null): void {\n this._selectedRecords.clear();\n this._anchorRecord = record;\n\n if (record) {\n this._selectedRecords.add(record);\n }\n\n this._boundIndices.forEach((dataIdx, i) => {\n if (dataIdx !== -1) this.updateRowVisualState(i);\n });\n\n this.notifySelectionChange();\n }\n\n /**\n * Returns the most recently anchored selected record, or null if the selection is empty.\n *\n * @returns The anchor {@link ModelRecord}, or null.\n */\n getSelectedRecord(): ModelRecord | null {\n return this._anchorRecord && this._selectedRecords.has(this._anchorRecord)\n ? this._anchorRecord\n : (this._selectedRecords.size > 0 ? [...this._selectedRecords][0] : null);\n }\n\n /**\n * Returns all currently selected records.\n *\n * @returns An array of selected {@link ModelRecord} instances.\n */\n getSelectedRecords(): ModelRecord[] {\n return [...this._selectedRecords];\n }\n\n /**\n * Replaces the selected-record set with exactly the given records.\n * Mirrors {@link selectRecord} but accepts a multi-record list.\n *\n * @param records - The records that should appear selected. The\n * first record (if any) becomes the new anchor.\n */\n setSelectedRecords(records: ModelRecord[]): void {\n this._selectedRecords.clear();\n this._anchorRecord = records.length > 0 ? records[0] : null;\n\n for (const record of records) {\n this._selectedRecords.add(record);\n }\n\n this._boundIndices.forEach((dataIdx, i) => {\n if (dataIdx !== -1) this.updateRowVisualState(i);\n });\n\n this.notifySelectionChange();\n }\n\n /**\n * Registers a listener for one of this body's events.\n * `\"verticalscroll\"` fires after the body scrolls vertically with the\n * new `scrollY`; `\"horizontalscroll\"` fires after a horizontal scroll\n * with the new `scrollX`; `\"selection\"` fires with the current\n * selected-record array; `\"cellclick\"` fires when a data cell is clicked,\n * carrying the clicked record, the column's field name and visible index,\n * the cell value, the record's row index in the visible-records view, and\n * the raw mouse event.\n *\n * @param event - The event name.\n * @param listener - Receives the new pixel offset along the scroll axis\n * (scroll events), the selected records (`\"selection\"`), or the\n * cell-click payload (`\"cellclick\"`).\n *\n * @returns This body, for method chaining.\n *\n * @remarks `\"verticalscroll\"` is used by\n * [`PinnedTable`](/api/component/table/classes/PinnedTable) to mirror\n * `scrollY` from the scroll-side body into the pinned-side body;\n * `\"horizontalscroll\"` is used by `Table` to mirror `scrollX` into the\n * header's transform so column headers stay aligned with the body cells\n * they label. The listeners fire from the `VirtualScroller`'s\n * onScroll hook (see `init`) — the body uses transform-based virtual\n * scroll, so the native DOM `scroll` event never fires.\n */\n on(event: \"verticalscroll\", listener: (scrollTop: number) => void): this;\n on(event: \"horizontalscroll\", listener: (scrollLeft: number) => void): this;\n on(event: \"selection\", listener: (records: ModelRecord[]) => void): this;\n on(event: \"cellclick\", listener: (e: CellClickEvent) => void): this;\n on(event: BodyEvent, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered scroll listener. The exact callback\n * reference must match the one passed to {@link on}.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This body, for method chaining.\n */\n off(event: BodyEvent, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every listener registered for `event` with the new scroll\n * offset, in registration order.\n *\n * @param event - The event to emit.\n * @param payload - The scroll offset (scroll events) or the selected\n * records (`\"selection\"`).\n */\n protected emit(event: \"verticalscroll\" | \"horizontalscroll\", offset: number): void;\n protected emit(event: \"selection\", records: ModelRecord[]): void;\n protected emit(event: \"cellclick\", detail: CellClickEvent): void;\n protected emit(event: BodyEvent, payload: number | ModelRecord[] | CellClickEvent): void {\n this._listeners.fire(event, payload);\n }\n\n /** Fire `\"selection\"` with the current selection. */\n private notifySelectionChange(): void {\n this.emit(\"selection\", this.getSelectedRecords());\n }\n\n /**\n * Scrolls the body so the given record is visible at the top.\n *\n * @param record - The record to scroll into view.\n */\n scrollToRecord(record: ModelRecord): void {\n const idx = this.getVisibleRecords().indexOf(record);\n if (idx === -1) {\n return;\n }\n\n this.setScrollY(idx * this._rowHeight);\n }\n\n /**\n * Computes the read-only union per cell and forwards it to\n * {@link Cell.setReadOnly}. Runs inside the rebind block once per\n * row.\n *\n * The union is OR-composed from three sources:\n *\n * 1. Column-level static flag from {@link ColumnConfig.readOnly}.\n * 2. Spec-level row predicate from {@link ColumnSpec.rowReadOnly}\n * (cached in `_rowReadOnly`).\n * 3. Per-column per-record predicate from\n * {@link ColumnConfig.cellReadOnly}.\n *\n * Source 1 is read from the column config rather than the cell's\n * current `_readOnly` flag — a previous bind may have marked the\n * cell read-only via a dynamic predicate, and re-reading the cell\n * state would make a positive predicate result sticky once a row\n * went read-only.\n *\n * @param row - The pool row being rebound.\n * @param record - The record now bound to that row.\n */\n private applyReadOnlyState(row: Row, record: ModelRecord): void {\n const rowOverride = this._rowReadOnly?.(record) === true;\n const cells = row.getComponents() as Cell<any>[];\n const fieldNames = row.getFieldNames();\n\n for (let i = 0; i < cells.length; i++) {\n const cell = cells[i];\n const fieldName = fieldNames[i];\n const config = this._columnConfigs.get(fieldName);\n const colStatic = config?.readOnly === true;\n const cellPredOk = config?.cellReadOnly?.(record) === true;\n const union = colStatic || rowOverride || cellPredOk;\n\n cell.setReadOnly(union);\n }\n }\n\n /**\n * Returns whether `value` counts as \"empty\" for the required-cell\n * tint: `null`, `undefined`, or `''`. `0` and `false` are legit\n * values and are NOT empty; an unset boolean (`null`/`undefined`,\n * rendered indeterminate) IS empty.\n *\n * @param value - The raw record value to test.\n * @returns `true` when the value is empty.\n */\n private static isEmptyValue(value: unknown): boolean {\n return value === null || value === undefined || value === '';\n }\n\n /**\n * Computes the required union per cell and forwards it, AND-ed with\n * emptiness, to {@link Cell.setRequiredEmpty}. Unlike\n * {@link applyReadOnlyState}, this runs on every render (not gated\n * on `wasRebound`) because the tint depends on the cell's current\n * value, which changes on in-place edits — a commit cascades\n * through `store.notifyRecordChanged` back into a `renderWindow`\n * pass, and this must re-run then to clear a filled cell's tint.\n * `setRequiredEmpty` is idempotent, so an unchanged cell costs one\n * comparison.\n *\n * The union is OR-composed from two sources:\n *\n * 1. Column-level static flag from {@link ColumnConfig.required}.\n * 2. Per-column per-record predicate from\n * {@link ColumnConfig.requiredPredicate}.\n *\n * @param row - The pool row being rendered.\n * @param record - The record currently bound to that row.\n */\n private applyRequiredEmptyState(row: Row, record: ModelRecord): void {\n const cells = row.getComponents() as Cell<any>[];\n const fieldNames = row.getFieldNames();\n\n for (let i = 0; i < cells.length; i++) {\n const fieldName = fieldNames[i];\n const config = this._columnConfigs.get(fieldName);\n const required = config?.required === true\n || config?.requiredPredicate?.(record) === true;\n const empty = Body.isEmptyValue(record.get(fieldName));\n\n cells[i].setRequiredEmpty(required && empty);\n }\n }\n\n /**\n * Applies selection highlight or normal visual state to the pool row at index i.\n *\n * @param i - The zero-based index into the row pool.\n */\n private updateRowVisualState(i: number): void {\n const dataIdx = this._boundIndices[i];\n if (dataIdx === -1) {\n return;\n }\n\n const record = this.getVisibleRecords()[dataIdx];\n if (!record) {\n return;\n }\n\n const row = this._rowPool[i];\n const rowEl = row.getElement()!;\n const isSelected = this._selectedRecords.has(record);\n\n // Per-record ephemeral selection highlight on a pooled row re-bound to a\n // different record on every render. Routing this through cached Component\n // setters would persist it into _options and replay it onto the next record\n // bound to this reused row, so write/remove the inline styles directly.\n if (isSelected) {\n DOM.sink.apply(rowEl, { style: {\n 'background-color': 'var(--ts-ui-table-row-selected, rgba(30, 100, 200, 0.15))',\n 'box-shadow': 'var(--ts-ui-table-row-selected-border, none)',\n } });\n } else {\n DOM.sink.apply(rowEl, { style: { 'box-shadow': null } });\n row.updateVisualState();\n }\n\n row.getAria().setSelected(isSelected);\n }\n\n /**\n * Internal wiring called by [`Table`](/api/component/table/classes/Table) —\n * not for consumer use. Hands the Body a reference to its sibling TableHeader so\n * `_updateFocusStyle` can mirror the focused column index onto the header\n * cells. Consumers instantiating `Body` standalone may leave this unset; the\n * header-side indicator is then simply skipped.\n *\n * @param header - The TableHeader sibling owned by the same Table.\n *\n * @returns This component, for method chaining.\n */\n setHeader(header: TableHeader): this {\n this._header = header;\n\n return this;\n }\n\n /**\n * Applies a focus ring to the cell at `_focusedColIndex` in the anchor row, clearing it from all other cells.\n *\n * @remarks Called after every navigation and after `renderWindow` re-binds pool slots.\n * Also mirrors the focused column index onto the linked header cells (when\n * one has been wired in via `setHeader`) so the header shows the matching\n * column indicator. `protected` so subclasses (e.g. `TreeBody`) can\n * refresh the focus indicator after a programmatic navigation. Not\n * for consumer use.\n */\n protected _updateFocusStyle(): void {\n // Per-cell ephemeral focus outline on pooled cells re-bound to different\n // records on every render. Routing this through cached Component setters\n // would persist it into _options and replay it onto the next record bound\n // to the reused cell, so write/remove the inline styles directly.\n for (const row of this._rowPool) {\n for (const cell of row.getComponents()) {\n const el = cell.getElement();\n\n if (el) {\n DOM.sink.apply(el, { style: { \"outline\": null, \"outline-offset\": null } });\n }\n }\n }\n\n const headerCells: HeaderCell[] | null = this._header !== null\n ? this._header.getColumns() as HeaderCell[]\n : null;\n\n if (headerCells !== null) {\n for (const cell of headerCells) {\n cell.setColumnFocused(false);\n }\n }\n\n if (!this._anchorRecord) {\n return;\n }\n\n if (headerCells !== null) {\n for (let i = 0; i < headerCells.length; i++) {\n headerCells[i].setColumnFocused(i === this._focusedColIndex);\n }\n }\n\n const anchorIdx = this.getVisibleRecords().indexOf(this._anchorRecord);\n const poolSlotIdx = this._boundIndices.indexOf(anchorIdx);\n\n if (poolSlotIdx < 0) {\n return;\n }\n\n const cells = this._rowPool[poolSlotIdx].getComponents();\n const cell = cells[this._focusedColIndex];\n\n if (cell) {\n const el = cell.getElement();\n\n if (el) {\n // Pooled-cell ephemeral focus style; see note at method top.\n DOM.sink.apply(el, { style: {\n \"outline\": \"var(--ts-ui-indicator-selection, 1px dashed rgb(120, 170, 240))\",\n \"outline-offset\": \"-1px\",\n } });\n }\n }\n }\n\n /**\n * Sets `aria-activedescendant` on the body container to point at the focused cell (or row).\n *\n * @remarks Must be called after `renderWindow()` so the pool slot\n * for the anchor record is guaranteed in the DOM. `protected` so\n * subclasses (e.g. `TreeBody`) can refresh the active-descendant\n * pointer after a programmatic navigation. Not for consumer use.\n */\n protected _updateActiveDescendant(): void {\n if (!this._anchorRecord) {\n this.getAria().setActiveDescendant(\"\");\n\n return;\n }\n\n const anchorIdx = this.getVisibleRecords().indexOf(this._anchorRecord);\n const poolSlotIdx = this._boundIndices.indexOf(anchorIdx);\n\n if (poolSlotIdx < 0) {\n this.getAria().setActiveDescendant(\"\");\n\n return;\n }\n\n const cells = this._rowPool[poolSlotIdx].getComponents();\n const cell = cells[this._focusedColIndex];\n\n if (cell) {\n this.getAria().setActiveDescendant(cell.getId());\n } else {\n this.getAria().setActiveDescendant(this._rowPool[poolSlotIdx].getId());\n }\n }\n\n /**\n * Handles keyboard navigation: ArrowUp/Down/Home/End move row selection; ArrowLeft/Right\n * move column focus; PageUp/Down move by a viewport-height page; Enter starts cell edit.\n *\n * @param e - The keyboard event fired on the body element.\n *\n * @remarks `protected` so subclasses (e.g. `TreeBody`) can intercept\n * additional keys (ArrowRight/Left for expand/collapse) and delegate\n * the rest to `super.onKeyDown`. Not for consumer use.\n */\n protected onKeyDown(e: KeyboardEvent): void {\n const records = this.getVisibleRecords();\n\n if (records.length === 0) {\n return;\n }\n\n const navigable = new Set([\n 'ArrowDown', 'ArrowUp', 'Home', 'End',\n 'ArrowLeft', 'ArrowRight', 'PageUp', 'PageDown', 'Enter', ' '\n ]);\n\n if (!navigable.has(e.key)) {\n return;\n }\n\n e.preventDefault();\n\n // Column navigation — no row change needed\n if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {\n const visibleColCount = this._store.model.getFields()\n .filter(f => !this._hiddenColumns.has(f.getName())).length;\n\n if (e.key === 'ArrowLeft') {\n this._focusedColIndex = Math.max(0, this._focusedColIndex - 1);\n } else {\n this._focusedColIndex = Math.min(visibleColCount - 1, this._focusedColIndex + 1);\n }\n\n this._updateActiveDescendant();\n this._updateFocusStyle();\n\n return;\n }\n\n // Enter/Space — start editing the focused cell\n if (e.key === 'Enter' || e.key === ' ') {\n if (!this._anchorRecord) {\n return;\n }\n\n const anchorIdx = records.indexOf(this._anchorRecord);\n const poolSlotIdx = this._boundIndices.indexOf(anchorIdx);\n\n if (poolSlotIdx < 0) {\n return;\n }\n\n const cells = this._rowPool[poolSlotIdx].getComponents();\n const cell = cells[this._focusedColIndex];\n\n if (cell instanceof Cell) {\n const typedCell = cell as Cell<unknown>;\n\n typedCell.on(\"editend\", () => {\n this.focus();\n this._updateFocusStyle();\n this._updateActiveDescendant();\n });\n\n typedCell.startEdit();\n }\n\n return;\n }\n\n // Row navigation\n const currentIdx = this._anchorRecord ? records.indexOf(this._anchorRecord) : -1;\n const pageSize = this.computePageSize();\n let newIdx: number;\n\n if (e.key === 'ArrowDown') {\n newIdx = currentIdx < 0 ? 0 : Math.min(currentIdx + 1, records.length - 1);\n } else if (e.key === 'ArrowUp') {\n newIdx = currentIdx < 0 ? 0 : Math.max(currentIdx - 1, 0);\n } else if (e.key === 'PageDown') {\n newIdx = currentIdx < 0 ? 0 : Math.min(currentIdx + pageSize, records.length - 1);\n } else if (e.key === 'PageUp') {\n newIdx = currentIdx < 0 ? 0 : Math.max(currentIdx - pageSize, 0);\n } else if (e.key === 'Home') {\n newIdx = 0;\n } else {\n newIdx = records.length - 1;\n }\n\n const newAnchor = records[newIdx];\n\n this.selectRecord(newAnchor);\n this.scrollRecordIntoView(newAnchor);\n this.renderWindow();\n this._updateActiveDescendant();\n }\n\n /**\n * Scrolls the body so the given record is visible, without moving the viewport unless necessary.\n *\n * @param record - The record to scroll into view.\n *\n * @remarks `protected` so subclasses (e.g. `TreeBody`) can keep\n * keyboard-driven navigation inside the scroll viewport. Not for\n * consumer use.\n */\n protected scrollRecordIntoView(record: ModelRecord): void {\n this.scrollRowIntoView(this.getVisibleRecords().indexOf(record));\n }\n\n /**\n * Scrolls the body horizontally so the column at `colIndex` is fully\n * visible, without moving the viewport unless necessary. The horizontal\n * mirror of {@link scrollRecordIntoView}.\n *\n * @param colIndex - The visible-column index to reveal.\n *\n * @remarks Driving the shared `VirtualScroller` keeps the header\n * translate and the scrollbar thumb in sync with the move. This is why an\n * inline edit routes through here rather than relying on the browser's\n * native focus-scroll: that scroll shifts only the clipped content layer\n * and leaves the header + scrollbar behind. `protected` so subclasses can\n * reuse it. Not for consumer use.\n */\n protected scrollColumnIntoView(colIndex: number): void {\n const widths = this._lastColumnWidths;\n\n if (!this._scroller || colIndex < 0 || colIndex >= widths.length) {\n return;\n }\n\n let left = 0;\n for (let i = 0; i < colIndex; i++) {\n left += widths[i];\n }\n const right = left + widths[colIndex];\n const scrollLeft = this._scroller.getScrollX();\n const viewportWidth = this.getWidth() || 0;\n const visibleRight = scrollLeft + viewportWidth;\n\n let target = scrollLeft;\n if (left < scrollLeft) {\n target = left;\n } else if (right > visibleRight) {\n target = right - viewportWidth;\n }\n if (target !== scrollLeft) {\n this.setScrollX(target);\n }\n }\n}\n\nconst BodyCallable = callable(Body);\ntype BodyCallable = Body;\nexport {\n Body as _Body,\n BodyCallable as Body\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { Row } from \"~/component/table/Row.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * The footer section of a table, rendered as a `<tfoot>` element.\n *\n * Contains a single {@link Row} and delegates width/height changes to it.\n *\n * Re-exported as `TableFooter` from the package barrel.\n *\n * @category Components\n */\nclass FooterRow extends Component {\n\n constructor() {\n super({ tag: \"tfoot\" });\n\n this.setBorder({ borderTop: \"1px solid var(--ts-ui-border-color, black)\" });\n // Apply the surface as both a colour and an image so a flat-colour\n // theme (e.g. ModernTheme, where --ts-ui-button-bg is a solid colour)\n // paints via the colour and a gradient theme via the image. Setting\n // only background-image left the footer transparent under a flat-colour\n // theme, since a colour is invalid as a background-image.\n const footerBg = \"var(--ts-ui-button-bg, linear-gradient(rgb(241, 241, 241), rgb(200, 200, 200)))\";\n this.setBackgroundColor(footerBg);\n this.setBackgroundImage(footerBg);\n\n let row = new Row();\n this.addRow(row);\n }\n\n /**\n * Returns the footer cell components in column order.\n *\n * @returns An array of cell components from the footer's inner row.\n */\n getColumns() {\n return this.getComponents()[0].getComponents();\n }\n\n /**\n * Appends a row to the footer.\n *\n * @param row - The row to append.\n */\n addRow(row: Row) : this {\n this.addComponent(row);\n\n return this;\n }\n\n /**\n * Adds a row as a child component of the footer.\n *\n * @param row - The row component to add.\n *\n * @returns This component, for method chaining.\n */\n addComponent(row: Row): this {\n super.addComponent(row);\n\n return this;\n }\n\n /**\n * Sets the footer width and propagates it to the inner row.\n *\n * @param width - The width in pixels.\n *\n * @returns This component, for method chaining.\n */\n setWidth(width: number): this {\n super.setWidth(width);\n\n this.getComponents()[0].setWidth(width);\n\n return this;\n }\n\n /**\n * Sets the footer height and propagates it to the inner row.\n *\n * @param height - The height in pixels.\n *\n * @returns This component, for method chaining.\n */\n setHeight(height: number): this {\n super.setHeight(height);\n\n this.getComponents()[0].setHeight(height);\n\n return this;\n }\n}\n\nconst FooterRowCallable = callable(FooterRow);\ntype FooterRowCallable = FooterRow;\nexport {\n FooterRow as _FooterRow,\n FooterRowCallable as FooterRow\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Field } from \"~/data/Field.js\";\nimport { ColumnConfig, ColumnSpec } from \"~/component/table/ColumnConfig.js\";\n\n/**\n * Resolved presentation descriptor for a single table column.\n *\n * Created internally by `Column.resolve` — not constructed directly by\n * application code. Wraps a {@link Field} and carries optional width constraints\n * and an initial visibility flag derived from a {@link ColumnSpec}.\n *\n * Re-exported as `TableColumn` from the package barrel.\n *\n * @category Components\n */\nexport class Column {\n\n private _field : Field;\n private _minWidth : number | undefined;\n private _maxWidth : number | undefined;\n private _hidden : boolean;\n private _unhideable : boolean;\n private _readOnly : boolean;\n private _headerGlyph : string | null;\n private _group : string | null;\n private _groupColor : string | null;\n private _required : boolean;\n\n /**\n * Constructs a Column from a field and an optional presentation config.\n *\n * @param field - The model field this column represents.\n * @param config - Optional config; all constraint properties default to absent / false.\n */\n constructor(field: Field, config?: ColumnConfig) {\n this._field = field;\n this._minWidth = config?.minWidth;\n this._maxWidth = config?.maxWidth;\n this._hidden = config?.hidden ?? false;\n this._unhideable = config?.unhideable ?? false;\n this._readOnly = config?.readOnly ?? false;\n this._headerGlyph = config?.headerGlyph ?? null;\n this._group = config?.group ?? null;\n this._groupColor = config?.groupColor ?? null;\n this._required = config?.required ?? false;\n }\n\n /**\n * Returns the model field this column represents.\n *\n * @returns The underlying {@link Field}.\n */\n getField(): Field {\n return this._field;\n }\n\n /**\n * Returns the minimum column width in pixels declared in the spec.\n *\n * @returns The minimum width, or `undefined` if unconstrained.\n */\n getMinWidth(): number | undefined {\n return this._minWidth;\n }\n\n /**\n * Returns the maximum column width in pixels declared in the spec.\n *\n * @returns The maximum width, or `undefined` if unconstrained.\n */\n getMaxWidth(): number | undefined {\n return this._maxWidth;\n }\n\n /**\n * Returns whether this column starts hidden according to the spec.\n *\n * @returns `true` if the spec declared `hidden: true` for this column.\n */\n isInitiallyHidden(): boolean {\n return this._hidden;\n }\n\n /**\n * Returns whether this column is marked unhideable in the spec.\n * Unhideable columns always render and cannot be toggled off via\n * the column context menu or {@link Table.setColumnVisible}.\n *\n * @returns `true` when the spec declared `unhideable: true`.\n */\n isUnhideable(): boolean {\n return this._unhideable;\n }\n\n /**\n * Returns whether this column is marked read-only in the spec.\n * Every cell in a read-only column refuses inline editing and\n * renders with a grey tint.\n *\n * @returns `true` when the spec declared `readOnly: true`.\n */\n isReadOnly(): boolean {\n return this._readOnly;\n }\n\n /**\n * Returns the registry glyph name shown to the left of this column's header text.\n *\n * @returns The glyph registry name, or `null` if no header glyph is set.\n */\n getHeaderGlyph(): string | null {\n return this._headerGlyph;\n }\n\n /**\n * Sets the registry glyph name shown to the left of this column's header text.\n *\n * Pass `null` (or call {@link Column.clearHeaderGlyph}) to remove the glyph.\n *\n * @param name - A registered glyph name, or `null` to remove.\n * @returns This column, for method chaining.\n */\n setHeaderGlyph(name: string | null): this {\n this._headerGlyph = name;\n\n return this;\n }\n\n /**\n * Clears the header glyph for this column. Equivalent to `setHeaderGlyph(null)`.\n *\n * @returns This column, for method chaining.\n */\n clearHeaderGlyph(): this {\n return this.setHeaderGlyph(null);\n }\n\n /**\n * Returns the parent-header group name this column belongs to, fixed at\n * construction. Columns sharing a group name and appearing contiguously\n * render under a single spanning {@link ParentHeaderCell}.\n *\n * @returns The group name, or `null` when the column is ungrouped.\n */\n getGroup(): string | null {\n return this._group;\n }\n\n /**\n * Returns the optional background color advertised by this column's\n * `ColumnConfig.groupColor`. The first non-null value in a contiguous\n * group run wins; later mismatched values are ignored.\n *\n * @returns The CSS color string, or `null` when none was declared.\n */\n getGroupColor(): string | null {\n return this._groupColor;\n }\n\n /**\n * Returns whether this column is marked required in the spec. Drives\n * the header asterisk; the per-record `requiredPredicate` composes\n * with this flag for the empty-cell tint but does not affect this\n * accessor.\n *\n * @returns `true` when the spec declared `required: true`.\n */\n isRequired(): boolean {\n return this._required;\n }\n\n /**\n * Resolves a {@link ColumnSpec} against a set of model fields into an ordered\n * array of {@link Column} instances.\n *\n * When no spec is provided every field becomes a default Column, preserving\n * existing table behaviour exactly.\n *\n * When a spec is provided:\n * - Fields listed in `spec.columns` receive their declared constraints.\n * - Fields not listed are appended as default Columns when `appendUnlisted`\n * is `true` (the default), or omitted entirely when it is `false`.\n *\n * The returned array is sorted by {@link Field.getOrder} so that the column\n * sequence matches the order used by the header and body renderers.\n *\n * @param fields - All fields returned by the model.\n * @param spec - Optional column spec; omit to auto-generate from all fields.\n *\n * @returns Resolved Column instances in display order.\n */\n static resolve(fields: Field[], spec?: ColumnSpec): Column[] {\n const sorted = fields.slice().sort((a, b) => a.getOrder() - b.getOrder());\n\n if (!spec) {\n return sorted.map(f => new Column(f));\n }\n\n const configMap = new Map(spec.columns.map(c => [c.field, c]));\n const listedNames = new Set(spec.columns.map(c => c.field));\n\n if (spec.appendUnlisted === false) {\n return sorted\n .filter(f => listedNames.has(f.getName()))\n .map(f => new Column(f, configMap.get(f.getName())));\n }\n\n // All fields in field order; apply config where one exists.\n // This keeps Column order in sync with the header, which also sorts by field.getOrder().\n return sorted.map(f => new Column(f, configMap.get(f.getName())));\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { DOM } from \"~/core/DOM.js\";\nimport { Column } from \"~/component/table/Column.js\";\nimport { ColumnConfig } from \"~/component/table/ColumnConfig.js\";\nimport { ModelRecord } from \"~/data/ModelRecord.js\";\nimport { FieldType } from \"~/data/Field.js\";\n\n/**\n * Options controlling a {@link Table} export operation.\n *\n * @category Components\n */\nexport interface ExportOptions {\n /** When true, hidden columns are included in the export. Default: false. */\n includeHidden?: boolean;\n\n /** Override the downloaded filename. */\n filename?: string;\n}\n\n/**\n * Stateless helper that converts a column list and a record list into a CSV\n * or JSON download.\n *\n * @remarks\n * Used internally by {@link Table.exportCSV} and {@link Table.exportJSON}.\n * Date, time, and datetime values are formatted with the same `toLocale*`\n * options the cell renderers use, so exports match what the user sees.\n */\nexport class TableExporter {\n\n /**\n * Converts columns + records to an RFC 4180 CSV string and triggers a download.\n *\n * @param columns - The columns to include as fields in the CSV.\n * @param records - The records to serialize as rows.\n * @param columnConfigs - Per-field column config map (carries `showSeconds` for time/datetime).\n * @param options - Optional export options.\n */\n static exportCSV(\n columns : Column[],\n records : ModelRecord[],\n columnConfigs: Map<string, ColumnConfig>,\n options ?: ExportOptions\n ): void {\n const header = columns\n .map(c => TableExporter.escapeCSVField(c.getField().getName()))\n .join(',');\n\n const rows = records.map(record =>\n columns\n .map(c => TableExporter.escapeCSVField(TableExporter.formatValue(c, record.get(c.getField().getName()), columnConfigs)))\n .join(',')\n );\n\n const csv = [header, ...rows].join('\\r\\n');\n\n TableExporter.download(csv, options?.filename ?? 'table-export.csv', 'text/csv;charset=utf-8;');\n }\n\n /**\n * Converts columns + records to a pretty-printed JSON array of objects and triggers a download.\n *\n * @param columns - The columns whose field names become the keys of each emitted object.\n * @param records - The records to serialize.\n * @param columnConfigs - Per-field column config map (carries `showSeconds` for time/datetime).\n * @param options - Optional export options.\n */\n static exportJSON(\n columns : Column[],\n records : ModelRecord[],\n columnConfigs: Map<string, ColumnConfig>,\n options ?: ExportOptions\n ): void {\n const data = records.map(record =>\n Object.fromEntries(\n columns.map(c => [\n c.getField().getName(),\n TableExporter.formatValue(c, record.get(c.getField().getName()), columnConfigs)\n ])\n )\n );\n const json = JSON.stringify(data, null, 2);\n\n TableExporter.download(json, options?.filename ?? 'table-export.json', 'application/json');\n }\n\n /**\n * Formats a raw cell value the same way the matching cell renderer does.\n *\n * @param column - The column whose field type drives the formatting choice.\n * @param value - The raw value read from the record.\n * @param columnConfigs - Per-field config map; consulted for `showSeconds` on time/datetime.\n * @returns The formatted value, or the original value when no formatting applies.\n */\n private static formatValue(column: Column, value: any, columnConfigs: Map<string, ColumnConfig>): any {\n if (value == null || !(value instanceof Date)) {\n return value;\n }\n\n const type : FieldType = column.getField().getType();\n const showSeconds : boolean = columnConfigs.get(column.getField().getName())?.showSeconds ?? false;\n\n switch (type) {\n case 'date':\n return value.toLocaleDateString();\n case 'time':\n return value.toLocaleTimeString(undefined, showSeconds\n ? { hour: '2-digit', minute: '2-digit', second: '2-digit' }\n : { hour: '2-digit', minute: '2-digit' });\n case 'datetime':\n return value.toLocaleString(undefined, showSeconds\n ? { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' }\n : { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' });\n default:\n return value;\n }\n }\n\n /**\n * Escapes a single CSV field value per RFC 4180.\n *\n * @param value - The raw value to escape; null and undefined become empty strings.\n * @returns The escaped CSV field, wrapped in quotes when it contains `,`, `\"`, or `\\n`.\n */\n private static escapeCSVField(value: any): string {\n const str = String(value ?? '');\n\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n return '\"' + str.replace(/\"/g, '\"\"') + '\"';\n }\n\n return str;\n }\n\n /**\n * Triggers a browser download of the given content via a temporary anchor element.\n *\n * @param content - The textual content to download.\n * @param filename - The suggested filename for the download.\n * @param mimeType - The MIME type used to construct the Blob.\n */\n private static download(content: string, filename: string, mimeType: string): void {\n const blob = new Blob([content], { type: mimeType });\n const url = URL.createObjectURL(blob);\n const a = DOM.sink.createElement('a');\n\n DOM.sink.apply(a, { setAttr: { \"href\": url, \"download\": filename } });\n\n DOM.sink.appendChild(DOM.source.getBody(), a);\n DOM.sink.click(a);\n DOM.sink.removeChild(DOM.source.getBody(), a);\n DOM.sink.release(a);\n\n URL.revokeObjectURL(url);\n }\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\nimport { Table as TableLayout } from \"~/layout/Table.js\";\nimport { TableHeader } from \"~/component/table/Header.js\";\nimport { Body } from \"~/component/table/Body.js\";\nimport type { CellClickEvent } from \"~/component/table/Body.js\";\nimport { FooterRow } from \"~/component/table/Footer.js\";\nimport { AbstractStore } from \"~/data/AbstractStore.js\";\nimport { ModelRecord } from \"~/data/ModelRecord.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { Menu } from \"~/overlay/Menu.js\";\nimport { MenuItemConfig } from \"~/component/container/MenuItem.js\";\nimport { Column } from \"~/component/table/Column.js\";\nimport type { ColumnConfig } from \"~/component/table/ColumnConfig.js\";\nimport { ColumnSpec } from \"~/component/table/ColumnConfig.js\";\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { Util } from \"~/core/Util.js\";\nimport { TableExporter, ExportOptions } from \"~/component/table/TableExporter.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/** Events emitted by {@link Table}. */\nexport type TableEvent = \"selection\" | \"cellclick\";\n\n/**\n * Construction-time options for {@link Table}.\n *\n * Table's primary configuration is the positional `(store, spec)` constructor\n * pair; this options bag carries only inherited Component styling. Defined\n * here so `this._options` is typed at the leaf class and Phase 6 has a place\n * to grow if Table starts accepting an options object.\n *\n * @category Components\n */\nexport interface TableOptions extends ComponentOptions {\n}\n\n/**\n * A data-bound table component rendered as an HTML `<table>` element.\n *\n * Composes a {@link TableHeader}, a virtual-scrolling {@link Body}, and an optional\n * {@link FooterRow}. Exposes CRUD and sync operations that delegate to the underlying\n * {@link AbstractStore}.\n *\n * An optional {@link ColumnSpec} controls which columns appear, their display\n * constraints (`minWidth`, `maxWidth`), and initial visibility. When no spec is\n * supplied the table auto-generates one column per model field — identical to the\n * pre-spec behaviour.\n *\n * @example\n * ```typescript\n * import { Model, MemoryStore } from '@jimka/typescript-ui/data';\n * import { Table } from '@jimka/typescript-ui/component/table';\n *\n * const PersonModel = new Model([\n * { name: 'id', type: 'number' },\n * { name: 'name', type: 'string' },\n * { name: 'age', type: 'number' },\n * ]);\n *\n * const store = new MemoryStore(PersonModel, [\n * { id: 1, name: 'Alice', age: 30 },\n * { id: 2, name: 'Bob', age: 25 },\n * ]);\n * await store.load();\n *\n * const table = new Table(store);\n * panel.addComponent(table);\n * ```\n *\n * @category Components\n */\nclass Table extends Component<TableOptions> {\n\n private _store : AbstractStore;\n private _spec : ColumnSpec | undefined;\n private _resolvedColumns : Column[] = [];\n private _hiddenColumns : Set<string> = new Set();\n private _columnContextMenu: Menu = new Menu();\n private _headerVisible : boolean;\n private _header : TableHeader;\n private _body : Body;\n private _bodyVisible : boolean;\n private _footer : FooterRow;\n private _footerVisible : boolean;\n private _columnWidths : number[] = [];\n private _resizeOriginClientX: number = 0;\n private _resizeOriginW0 : number = 0;\n private _resizeOriginW1 : number = 0;\n private _savedColumnWidths: Map<string, number> = new Map();\n private _columnConfigs : Map<string, ColumnConfig> = new Map();\n private _exportMenuEnabled: boolean = false;\n private _listeners : ListenerBag<TableEvent> = new ListenerBag<TableEvent>();\n\n /**\n * Constructs a Table bound to the given store, optionally constrained by a\n * column presentation spec.\n *\n * @param store - The data store to bind to this table.\n * @param spec - Optional column spec; omit to auto-generate all columns.\n * @param bodyFactory - Optional. Closure that constructs the body\n * component. Used by [`TreeTable`](/api/component/table/classes/TreeTable)\n * to install a [`TreeBody`](/api/component/table/classes/TreeBody)\n * in place of the default flat-list body. A closure (not a\n * subclass-overridable method) avoids the class-field super-trap\n * that would otherwise read tree-spec fields before they are\n * initialised.\n */\n constructor(store: AbstractStore, spec?: ColumnSpec, bodyFactory?: (store: AbstractStore) => Body) {\n super({ tag: \"table\" });\n\n this.setLayoutManager(new TableLayout());\n this.getAria().setRole(\"grid\");\n this.setBorder({ border: \"1px solid var(--ts-ui-border-color, black)\" });\n this.setInsets(new Insets(0, 0, 0, 0));\n this.setOverflow(\"hidden\");\n this.setMinSize({ width: 100, height: 100 });\n\n this._store = store;\n this._spec = spec;\n this._headerVisible = true;\n this._bodyVisible = true;\n this._footerVisible = false;\n\n this._resolvedColumns = Column.resolve(store.model.getFields(), spec);\n this.initHiddenFromSpec();\n\n this._header = new TableHeader(store.model, store);\n this._header.on(\"columnresizestart\", (i, clientX) => this.onColumnResizeStart(i, clientX));\n this._header.on(\"columnresize\", (i, clientX) => this.onColumnResize(i, clientX));\n this._header.on(\"columncontextmenu\", (_, x, y) => this.showColumnMenu(x, y));\n // The header is the permanent target of horizontal scroll mirroring (see the\n // scroll listener below), so promote it to its own compositor layer for the\n // Table's lifetime. One hint per Table is well under the per-page threshold.\n this._header.setWillChange(\"transform\");\n this.addComponent(this._header);\n this._header.setColumns(this._resolvedColumns);\n\n this._body = bodyFactory ? bodyFactory(store) : new Body(store);\n this._body.setHeader(this._header);\n this.addComponent(this._body);\n this._body.setColumns(this._resolvedColumns);\n\n this._footer = new FooterRow();\n this.addComponent(this._footer);\n\n const effectiveHidden = this.getEffectiveHiddenSet();\n\n if (effectiveHidden.size > 0) {\n this._header.setHiddenColumns(effectiveHidden);\n this._body.setHiddenColumns(effectiveHidden);\n }\n\n if (spec) {\n this._columnConfigs = this.buildColumnConfigs(spec);\n this._body.setColumnConfigs(this._columnConfigs);\n }\n\n this._body.setRowReadOnly(spec?.rowReadOnly ?? null);\n\n this.getAria().setColCount(this.getColumns().length);\n\n // Sync header horizontal scroll with body. The body uses\n // transform-based virtual scroll (via `VirtualScroller`), so the\n // native DOM `scroll` event never fires; hook the body's\n // `on(\"horizontalscroll\")` listener instead. Translate the header's\n // two inner rows (parent row + column row) rather than the header\n // element itself — the header band stays pinned to the viewport\n // width so its background covers the vertical-scrollbar reserve\n // band on the right edge, and only the cells inside scroll with\n // the body.\n this._body.on(\"horizontalscroll\", scrollLeft => {\n this._header.getParentRow().setTranslate(-scrollLeft, 0);\n this._header.getComponents()[1].setTranslate(-scrollLeft, 0);\n });\n\n // Surface the body's selection changes on the Table's own event so\n // consumers can react (e.g. enabling a delete action) without reaching\n // into the private body.\n this._body.on(\"selection\", records => this.emit(\"selection\", records));\n\n // Forward the body's column-aware cell-click on the Table's own event,\n // mirroring the selection forward above so consumers can react to a\n // click on a specific cell (record + column) without inferring the\n // column from a selection change.\n this._body.on(\"cellclick\", e => this.emit(\"cellclick\", e));\n }\n\n /**\n * Registers a listener for one of this table's events.\n *\n * @param event - `\"selection\"` fires whenever the selected-record set\n * changes, receiving the current selection; `\"cellclick\"` fires when a\n * data cell is clicked, carrying the clicked record, the column's field\n * name and visible index, the cell value, the record's row index in the\n * visible-records view, and the raw mouse event.\n * @param listener - The callback to invoke when the event fires.\n *\n * @returns This table, for method chaining.\n */\n on(event: \"selection\", listener: (records: ModelRecord[]) => void): this;\n on(event: \"cellclick\", listener: (e: CellClickEvent) => void): this;\n on(event: TableEvent, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered listener. The exact callback reference\n * must match the one passed to {@link on}.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The callback to remove.\n *\n * @returns This table, for method chaining.\n */\n off(event: TableEvent, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fires every listener registered for `event`, in registration order.\n *\n * @param event - The event to emit.\n * @param payload - The current selection (`\"selection\"`) or the\n * cell-click detail (`\"cellclick\"`) forwarded to each listener.\n */\n protected emit(event: \"selection\", records: ModelRecord[]): void;\n protected emit(event: \"cellclick\", detail: CellClickEvent): void;\n protected emit(event: TableEvent, payload: ModelRecord[] | CellClickEvent): void {\n this._listeners.fire(event, payload);\n }\n\n /**\n * Returns the resolved, visible columns in display order.\n *\n * Excludes columns that are currently hidden (via runtime toggle or the spec's\n * `hidden` flag) and columns excluded by a strict spec (`appendUnlisted: false`).\n *\n * @returns Visible {@link Column} instances in field display order.\n */\n getColumns(): Column[] {\n const effective = this.getEffectiveHiddenSet();\n\n return this._resolvedColumns.filter(c => !effective.has(c.getField().getName()));\n }\n\n /**\n * Returns the data store this table is bound to.\n *\n * @returns The current {@link AbstractStore}.\n */\n getStore(): AbstractStore {\n return this._store;\n }\n\n /**\n * Returns the model associated with this table's store.\n *\n * @returns The model from the store.\n */\n getModel() {\n return this._store.model;\n }\n\n /**\n * Replaces the data store, re-resolves columns from the new model, and updates\n * the body and header to reflect the change.\n *\n * @param store - The new store to bind to the table.\n */\n setStore(store: AbstractStore): this {\n this._store = store;\n this._columnWidths = [];\n this._savedColumnWidths = new Map();\n this._resolvedColumns = Column.resolve(store.model.getFields(), this._spec);\n\n this._body.setStore(store);\n this._header.setModel(store.model);\n this._header.setColumns(this._resolvedColumns);\n this._body.setColumns(this._resolvedColumns);\n this._header.setHiddenColumns(this.getEffectiveHiddenSet());\n this.getAria().setColCount(this.getColumns().length);\n\n return this;\n }\n\n /**\n * Returns the per-column width array maintained by the layout manager.\n *\n * @returns The current column widths in pixels.\n */\n getColumnWidths(): number[] {\n return this._columnWidths;\n }\n\n /**\n * Stores the per-column width array (called by the layout manager on each layout pass).\n *\n * @param widths - The new column widths in pixels.\n * @remarks Also mirrors each width into `savedColumnWidths` keyed by field name so\n * that show/hide toggles can restore per-column widths without a full re-initialisation.\n */\n setColumnWidths(widths: number[]): this {\n this._columnWidths = widths;\n\n const visibleColumns = this.getColumns();\n\n widths.forEach((w, i) => {\n const col = visibleColumns[i];\n\n if (col) {\n this._savedColumnWidths.set(col.getField().getName(), w);\n }\n });\n\n return this;\n }\n\n /**\n * Shows or hides the column identified by the given field name.\n *\n * Only columns present in the resolved column list can be toggled; columns\n * excluded by a strict spec (`appendUnlisted: false`) are unaffected.\n *\n * Calls to hide a column marked `unhideable: true` in the spec are a no-op —\n * the column always remains visible. Calls to show any column run normally.\n *\n * Manually resized widths are preserved across visibility toggles. When showing\n * a column introduces extra width that would overflow the container, existing\n * columns are proportionally trimmed via `trimToTarget` to make room before\n * the layout manager runs.\n *\n * @param fieldName - The model field name of the column to toggle.\n * @param visible - `true` to show the column, `false` to hide it.\n */\n setColumnVisible(fieldName: string, visible: boolean): this {\n if (!visible) {\n const col = this._resolvedColumns.find(c => c.getField().getName() === fieldName);\n\n if (col && col.isUnhideable()) {\n return this;\n }\n }\n\n if (visible) {\n this._hiddenColumns.delete(fieldName);\n } else {\n this._hiddenColumns.add(fieldName);\n }\n\n const newVisibleColumns = this.getColumns();\n const rawWidths = newVisibleColumns.map(col =>\n this._savedColumnWidths.get(col.getField().getName()) ?? this.defaultColumnWidth(col)\n );\n const savedTotal = this._columnWidths.reduce((s, w) => s + w, 0);\n const rawTotal = rawWidths.reduce((s, w) => s + w, 0);\n\n this._columnWidths = (rawTotal > savedTotal + 0.5 && savedTotal > 0)\n ? this.trimToTarget(newVisibleColumns, rawWidths, savedTotal, fieldName)\n : rawWidths;\n\n const effectiveHidden = this.getEffectiveHiddenSet();\n\n this._header.setHiddenColumns(effectiveHidden);\n this._body.setHiddenColumns(effectiveHidden);\n this.getAria().setColCount(this.getColumns().length);\n this.doLayout();\n\n return this;\n }\n\n /**\n * Returns the table header component.\n *\n * @returns The {@link TableHeader} section of this table.\n */\n getHeader() {\n return this._header;\n }\n\n /**\n * Returns whether the header section is visible.\n *\n * @returns `true` if the header is visible.\n */\n isHeaderVisible() {\n return this._headerVisible;\n }\n\n /**\n * Returns the table body component.\n *\n * @returns The virtual-scrolling {@link Body} section of this table.\n */\n getBody() {\n return this._body;\n }\n\n /**\n * Returns whether the body section is visible.\n *\n * @returns `true` if the body is visible.\n */\n isBodyVisible() {\n return this._bodyVisible;\n }\n\n /**\n * Returns the table footer component.\n *\n * @returns The {@link FooterRow} section of this table.\n */\n getFooter() {\n return this._footer;\n }\n\n /**\n * Returns whether the footer section is visible.\n *\n * @returns `true` if the footer is visible.\n */\n isFooterVisible() {\n return this._footerVisible;\n }\n\n /**\n * Adds a new record to the store, scrolls to it, and selects it.\n *\n * @param defaults - Optional initial field values for the new record.\n * \n * @returns The newly created {@link ModelRecord}.\n */\n addRow(defaults: Record<string, any> = {}): ModelRecord {\n const [record] = this._store.add(defaults);\n this._body.scrollToRecord(record);\n this._body.selectRecord(record);\n\n return record;\n }\n\n /**\n * Removes the currently selected record from the store.\n */\n removeSelectedRow(): this {\n const record = this._body.getSelectedRecord();\n\n if (!record) {\n return this;\n }\n\n this._body.selectRecord(null);\n this._store.remove(record);\n\n return this;\n }\n\n /**\n * Selects the given record — or clears the selection when `null` — and\n * scrolls it into view, driving the selection programmatically without the\n * caller reaching into the private body. This is the read/write counterpart\n * to {@link getSelectedRecord}: use it to sync the table to an external\n * selection (a linked tree/diagram, a master/detail view). Fires the\n * `\"selection\"` event like a user click, so a caller that drives it in\n * response to its own `\"selection\"` handler must guard against re-entrancy.\n *\n * @param record - The record to select and reveal, or `null` to clear.\n *\n * @returns This table, for method chaining.\n */\n selectRecord(record: ModelRecord | null): this {\n if (record) {\n this._body.scrollToRecord(record);\n }\n\n this._body.selectRecord(record);\n\n return this;\n }\n\n /**\n * Persists all pending store changes to the server via the configured proxy.\n *\n * @returns A Promise that resolves when the sync operation completes.\n */\n async sync(): Promise<void> {\n return this._store.sync();\n }\n\n /**\n * Discards all unsynced store changes — reverts dirty records, drops new\n * ones, and restores pending removals.\n */\n reject(): void {\n this._store.reject();\n }\n\n /**\n * Returns the currently selected record, or null if none is selected.\n *\n * @returns The selected {@link ModelRecord}, or null.\n */\n getSelectedRecord(): ModelRecord | null {\n return this._body.getSelectedRecord();\n }\n\n /**\n * Returns all currently selected records.\n *\n * @returns An array of selected {@link ModelRecord} instances.\n */\n getSelectedRecords(): ModelRecord[] {\n return this._body.getSelectedRecords();\n }\n\n /**\n * Adds a header, body, or footer section component, updating the stored reference.\n *\n * @param row - The section component to add.\n * @param constraints - Optional layout constraints for the section.\n *\n * @returns This component, for method chaining.\n */\n addComponent(row: TableHeader | Body | FooterRow, constraints?: LayoutConstraints): this {\n if (row instanceof TableHeader) {\n this._header = row;\n } else if (row instanceof Body) {\n this._body = row;\n } else if (row instanceof FooterRow) {\n this._footer = row;\n }\n\n super.addComponent(row, constraints);\n\n return this;\n }\n\n /**\n * Returns a type-based default width for a column that has no saved width yet.\n *\n * @param col - The column to compute a default width for.\n * @returns A positive pixel width appropriate for the column's field type.\n */\n private defaultColumnWidth(col: Column): number {\n const f = col.getField();\n\n // Measure under the same font properties HeaderCell actually renders with\n // (bold + table-header font size). 4px cell padding + 5px resize-handle gutter\n // + 12px breathing room for the optional sort indicator (▲/▼).\n const textWidth = Util.measureTextWidth(f.getName(), {\n fontSize : \"var(--ts-ui-table-header-font-size, var(--ts-ui-font-size))\",\n fontWeight: \"bold\",\n });\n const headerMin = textWidth + 21;\n\n switch (f.getType()) {\n case 'boolean': return Math.max(60, headerMin);\n case 'number': return Math.max(90, headerMin);\n case 'date': return Math.max(110, headerMin);\n default: return Math.max(100, headerMin);\n }\n }\n\n /**\n * Proportionally reduces existing saved columns to bring the total down to `targetTotal`,\n * leaving `exemptField` and any unsaved columns at their assigned widths.\n *\n * Flex (string/auto) columns are reduced first so that `rescaleWidths` can restore them\n * automatically when the shown column is later hidden again. Fixed-type (boolean/number/date)\n * columns are only reduced if flex space is fully exhausted.\n *\n * @param columns - The full new visible column list.\n * @param widths - Raw width per column (saved or default).\n * @param targetTotal - The pixel budget the result must not exceed.\n * @param exemptField - Field name of the column being shown; never trimmed.\n * \n * @returns Adjusted width array whose sum is at most `targetTotal`.\n */\n private trimToTarget(columns: Column[], widths: number[], targetTotal: number, exemptField: string): number[] {\n const result = [...widths];\n\n const isFixedType = (col: Column): boolean => {\n const t = col.getField().getType();\n\n return t === 'boolean' || t === 'number' || t === 'date';\n };\n\n const trim = (pool: { i: number; room: number }[], deficit: number): number => {\n const totalRoom = pool.reduce((s, c) => s + c.room, 0);\n\n if (totalRoom <= 0) {\n return deficit;\n }\n\n const toRemove = Math.min(deficit, totalRoom);\n\n for (const { i, room } of pool) {\n result[i] = Math.max(result[i] - (room / totalRoom) * toRemove, columns[i].getMinWidth() ?? 30);\n }\n\n return deficit - toRemove;\n };\n\n const pool = (fixedType: boolean) => columns\n .map((col, i) => ({\n i,\n room: (isFixedType(col) === fixedType\n && col.getField().getName() !== exemptField\n && this._savedColumnWidths.has(col.getField().getName()))\n ? result[i] - (col.getMinWidth() ?? 30)\n : 0\n }))\n .filter(c => c.room > 0.5);\n\n let deficit = result.reduce((s, w) => s + w, 0) - targetTotal;\n\n deficit = trim(pool(false), deficit);\n\n if (deficit > 0.5) {\n trim(pool(true), deficit);\n }\n\n return result;\n }\n\n /**\n * Populates `hiddenColumns` from columns declared `hidden: true` in the spec,\n * so they start hidden but remain user-toggleable via the context menu.\n */\n private buildColumnConfigs(spec: ColumnSpec): Map<string, ColumnConfig> {\n const map = new Map<string, ColumnConfig>();\n\n for (const col of spec.columns) {\n map.set(col.field, col);\n }\n\n return map;\n }\n\n private initHiddenFromSpec(): void {\n for (const col of this._resolvedColumns) {\n if (col.isInitiallyHidden() && !col.isUnhideable()) {\n this._hiddenColumns.add(col.getField().getName());\n }\n }\n }\n\n /**\n * Returns the union of runtime-hidden columns and columns excluded by the spec\n * (`appendUnlisted: false`). This is the set passed to the header and body renderers.\n *\n * @returns The effective set of field names that must not be rendered.\n */\n private getEffectiveHiddenSet(): Set<string> {\n const resolvedNames = new Set(this._resolvedColumns.map(c => c.getField().getName()));\n const result = new Set(this._hiddenColumns);\n\n for (const f of this._store.model.getFields()) {\n if (!resolvedNames.has(f.getName())) {\n result.add(f.getName());\n }\n }\n\n return result;\n }\n\n /**\n * Displays the column visibility context menu, listing only columns present in\n * the resolved column list (excluded columns do not appear).\n *\n * @param x - Viewport x coordinate for the menu.\n * @param y - Viewport y coordinate for the menu.\n */\n private showColumnMenu(x: number, y: number): void {\n const columns = this._resolvedColumns\n .slice()\n .sort((a, b) => a.getField().getOrder() - b.getField().getOrder());\n\n const items: MenuItemConfig[] = [];\n let lastGroup: string | null | undefined = undefined;\n\n // Walk in display order, emitting a disabled section header\n // each time the group changes. Ungrouped columns sit flush\n // (no section header above them); transitioning back to an\n // ungrouped column from a grouped run is treated as \"end of\n // group\" — the next ungrouped item just appears with no\n // preceding header. Field-list order matches the parent\n // header band above, so the menu reads as a vertical\n // restatement of what the user sees on screen.\n //\n // The indent uses non-breaking spaces (` `) because the\n // menu item renders text with the default `white-space: nowrap`\n // setting, which still collapses runs of ASCII spaces — regular\n // `' '` would render as a single space.\n const GROUP_INDENT = \"    \";\n\n columns.forEach(col => {\n const fieldName = col.getField().getName();\n const visible = !this._hiddenColumns.has(fieldName);\n const group = col.getGroup();\n\n if (group !== null && group !== lastGroup) {\n items.push({ text: group, enabled: false });\n }\n\n const indent = group !== null ? GROUP_INDENT : \"\";\n\n // An unhideable column is currently visible (since it\n // cannot be hidden) — the menu entry stays in the list so\n // the user sees the column's identity but is disabled to\n // signal it can't be toggled.\n const hideable = !col.isUnhideable();\n\n items.push({\n text: indent + (visible ? '✓ ' : ' ') + fieldName,\n action: () => this.setColumnVisible(fieldName, !visible),\n enabled: hideable,\n });\n\n lastGroup = group;\n });\n\n items.push(\n { separator: true },\n { text: 'Reset columns', action: () => this.resetColumns() }\n );\n\n if (this._exportMenuEnabled) {\n items.push(\n { separator: true },\n { text: 'Export as CSV', action: () => this.exportCSV() },\n { text: 'Export as JSON', action: () => this.exportJSON() }\n );\n }\n\n this._columnContextMenu.show(x, y, items);\n }\n\n /**\n * Enables or disables the \"Export as CSV\" / \"Export as JSON\" entries in\n * the column context menu.\n *\n * @param enabled - When true the export items are appended to the menu.\n */\n setExportMenuEnabled(enabled: boolean): this {\n this._exportMenuEnabled = enabled;\n\n return this;\n }\n\n /**\n * Triggers a CSV download of the current store view.\n *\n * @param options - Optional export options (e.g. include hidden columns, custom filename).\n */\n exportCSV(options?: ExportOptions): void {\n const columns = this.getExportColumns(options?.includeHidden ?? false);\n const records = this._store.getRecords();\n\n TableExporter.exportCSV(columns, records, this._columnConfigs, options);\n }\n\n /**\n * Triggers a JSON download of the current store view.\n *\n * @param options - Optional export options (e.g. include hidden columns, custom filename).\n */\n exportJSON(options?: ExportOptions): void {\n const columns = this.getExportColumns(options?.includeHidden ?? false);\n const records = this._store.getRecords();\n\n TableExporter.exportJSON(columns, records, this._columnConfigs, options);\n }\n\n /**\n * Returns the columns to include in an export.\n *\n * @param includeHidden - When true, all resolved columns are returned; otherwise only visible columns.\n * @returns The columns to export, in display order.\n */\n private getExportColumns(includeHidden: boolean): Column[] {\n return includeHidden ? this._resolvedColumns.slice() : this.getColumns();\n }\n\n /**\n * Captures the resize origin when a column-resize drag begins: the absolute\n * pointer `clientX` and the current widths of the adjacent column pair.\n * {@link onColumnResize} derives the new widths from these origins so\n * over-travel past a column's `minWidth`/`maxWidth` is absorbed without the\n * edge decoupling from the cursor on reversal.\n *\n * @param colIndex - Zero-based index of the column whose right edge is being dragged.\n * @param clientX - The absolute pointer `clientX` at the moment the drag began.\n */\n private onColumnResizeStart(colIndex: number, clientX: number): void {\n const n = this._columnWidths.length;\n\n if (n === 0 || colIndex >= n - 1) {\n return;\n }\n\n this._resizeOriginClientX = clientX;\n this._resizeOriginW0 = this._columnWidths[colIndex];\n this._resizeOriginW1 = this._columnWidths[colIndex + 1];\n }\n\n /**\n * Handles a column resize drag, clamping the adjacent column pair to their\n * per-column `minWidth` and `maxWidth` constraints.\n *\n * @param colIndex - Zero-based index of the column whose right edge is being dragged.\n * @param clientX - The absolute pointer `clientX` for this move.\n *\n * @remarks The new widths are derived from the origin captured in\n * {@link onColumnResizeStart} as `originWidth ± (clientX − originClientX)`,\n * then clamped/redistributed against the pair's `minWidth`/`maxWidth`.\n * Clamping the absolute result (rather than accumulating per-move deltas)\n * makes dragging past a column's minimum idempotent: the edge stays at the\n * boundary until the pointer returns past the coordinate where the clamp\n * was hit.\n */\n private onColumnResize(colIndex: number, clientX: number): void {\n const n = this._columnWidths.length;\n\n if (n === 0 || colIndex >= n - 1) {\n return;\n }\n\n const columns = this.getColumns();\n const min0 = columns[colIndex]?.getMinWidth() ?? 30;\n const max0 = columns[colIndex]?.getMaxWidth() ?? Infinity;\n const min1 = columns[colIndex + 1]?.getMinWidth() ?? 30;\n const max1 = columns[colIndex + 1]?.getMaxWidth() ?? Infinity;\n\n const delta = clientX - this._resizeOriginClientX;\n\n let w0 = this._resizeOriginW0 + delta;\n let w1 = this._resizeOriginW1 - delta;\n\n if (w0 < min0) {\n w1 += w0 - min0;\n w0 = min0;\n }\n\n if (w1 < min1) {\n w0 += w1 - min1;\n w1 = min1;\n }\n\n if (w0 < min0 || w1 < min1 || w0 > max0 || w1 > max1) {\n return;\n }\n\n this._columnWidths[colIndex] = w0;\n this._columnWidths[colIndex + 1] = w1;\n\n this.doLayout();\n }\n\n /**\n * Resets column visibility and widths to their initial spec-defined state.\n *\n * Columns marked `hidden: true` in the spec are re-hidden; all others are shown.\n * All manually resized widths are discarded and recomputed from defaults.\n */\n private resetColumns(): void {\n this._hiddenColumns = new Set();\n this.initHiddenFromSpec();\n this._savedColumnWidths = new Map();\n this._columnWidths = this.getColumns().map(col => this.defaultColumnWidth(col));\n\n const effectiveHidden = this.getEffectiveHiddenSet();\n\n this._header.setHiddenColumns(effectiveHidden);\n this._body.setHiddenColumns(effectiveHidden);\n this.doLayout();\n }\n}\n\nconst TableCallable = callable(Table);\ntype TableCallable = Table;\nexport {\n Table as _Table,\n TableCallable as Table\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { AbstractStore } from \"~/data/AbstractStore.js\";\nimport { Border } from \"~/layout/Border.js\";\nimport { HBox } from \"~/layout/HBox.js\";\nimport { Container } from \"~/core/Container.js\";\nimport { Placement } from \"~/primitive/Placement.js\";\nimport { Button } from \"~/component/button/Button.js\";\nimport { PaginationBar } from \"~/component/display/PaginationBar.js\";\nimport { ProgressSpinner } from \"~/component/display/ProgressSpinner.js\";\nimport { Table } from \"~/component/table/Table.js\";\nimport { ExportOptions } from \"~/component/table/TableExporter.js\";\nimport { Tooltip } from \"~/overlay/Tooltip.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { plus } from \"~/glyphs/solid/plus.js\";\nimport { minus } from \"~/glyphs/solid/minus.js\";\nimport { arrows_rotate } from \"~/glyphs/solid/arrows_rotate.js\";\nimport { ban } from \"~/glyphs/solid/ban.js\";\n\nGlyph.register(plus, minus, arrows_rotate, ban);\n\n/**\n * A composite panel that combines a {@link Table} with an add/remove/sync toolbar.\n *\n * The toolbar is docked to the north region; the table fills the center region.\n *\n * @category Components\n */\nclass TablePanel extends Container {\n\n private _table: Table;\n private _toolbar: Component;\n private _syncBtn: Button;\n private _rejectBtn: Button;\n private _spinner: ProgressSpinner | null = null;\n private _paginationBar: PaginationBar | undefined = undefined;\n\n constructor(store: AbstractStore) {\n super();\n\n this.setLayoutManager(new Border());\n\n this._toolbar = new Component();\n this._toolbar.setLayoutManager(new HBox());\n\n const addBtn = new Button({ glyph: \"plus\" });\n addBtn.setPreferredSize({ width: 28, height: 28 });\n addBtn.on(\"action\", () => { this._table.addRow(); });\n Tooltip.attach(addBtn, \"Add row\");\n this._toolbar.addComponent(addBtn);\n\n const removeBtn = new Button({ glyph: \"minus\" });\n removeBtn.setPreferredSize({ width: 28, height: 28 });\n removeBtn.on(\"action\", () => { this._table.removeSelectedRow(); });\n Tooltip.attach(removeBtn, \"Remove selected row\");\n this._toolbar.addComponent(removeBtn);\n\n this._syncBtn = new Button({ glyph: \"arrows-rotate\" });\n this._syncBtn.setPreferredSize({ width: 28, height: 28 });\n this._syncBtn.on(\"action\", () => { void this._table.sync(); });\n Tooltip.attach(this._syncBtn, \"Sync pending changes\");\n this._toolbar.addComponent(this._syncBtn);\n\n this._rejectBtn = new Button({ glyph: \"ban\" });\n this._rejectBtn.setPreferredSize({ width: 28, height: 28 });\n this._rejectBtn.on(\"action\", () => this._table.reject());\n Tooltip.attach(this._rejectBtn, \"Reject pending changes\");\n this._toolbar.addComponent(this._rejectBtn);\n\n this._table = new Table(store);\n\n super.addComponent(this._toolbar, { placement: Placement.NORTH });\n super.addComponent(this._table, { placement: Placement.CENTER });\n\n store.on('loadingchange', (payload: { loading: boolean }) => {\n if (!this._spinner) {\n this._spinner = new ProgressSpinner(24);\n }\n\n if (payload.loading) {\n this._spinner.showOverlay(this._table);\n } else {\n this._spinner.hideOverlay();\n }\n });\n\n const refreshSyncButtons = (): void => this.refreshSyncButtons();\n store.on('add', refreshSyncButtons);\n store.on('remove', refreshSyncButtons);\n store.on('datachange', refreshSyncButtons);\n store.on('sync', refreshSyncButtons);\n store.on('load', refreshSyncButtons);\n\n this.refreshSyncButtons();\n }\n\n /**\n * Updates the Sync and Reject button-enabled state to reflect whether the\n * store has any unsynced changes. Both buttons disable when there is\n * nothing to sync or reject.\n */\n private refreshSyncButtons(): void {\n const hasChanges = this._table.getStore().hasPendingChanges();\n\n this._syncBtn.setEnabled(hasChanges);\n this._rejectBtn.setEnabled(hasChanges);\n }\n\n /**\n * Returns the Table component managed by this panel.\n *\n * @returns The managed {@link Table} instance.\n */\n getTable(): Table {\n return this._table;\n }\n\n /**\n * Returns the toolbar component containing the add/remove/sync buttons.\n *\n * @returns The toolbar {@link Component}.\n */\n getToolbar(): Component {\n return this._toolbar;\n }\n\n /**\n * Enables or disables the \"Export as CSV\" / \"Export as JSON\" entries in\n * the underlying table's column context menu.\n *\n * @param enabled - When true the export items are appended to the menu.\n */\n setExportMenuEnabled(enabled: boolean): this {\n this._table.setExportMenuEnabled(enabled);\n\n return this;\n }\n\n /**\n * Triggers a CSV download of the current store view.\n *\n * @param options - Optional export options (e.g. include hidden columns, custom filename).\n */\n exportCSV(options?: ExportOptions): void {\n this._table.exportCSV(options);\n }\n\n /**\n * Triggers a JSON download of the current store view.\n *\n * @param options - Optional export options (e.g. include hidden columns, custom filename).\n */\n exportJSON(options?: ExportOptions): void {\n this._table.exportJSON(options);\n }\n\n /**\n * Docks a {@link PaginationBar} to the south region of the panel.\n *\n * @param bar - The pagination bar to attach. Replaces any previously attached bar.\n *\n * @remarks\n * Any previously attached bar is removed and disposed before the new one\n * is installed, so its store listeners do not leak.\n */\n setPaginationBar(bar: PaginationBar): this {\n if (this._paginationBar) {\n this.removeComponent(this._paginationBar);\n this._paginationBar.dispose();\n }\n\n this._paginationBar = bar;\n super.addComponent(bar, { placement: Placement.SOUTH });\n\n return this;\n }\n\n /**\n * Returns the currently attached pagination bar, or undefined if none is set.\n *\n * @returns The pagination bar previously installed via {@link setPaginationBar}.\n */\n getPaginationBar(): PaginationBar | undefined {\n return this._paginationBar;\n }\n}\n\nconst TablePanelCallable = callable(TablePanel);\ntype TablePanelCallable = TablePanel;\nexport {\n TablePanel as _TablePanel,\n TablePanelCallable as TablePanel\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { _Body } from \"~/component/table/Body.js\";\nimport { AbstractStore } from \"~/data/AbstractStore.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { DragEventDetail, DragManager } from \"~/overlay/DragManager.js\";\nimport { ModelRecord } from \"~/data/ModelRecord.js\";\nimport { Row } from \"~/component/table/Row.js\";\nimport { TreeCellRenderer } from \"~/component/table/cell/renderer/TreeCell.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Drop-target outcome callback injected by\n * [`TreeTable`](/api/component/table/classes/TreeTable). Returns whether\n * the drop reparented the dragged record.\n *\n * @internal Framework wiring between `TreeTable` and `TreeBody`. Not\n * part of the public API.\n */\ntype TreeBodyReparentHandler = (\n dragged: ModelRecord,\n newParent: ModelRecord | null,\n) => boolean;\n\n/**\n * Drop-target validity callback. Returns `true` when the dragged record\n * may legally land under `newParent`. Used by the per-row drop targets\n * to drive the green/red feedback tint before commit.\n *\n * @internal Framework wiring between `TreeTable` and `TreeBody`. Not\n * part of the public API.\n */\ntype TreeBodyReparentValidator = (\n dragged: ModelRecord,\n newParent: ModelRecord | null,\n) => boolean;\n\n/**\n * A flattened entry in {@link TreeBody}'s visible-row list.\n *\n * @category Components\n */\nexport interface FlatRecord {\n /** The record at this row of the flattened, expansion-aware view. */\n record: ModelRecord;\n /** Zero-based nesting depth (0 = root). */\n depth: number;\n /** Whether this record has at least one child record in the store. */\n hasChildren: boolean;\n /** Whether this record is currently expanded (false for leaves). */\n expanded: boolean;\n /** Number of sibling records under the same parent — drives `aria-setsize`. */\n siblingCount: number;\n /** 1-based position among siblings — drives `aria-posinset`. */\n posInSet: number;\n}\n\n/**\n * Construction-time configuration for a {@link TreeBody}. Mirrors the\n * subset of {@link TreeTableSpec} the body needs at run time.\n *\n * @category Components\n */\nexport interface TreeBodySpec {\n /** Field carrying each record's id. */\n idField: string;\n /** Field carrying each record's parent id, or `null` for roots. */\n parentField: string;\n /** Field name of the column whose cell carries the indent + toggle. */\n treeColumn: string;\n /** Pixels of indentation per depth level. */\n indentPx: number;\n}\n\n/**\n * A virtual-scrolling body for a {@link TreeTable}. Subclass of\n * [`Body`](/api/component/table/classes/Body) — reuses the entire\n * row-pool / virtual-scroll / selection / focus / editing / context\n * menu / keyboard machinery, overriding only the visible-record source\n * and a handful of hooks the base class exposes for subclasses:\n *\n * - `Body.onStoreChange` — rebuilds the parent/child index and\n * re-flattens the visible subtree before the inherited refresh runs.\n * - `Body.getVisibleRecords` — returns the depth-aware,\n * expansion-aware flat-record list rather than the store view.\n * - `Body.createRow` — constructs each pool row with the tree\n * column's field name so the matching cell carries a\n * [`TreeCellRenderer`](/api/component/table/classes/TreeCellRenderer).\n * - `Body.computeRowAria` — additionally writes `aria-level`,\n * `aria-expanded`, `aria-setsize`, `aria-posinset`.\n * - `Body.afterRowBound` — pushes depth + expansion state to the\n * row's tree cell renderer on every render pass.\n * - `Body.onSubtreeClick` — dispatches toggle clicks to\n * {@link setExpanded} before falling through to the inherited\n * row-click selection handling.\n * - `Body.onKeyDown` — adds `ArrowRight` / `ArrowLeft` expand /\n * collapse / parent-jump on top of the inherited row + column nav.\n *\n * Expand state is keyed by the record's id (the `idField` value), not\n * by `ModelRecord` reference — a store sync that replaces records\n * preserves expansion.\n *\n * Sort interaction: when the store is sorted, the parent/child index\n * walks the sorted records, so children sit right under each parent at\n * the moment of flatten. A parent dropped by a filter takes its subtree\n * out of the visible list.\n *\n * @category Components\n */\nclass TreeBody extends _Body {\n\n declare private _idField: string;\n declare private _parentField: string;\n declare private _treeColumn: string;\n declare private _indentPx: number;\n\n private _byId: Map<any, ModelRecord> = new Map();\n private _childIds: Map<any, ModelRecord[]> = new Map();\n private _expanded: Set<any> = new Set();\n private _flatRows: FlatRecord[] = [];\n\n /**\n * Per-row drag-source + drop-target teardown bag. Keyed by the\n * pool `Row` (whose identity is stable across rebinds — the\n * underlying record changes, but the row component does not), so a\n * rebind tears down the previous closures around the previous\n * record before installing fresh ones.\n */\n private _rowDnDTeardowns: Map<Row, () => void> = new Map();\n\n /** Empty-area drop-target teardown — registered in {@link init}. */\n private _emptyAreaDropTeardown: (() => void) | null = null;\n\n /** Optional reparent commit injected by `TreeTable`. */\n private _reparentHandler: TreeBodyReparentHandler | null = null;\n\n /** Optional reparent validity check injected by `TreeTable`. */\n private _reparentValidator: TreeBodyReparentValidator | null = null;\n\n /**\n * Constructs a TreeBody bound to the given store, configured by the\n * tree spec.\n *\n * @param store - The data store. Records must carry `idField` and\n * `parentField` values; roots have a null/undefined parent id.\n * @param spec - The tree configuration (id field, parent field,\n * tree column, indent pixels).\n */\n constructor(store: AbstractStore, spec: TreeBodySpec) {\n super(store);\n\n this._idField = spec.idField;\n this._parentField = spec.parentField;\n this._treeColumn = spec.treeColumn;\n this._indentPx = spec.indentPx;\n\n // The base constructor's `bindStore` has already wired\n // `onStoreChange`, but with `this._byId` / `_childIds` /\n // `_flatRows` still empty (the class-field initializers above\n // run *after* `super()`). Build the initial index now so the\n // first `renderWindow` reads a populated `_flatRows`.\n this.rebuildIndex();\n this.flatten();\n }\n\n /**\n * Returns the field name carrying each record's id.\n *\n * @returns The id field name.\n */\n getIdField(): string {\n return this._idField;\n }\n\n /**\n * Returns the field name carrying each record's parent id.\n *\n * @returns The parent-id field name.\n */\n getParentField(): string {\n return this._parentField;\n }\n\n /**\n * Returns the name of the column whose cell carries the indent +\n * expand/collapse toggle.\n *\n * @returns The tree-column field name.\n */\n getTreeColumn(): string {\n return this._treeColumn;\n }\n\n /**\n * Returns the flattened visible-row list snapshot. The list is\n * rebuilt on every store event and on every expand/collapse.\n *\n * @returns The current flat-record array (do not mutate).\n */\n getFlatRecords(): FlatRecord[] {\n return this._flatRows;\n }\n\n /**\n * Resolves a record by its id-field value. Reads the `_byId` map\n * maintained by `rebuildIndex`, so the lookup is O(1) and stays in\n * sync with the latest store snapshot.\n *\n * @param id - The id-field value to look up.\n *\n * @returns The matching record, or `undefined` when the id is not\n * present in the current store view.\n */\n getRecordById(id: any): ModelRecord | undefined {\n return this._byId.get(id);\n }\n\n /**\n * Returns the records that name `id` as their parent-id. Reads the\n * `_childIds` map maintained by `rebuildIndex`; returns an empty\n * array (not the underlying list) when the id has no children, so\n * callers can iterate without an existence check.\n *\n * @param id - The parent-id field value to look up.\n *\n * @returns A snapshot of the child records (do not mutate).\n */\n getChildrenOf(id: any): ModelRecord[] {\n return this._childIds.get(id) ?? [];\n }\n\n /**\n * Returns whether the given record currently has at least one\n * child — i.e. behaves as a directory in the rendered tree.\n *\n * @param record - The record to test.\n *\n * @returns `true` when the record has children; `false` for leaves.\n */\n isDirectoryRecord(record: ModelRecord): boolean {\n return this.getChildrenOf(record.get(this._idField)).length > 0;\n }\n\n /**\n * Walks the parent chain from `descendant` upward and returns\n * `true` when `ancestor` is found at any depth. Used by\n * [`TreeTable.reparentRow`](/api/component/table/classes/TreeTable#reparentrow)\n * to reject drops that would create a cycle.\n *\n * @param ancestor - The candidate ancestor record.\n * @param descendant - The starting record. The walk follows\n * `descendant.get(parentField)` recursively.\n *\n * @returns `true` when `ancestor === descendant` or `ancestor` is\n * any transitive parent of `descendant`.\n */\n isAncestorOf(ancestor: ModelRecord, descendant: ModelRecord): boolean {\n if (ancestor === descendant) {\n return true;\n }\n\n const ancestorId = ancestor.get(this._idField);\n\n let current: ModelRecord | undefined = descendant;\n const seen = new Set<any>();\n\n while (current) {\n const parentId = current.get(this._parentField);\n\n if (parentId == null) {\n return false;\n }\n\n if (parentId === ancestorId) {\n return true;\n }\n\n // Defensive cycle guard — a corrupt parent chain must not\n // infinite-loop the validity check.\n if (seen.has(parentId)) {\n return false;\n }\n\n seen.add(parentId);\n current = this._byId.get(parentId);\n }\n\n return false;\n }\n\n /**\n * Returns whether the given record is currently expanded. A leaf\n * record (no children) reports `false` even if its id is in the\n * expansion set.\n *\n * @param record - The record to query.\n *\n * @returns `true` when the record is a branch and currently\n * expanded, `false` otherwise.\n */\n isExpanded(record: ModelRecord): boolean {\n const id = record.get(this._idField);\n const children = this._childIds.get(id);\n\n return !!children && children.length > 0 && this._expanded.has(id);\n }\n\n /**\n * Expands or collapses the given record. No-op for leaves and for\n * records not present in the current store view.\n *\n * @param record - The record to expand or collapse.\n * @param expanded - `true` to expand, `false` to collapse.\n *\n * @returns This body, for method chaining.\n */\n setExpanded(record: ModelRecord, expanded: boolean): this {\n const id = record.get(this._idField);\n const children = this._childIds.get(id);\n\n if (!children || children.length === 0) {\n return this;\n }\n\n if (expanded) {\n if (this._expanded.has(id)) {\n return this;\n }\n\n this._expanded.add(id);\n } else {\n if (!this._expanded.has(id)) {\n return this;\n }\n\n this._expanded.delete(id);\n }\n\n this.flatten();\n this.invalidateRowBindings();\n this.renderWindow();\n\n return this;\n }\n\n /**\n * Expands every record at depth `<= depth`. Roots are at depth 0,\n * so `expandToDepth(0)` expands only the roots, `expandToDepth(1)`\n * expands roots and their children, and so on.\n *\n * @param depth - Maximum depth to expand.\n *\n * @returns This body, for method chaining.\n */\n expandToDepth(depth: number): this {\n this._expanded.clear();\n\n const recurse = (records: ModelRecord[], d: number): void => {\n if (d > depth) {\n return;\n }\n\n for (const record of records) {\n const id = record.get(this._idField);\n const children = this._childIds.get(id);\n\n if (children && children.length > 0) {\n this._expanded.add(id);\n recurse(children, d + 1);\n }\n }\n };\n\n const roots = this._childIds.get(null) ?? [];\n recurse(roots, 0);\n\n this.flatten();\n this.invalidateRowBindings();\n this.renderWindow();\n\n return this;\n }\n\n /**\n * Collapses every record. Equivalent to clearing the expansion set\n * and refreshing the view.\n *\n * @returns This body, for method chaining.\n */\n collapseAll(): this {\n if (this._expanded.size === 0) {\n return this;\n }\n\n this._expanded.clear();\n\n this.flatten();\n this.invalidateRowBindings();\n this.renderWindow();\n\n return this;\n }\n\n /**\n * Expands every record that has children, regardless of depth.\n *\n * @returns This body, for method chaining.\n */\n expandAll(): this {\n const next = new Set<any>();\n\n for (const [parentId, children] of this._childIds) {\n if (parentId !== null && children.length > 0) {\n next.add(parentId);\n }\n }\n\n // Roots that have children also expand — they're keyed in\n // `_childIds` under `null` (their parent) but the expand set\n // is keyed by *the record's own id*.\n const roots = this._childIds.get(null) ?? [];\n for (const root of roots) {\n const id = root.get(this._idField);\n const children = this._childIds.get(id);\n\n if (children && children.length > 0) {\n next.add(id);\n }\n }\n\n this._expanded = next;\n\n this.flatten();\n this.invalidateRowBindings();\n this.renderWindow();\n\n return this;\n }\n\n /**\n * Returns the toggle element of the row's `TreeCellRenderer`, or\n * `null` when the row has no tree cell or the tree cell renders a\n * leaf (no toggle).\n *\n * @param row - The pool row to inspect.\n *\n * @returns The toggle's DOM element handle, or `null`.\n */\n private getToggleElement(row: Row): Handle | null {\n const treeCell = row.getTreeCell();\n\n if (!treeCell) {\n return null;\n }\n\n const renderer = treeCell.getRenderer();\n\n if (!(renderer instanceof TreeCellRenderer)) {\n return null;\n }\n\n const toggle = renderer.getToggle();\n\n return toggle ? (toggle.getElement() ?? null) : null;\n }\n\n /**\n * Initialises the tree body, then registers the empty-area drop\n * target so a row released over the body's whitespace reparents to\n * root. The element must exist before [`DragManager.makeDropTarget`](/api/overlay/variables/DragManager#makedroptarget)\n * can resolve its id — defer past the base class `init`.\n *\n * @param element - Optional element to initialise with.\n *\n * @returns This body, for method chaining.\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n this.ensureEmptyAreaDropTarget();\n\n return this;\n }\n\n /**\n * Rebuilds the parent/child index from the current store records,\n * then re-flattens the visible subtree. Invoked from\n * `Body.onStoreChange` before the inherited refresh runs.\n */\n protected onStoreChange(): void {\n this.rebuildIndex();\n this.flatten();\n\n super.onStoreChange();\n }\n\n /**\n * Returns the depth-aware visible-record list — the records the\n * row pool binds to.\n *\n * @returns The current flat-record list as plain `ModelRecord` array.\n */\n protected getVisibleRecords(): ModelRecord[] {\n return this._flatRows.map(f => f.record);\n }\n\n /**\n * Constructs a row carrying a {@link TreeCellRenderer} on the tree\n * column.\n *\n * @returns The new pool row.\n */\n protected createRow(): Row {\n return new Row(\n this.getStore().model,\n undefined,\n this.getHiddenColumns(),\n this.getColumnConfigs(),\n (record) => this.getStore().notifyRecordChanged(record),\n this._treeColumn,\n );\n }\n\n /**\n * Returns the tree column's field name so an in-place column-toggle\n * sync preserves the {@link TreeCellRenderer} on the tree-column\n * cell. Overrides the base, which returns `undefined`.\n *\n * @returns The tree column's field name.\n */\n protected getTreeFieldName(): string {\n return this._treeColumn;\n }\n\n /**\n * Writes the standard `aria-rowindex` from the base class, then\n * adds the tree-specific ARIA properties read from the flat record\n * at the same index.\n *\n * @param row - The pool row whose ARIA attributes to update.\n * @param dataIndex - The row's index into `_flatRows`.\n */\n protected computeRowAria(row: Row, dataIndex: number): void {\n super.computeRowAria(row, dataIndex);\n\n const flat = this._flatRows[dataIndex];\n\n if (!flat) {\n return;\n }\n\n row.getAria().setLevel(flat.depth + 1);\n row.getAria().setExpanded(flat.hasChildren ? flat.expanded : null);\n row.getAria().setSetSize(flat.siblingCount);\n row.getAria().setPosInSet(flat.posInSet);\n }\n\n /**\n * Pushes depth + expansion state to the row's\n * [`TreeCellRenderer`](/api/component/table/classes/TreeCellRenderer)\n * so the toggle glyph and indent match the flat record at this\n * data index, then re-wires the per-row drag source / drop target\n * for the bound record.\n *\n * @param row - The pool row being processed.\n * @param dataIndex - The row's index into `_flatRows`.\n * @param wasRebound - True when the row's record changed on this\n * pass; the DnD closures captured the previous record and must be\n * replaced.\n */\n protected afterRowBound(row: Row, dataIndex: number, wasRebound: boolean): void {\n const treeCell = row.getTreeCell();\n const flat = this._flatRows[dataIndex];\n\n if (treeCell && flat) {\n const renderer = treeCell.getRenderer() as TreeCellRenderer<any>;\n\n renderer.setTreeState(flat.depth, flat.hasChildren, flat.expanded);\n }\n\n if (this._reparentHandler === null) {\n return;\n }\n\n if (!flat) {\n this.teardownRowDnD(row);\n\n return;\n }\n\n if (wasRebound || !this._rowDnDTeardowns.has(row)) {\n this.installRowDnD(row, flat.record);\n }\n }\n\n /**\n * Installs the manager-side [`DragManager.makeDragSource`](/api/overlay/variables/DragManager#makedragsource) +\n * [`DragManager.makeDropTarget`](/api/overlay/variables/DragManager#makedroptarget) pair for a pool row, replacing\n * any previously installed pair.\n */\n private installRowDnD(row: Row, record: ModelRecord): void {\n this.teardownRowDnD(row);\n\n const tearDownSource = DragManager.makeDragSource(row, {\n dragData: { recordId: record.get(this._idField) },\n });\n\n const dropTargetForRow = (detail: DragEventDetail): ModelRecord | null => {\n const dragged = this.resolveDragged(detail);\n\n if (!dragged) {\n return null;\n }\n\n return this.resolveDropParentForRow(record);\n };\n\n const tearDownTarget = DragManager.makeDropTarget(row, {\n accepts: (detail: DragEventDetail) => {\n const dragged = this.resolveDragged(detail);\n\n if (!dragged) {\n return false;\n }\n\n const newParent = dropTargetForRow(detail);\n\n return this._reparentValidator?.(dragged, newParent) ?? false;\n },\n onDrop: (detail: DragEventDetail): boolean | void => {\n const dragged = this.resolveDragged(detail);\n\n if (!dragged) {\n return false;\n }\n\n const newParent = dropTargetForRow(detail);\n\n return this._reparentHandler?.(dragged, newParent) ?? false;\n },\n });\n\n this._rowDnDTeardowns.set(row, () => {\n tearDownSource();\n tearDownTarget();\n });\n }\n\n /**\n * Resolves the dragged record from the manager's detail payload\n * (which carries the record-id assigned in {@link installRowDnD}).\n * Returns `undefined` when the record is no longer in the store.\n */\n private resolveDragged(detail: DragEventDetail): ModelRecord | undefined {\n const id = detail.dragData[\"recordId\"];\n\n return this._byId.get(id);\n }\n\n /**\n * Maps a hovered row's bound record to the directory it implies.\n * Directories accept drops directly; leaves forward to their\n * parent (so \"drop on a sibling file\" lands next to it under the\n * same directory).\n */\n private resolveDropParentForRow(record: ModelRecord): ModelRecord | null {\n if (this.isDirectoryRecord(record)) {\n return record;\n }\n\n const parentId = record.get(this._parentField);\n\n if (parentId == null) {\n return null;\n }\n\n return this._byId.get(parentId) ?? null;\n }\n\n /**\n * Tears down the drag-source + drop-target pair for a pool row.\n */\n private teardownRowDnD(row: Row): void {\n const teardown = this._rowDnDTeardowns.get(row);\n\n if (teardown) {\n teardown();\n this._rowDnDTeardowns.delete(row);\n }\n }\n\n /**\n * Wires the reparent callbacks supplied by `TreeTable`. Installed\n * once at construction so per-row DnD has everything it needs by\n * the time `afterRowBound` first runs.\n *\n * @param validator - Pre-drop validity check.\n * @param handler - Commits the reparent and returns whether it\n * succeeded.\n *\n * @returns This body, for method chaining.\n *\n * @internal Framework wiring; application code does not call this.\n */\n setReparentHandlers(validator: TreeBodyReparentValidator, handler: TreeBodyReparentHandler): this {\n this._reparentValidator = validator;\n this._reparentHandler = handler;\n\n if (this.getElement()) {\n this.ensureEmptyAreaDropTarget();\n }\n\n return this;\n }\n\n /**\n * Registers the body element itself as a drop target so a row\n * released over the empty area below the last row reparents to\n * root. Idempotent — safe to call after either\n * {@link setReparentHandlers} or `init`.\n */\n private ensureEmptyAreaDropTarget(): void {\n if (this._emptyAreaDropTeardown !== null) {\n return;\n }\n\n if (this._reparentHandler === null) {\n return;\n }\n\n this._emptyAreaDropTeardown = DragManager.makeDropTarget(this, {\n accepts: (detail: DragEventDetail) => {\n const dragged = this.resolveDragged(detail);\n\n if (!dragged) {\n return false;\n }\n\n return this._reparentValidator?.(dragged, null) ?? false;\n },\n onDrop: (detail: DragEventDetail): boolean | void => {\n const dragged = this.resolveDragged(detail);\n\n if (!dragged) {\n return false;\n }\n\n return this._reparentHandler?.(dragged, null) ?? false;\n },\n });\n }\n\n /**\n * Intercepts subtree clicks on a tree-toggle and routes them to\n * {@link setExpanded}. Other clicks fall through to the inherited\n * row-selection handler.\n *\n * @param e - The bubbled click event.\n */\n protected onSubtreeClick(e: MouseEvent): void {\n const target = e.target === null ? null : DOM.source.intern(e.target);\n\n for (const row of this.getRowPool()) {\n const toggleEl = this.getToggleElement(row);\n\n if (toggleEl && target !== null && (target === toggleEl || DOM.source.contains(toggleEl, target))) {\n const record = row.getData();\n\n if (record) {\n this.setExpanded(record, !this.isExpanded(record));\n }\n\n return;\n }\n }\n\n super.onSubtreeClick(e);\n }\n\n /**\n * Intercepts `ArrowRight` / `ArrowLeft` for tree navigation,\n * delegating every other key to the base `Body` handler. When\n * there is a selected anchor record:\n *\n * - `ArrowRight` expands a collapsed branch, or moves focus to the\n * first child of an already-expanded branch (no-op on leaves).\n * - `ArrowLeft` collapses an expanded branch, or moves focus to the\n * parent record (no-op at depth 0 with no children).\n *\n * Replaces the base body's `ArrowRight` / `ArrowLeft` column-focus\n * navigation while the user is interacting with the tree. Other\n * keys (`ArrowUp`/`Down`, `Home`/`End`, `Enter`/`Space`,\n * `PageUp`/`PageDown`) keep their inherited behaviour.\n *\n * @param e - The keyboard event fired on the body element.\n */\n protected onKeyDown(e: KeyboardEvent): void {\n if (e.key !== \"ArrowRight\" && e.key !== \"ArrowLeft\") {\n super.onKeyDown(e);\n\n return;\n }\n\n const anchor = this.getSelectedRecord();\n\n if (!anchor) {\n super.onKeyDown(e);\n\n return;\n }\n\n const idx = this._flatRows.findIndex(f => f.record === anchor);\n\n // Anchor exists but isn't in the flat view (e.g. a filter\n // dropped its subtree mid-key-press). Treat as no-op for tree\n // navigation rather than silently swallowing the keystroke.\n if (idx < 0) {\n e.preventDefault();\n\n return;\n }\n\n e.preventDefault();\n\n const flat = this._flatRows[idx];\n\n if (e.key === \"ArrowRight\") {\n this.handleArrowRight(flat, idx);\n } else {\n this.handleArrowLeft(flat, idx);\n }\n }\n\n /**\n * ArrowRight: expand a collapsed branch, or move focus into the\n * first child of an expanded branch. No-op on leaves.\n */\n private handleArrowRight(flat: FlatRecord, idx: number): void {\n if (!flat.hasChildren) {\n return;\n }\n\n if (!flat.expanded) {\n this.setExpanded(flat.record, true);\n\n return;\n }\n\n // Already expanded — move focus to the first child.\n const next = this._flatRows[idx + 1];\n\n if (next && next.depth === flat.depth + 1) {\n this.moveFocusTo(next.record);\n }\n }\n\n /**\n * ArrowLeft: collapse an expanded branch, or move focus to the\n * parent record. No-op when already at depth 0 and either leaf or\n * collapsed.\n */\n private handleArrowLeft(flat: FlatRecord, idx: number): void {\n if (flat.hasChildren && flat.expanded) {\n this.setExpanded(flat.record, false);\n\n return;\n }\n\n if (flat.depth === 0) {\n return;\n }\n\n // Walk backwards to find the parent record (the nearest\n // ancestor row with depth = current - 1).\n for (let i = idx - 1; i >= 0; i--) {\n if (this._flatRows[i].depth === flat.depth - 1) {\n this.moveFocusTo(this._flatRows[i].record);\n\n return;\n }\n }\n }\n\n /**\n * Programmatic equivalent of an arrow-key navigation: selects the\n * record, scrolls it into view, re-renders, and refreshes the\n * focus + active-descendant indicators. Mirrors the trailing\n * `selectRecord + scrollRecordIntoView + renderWindow +\n * _updateActiveDescendant + _updateFocusStyle` sequence the base\n * `Body.onKeyDown` runs after row-nav keys.\n */\n private moveFocusTo(record: ModelRecord): void {\n this.selectRecord(record);\n this.scrollRecordIntoView(record);\n this.renderWindow();\n this._updateActiveDescendant();\n this._updateFocusStyle();\n }\n\n /**\n * Rebuilds `_byId` and `_childIds` from the current store records.\n * Records with a parent id that doesn't resolve to any known record\n * are treated as roots (orphan fallback).\n */\n private rebuildIndex(): void {\n this._byId.clear();\n this._childIds.clear();\n\n const records = this.getStore().getRecords();\n\n for (const record of records) {\n const id = record.get(this._idField);\n\n this._byId.set(id, record);\n }\n\n // Roots sit under the `null` key in `_childIds`. Records whose\n // parent id doesn't resolve fall through to `null` too —\n // orphans render as additional roots rather than silently\n // disappearing.\n for (const record of records) {\n const parentId = record.get(this._parentField);\n const key = parentId != null && this._byId.has(parentId) ? parentId : null;\n\n const list = this._childIds.get(key);\n\n if (list) {\n list.push(record);\n } else {\n this._childIds.set(key, [record]);\n }\n }\n }\n\n /**\n * Re-flattens the visible subtree from the current `_byId` /\n * `_childIds` index. The walk skips children whose parent id is\n * absent from the `_expanded` set.\n */\n private flatten(): void {\n const rows: FlatRecord[] = [];\n\n const recurse = (records: ModelRecord[], depth: number): void => {\n const siblingCount = records.length;\n\n for (let i = 0; i < records.length; i++) {\n const record = records[i];\n const id = record.get(this._idField);\n const children = this._childIds.get(id);\n const hasKids = !!children && children.length > 0;\n const expanded = hasKids && this._expanded.has(id);\n\n rows.push({\n record,\n depth,\n hasChildren: hasKids,\n expanded,\n siblingCount,\n posInSet: i + 1,\n });\n\n if (expanded) {\n recurse(children!, depth + 1);\n }\n }\n };\n\n const roots = this._childIds.get(null) ?? [];\n\n recurse(roots, 0);\n\n this._flatRows = rows;\n }\n}\n\nconst TreeBodyCallable = callable(TreeBody);\ntype TreeBodyCallable = TreeBody;\nexport {\n TreeBody as _TreeBody,\n TreeBodyCallable as TreeBody\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Event } from \"~/core/Event.js\";\nimport { Table } from \"~/component/table/Table.js\";\nimport { TreeBody } from \"~/component/table/TreeBody.js\";\nimport { TreeTableSpec } from \"~/component/table/TreeTableSpec.js\";\nimport { AbstractStore } from \"~/data/AbstractStore.js\";\nimport { ModelRecord } from \"~/data/ModelRecord.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/** Default pixels of indentation per depth level when the spec omits `indentPx`. */\nconst DEFAULT_INDENT_PX = 16;\n\n/**\n * Payload delivered to listeners registered through\n * {@link TreeTable.addRowReparentListener}. Fired after a successful\n * {@link TreeTable.reparentRow} call.\n *\n * @category Components\n */\nexport interface RowReparentDetail {\n /** The record whose `parentField` value changed. */\n record: ModelRecord;\n /** The new parent record, or `null` when reparented to root. */\n newParent: ModelRecord | null;\n /** The previous parent record, or `null` when the row was previously at root. */\n oldParent: ModelRecord | null;\n}\n\n/**\n * A data-bound table whose rows form a parent/child hierarchy.\n *\n * Composes the standard [`Header`](/api/component/table/classes/Header),\n * a tree-aware [`TreeBody`](/api/component/table/classes/TreeBody), and an\n * optional [`FooterRow`](/api/component/table/classes/FooterRow). Reuses\n * every public surface from {@link Table} (CRUD, sync, column visibility\n * and resize, sort cycling, context menu, exporter) — the only\n * difference is the body: virtual-scroll over a depth-aware, expansion-\n * aware flat record list rather than over the store's view directly.\n *\n * Hierarchy is encoded on the store's existing model: each record names\n * the id of its parent (or `null` for roots) via the field named in\n * {@link TreeTableSpec.parentField}. No new store subtype or schema\n * extension is required.\n *\n * One designated column — {@link TreeTableSpec.treeColumn} — renders\n * with an indent + expand/collapse toggle to the left of the value.\n * The toggle is a [`Glyph`](/api/component/display/classes/Glyph)\n * (`caret-down` / `caret-right`); the rest of the cell content is\n * drawn by whichever typed renderer the column would normally use,\n * unchanged.\n *\n * The ARIA role is `treegrid` (not `grid`). Pool rows additionally\n * carry `aria-level`, `aria-expanded`, `aria-setsize`, and\n * `aria-posinset`.\n *\n * @example\n * ```typescript\n * const model = new Model([\n * { name: 'id', type: 'number' },\n * { name: 'parentId', type: 'number' },\n * { name: 'name', type: 'string' },\n * { name: 'size', type: 'number' },\n * ]);\n *\n * const store = new MemoryStore(model, [\n * { id: 1, parentId: null, name: 'Documents', size: 0 },\n * { id: 2, parentId: 1, name: 'report.pdf', size: 84 },\n * { id: 3, parentId: null, name: 'Music', size: 0 },\n * ]);\n *\n * const tree = new TreeTable(store, {\n * idField: 'id',\n * parentField: 'parentId',\n * treeColumn: 'name',\n * columns: [\n * { field: 'name', minWidth: 200 },\n * { field: 'size' },\n * ],\n * });\n *\n * tree.expandToDepth(0);\n * ```\n *\n * @category Components\n */\nclass TreeTable extends Table {\n\n private _treeSpec: TreeTableSpec;\n private _treeBody: TreeBody;\n\n /**\n * Maps each consumer-supplied `rowreparent` listener to the\n * CustomEvent-unwrapping wrapper actually registered with\n * `Event.addListener`. The wrapper is the removable reference;\n * the original listener is the lookup key. Inline arrows can't be\n * removed by reference, so the wrapper is built once at add-time\n * and stored.\n */\n private _rowReparentWrappers = new Map<\n (detail: RowReparentDetail) => void,\n (e: CustomEvent<RowReparentDetail>) => void\n >();\n\n /**\n * Constructs a TreeTable bound to the given store.\n *\n * @param store - The data store carrying the records. Each record\n * must have a value at `spec.idField`; root records have a\n * `null` (or absent) value at `spec.parentField`.\n * @param spec - Tree presentation + hierarchy configuration.\n */\n constructor(store: AbstractStore, spec: TreeTableSpec) {\n const indentPx = spec.indentPx ?? DEFAULT_INDENT_PX;\n const adjustedSpec = TreeTable.markTreeColumnUnhideable(spec);\n\n // Body factory is a closure capturing `spec` from the constructor\n // parameter, so super() can construct the TreeBody before any\n // TreeTable instance field has been initialised. A subclass-\n // overridable `createBody` method would hit the class-field\n // super-trap here (the field carrying the spec would still be\n // `undefined` when the override ran).\n super(store, adjustedSpec, (s) => new TreeBody(s, {\n idField: spec.idField,\n parentField: spec.parentField,\n treeColumn: spec.treeColumn,\n indentPx,\n }));\n\n this._treeSpec = spec;\n this._treeBody = this.getBody() as TreeBody;\n\n this.getAria().setRole(\"treegrid\");\n\n // Inject the reparent callbacks so `TreeBody` can install\n // per-row drag sources / drop targets once each pool row is\n // bound. The validator path lets the per-row drop targets\n // surface the cycle / no-op rejection through the green/red\n // feedback tint without committing the move.\n this._treeBody.setReparentHandlers(\n (dragged, newParent) => this.canReparent(dragged, newParent),\n (dragged, newParent) => this.reparentRow(dragged, newParent),\n );\n }\n\n /**\n * Returns a clone of `spec` with `unhideable: true` set on the column\n * matching `spec.treeColumn`. Synthesises a new `ColumnConfig` entry\n * if the tree column is not listed in `spec.columns`, so that\n * `appendUnlisted: false` cannot drop the tree column.\n *\n * @param spec - The consumer-supplied tree-table spec.\n *\n * @returns A new spec object whose `columns` entry for the tree\n * column carries `unhideable: true`.\n */\n private static markTreeColumnUnhideable(spec: TreeTableSpec): TreeTableSpec {\n const existing = spec.columns.find(c => c.field === spec.treeColumn);\n const updated = existing\n ? spec.columns.map(c => c.field === spec.treeColumn ? { ...c, unhideable: true } : c)\n : [...spec.columns, { field: spec.treeColumn, unhideable: true }];\n\n return { ...spec, columns: updated };\n }\n\n /**\n * Returns the tree-aware body that owns the depth-flattened\n * record list and expand/collapse state. Narrower return type than\n * {@link Table.getBody}.\n *\n * @returns The {@link TreeBody}.\n */\n getBody(): TreeBody {\n return super.getBody() as TreeBody;\n }\n\n /**\n * Returns the tree-table spec passed at construction.\n *\n * @returns The {@link TreeTableSpec} used to configure this tree.\n */\n getTreeSpec(): TreeTableSpec {\n return this._treeSpec;\n }\n\n /**\n * Expands or collapses the given record. No-op for leaves and for\n * records not present in the current store view.\n *\n * @param record - The record to expand or collapse.\n * @param expanded - `true` to expand, `false` to collapse.\n *\n * @returns This table, for method chaining.\n */\n setExpanded(record: ModelRecord, expanded: boolean): this {\n this._treeBody.setExpanded(record, expanded);\n\n return this;\n }\n\n /**\n * Returns whether the given record is currently expanded.\n *\n * @param record - The record to query.\n *\n * @returns `true` when the record is a branch and currently\n * expanded, `false` otherwise.\n */\n isExpanded(record: ModelRecord): boolean {\n return this._treeBody.isExpanded(record);\n }\n\n /**\n * Expands every record at depth `<= depth`. Roots are at depth 0.\n *\n * @param depth - Maximum depth to expand.\n *\n * @returns This table, for method chaining.\n */\n expandToDepth(depth: number): this {\n this._treeBody.expandToDepth(depth);\n\n return this;\n }\n\n /**\n * Collapses every record.\n *\n * @returns This table, for method chaining.\n */\n collapseAll(): this {\n this._treeBody.collapseAll();\n\n return this;\n }\n\n /**\n * Expands every record that has children.\n *\n * @returns This table, for method chaining.\n */\n expandAll(): this {\n this._treeBody.expandAll();\n\n return this;\n }\n\n /**\n * Adds a new record, optionally setting its parent-id field so the\n * new record slots under the given parent.\n *\n * @param defaults - Optional initial field values for the new record.\n * @param parent - Optional. When supplied, the new record's\n * `spec.parentField` value is set to `parent.get(spec.idField)`\n * before insertion.\n *\n * @returns The newly created {@link ModelRecord}.\n */\n addRow(defaults: Record<string, any> = {}, parent?: ModelRecord): ModelRecord {\n const payload = parent\n ? { ...defaults, [this._treeSpec.parentField]: parent.get(this._treeSpec.idField) }\n : defaults;\n\n return super.addRow(payload);\n }\n\n /**\n * Resolves a record by its `idField` value. Delegates to\n * [`TreeBody.getRecordById`](/api/component/table/classes/TreeBody#getrecordbyid).\n *\n * @param id - The id-field value to look up.\n *\n * @returns The matching record, or `undefined` when the id is not\n * present in the current store view.\n */\n getRecordById(id: any): ModelRecord | undefined {\n return this._treeBody.getRecordById(id);\n }\n\n /**\n * Returns whether the given record currently has at least one\n * child — i.e. behaves as a directory in the rendered tree.\n *\n * @param record - The record to test.\n *\n * @returns `true` when the record has children; `false` for leaves.\n */\n isDirectoryRecord(record: ModelRecord): boolean {\n return this._treeBody.isDirectoryRecord(record);\n }\n\n /**\n * Reparents the given record under `newParent` (or root when\n * `newParent` is `null`). Rejects cycles and no-ops, fires a\n * `rowreparent` event on success.\n *\n * @param record - The record to move.\n * @param newParent - The new parent record, or `null` for root.\n *\n * @returns `true` when the reparent succeeded; `false` when it was\n * rejected (cycle, no-op, or unknown record).\n */\n reparentRow(record: ModelRecord, newParent: ModelRecord | null): boolean {\n if (!this.canReparent(record, newParent)) {\n return false;\n }\n\n const parentField = this._treeSpec.parentField;\n const idField = this._treeSpec.idField;\n\n const oldParentId = record.get(parentField);\n const oldParent = oldParentId != null ? this._treeBody.getRecordById(oldParentId) ?? null : null;\n\n record.set(parentField, newParent !== null ? newParent.get(idField) : null);\n this.getStore().notifyRecordChanged(record);\n\n const detail: RowReparentDetail = {\n record,\n newParent,\n oldParent,\n };\n\n Event.fireEvent(this, \"rowreparent\", { detail });\n\n return true;\n }\n\n /**\n * Returns whether `record` may legally be reparented under\n * `newParent`. Used both by {@link reparentRow} for the commit-\n * time guard and by `TreeBody`'s per-row drop targets for the\n * visual feedback tint.\n *\n * @param record - The candidate record.\n * @param newParent - The candidate new parent, or `null` for root.\n *\n * @returns `true` when the move is a real change and would not\n * create a cycle; `false` otherwise.\n */\n private canReparent(record: ModelRecord, newParent: ModelRecord | null): boolean {\n if (!this._treeBody.getRecordById(record.get(this._treeSpec.idField))) {\n return false;\n }\n\n // No-op: dropping a record back into the same parent (or onto\n // itself) is rejected so the visual feedback tint reads \"no\n // valid move\" rather than flashing green and silently doing\n // nothing.\n const currentParentId = record.get(this._treeSpec.parentField);\n const newParentId = newParent !== null ? newParent.get(this._treeSpec.idField) : null;\n\n if (currentParentId === newParentId) {\n return false;\n }\n\n if (newParent !== null && this._treeBody.isAncestorOf(record, newParent)) {\n return false;\n }\n\n return true;\n }\n\n /**\n * Registers a listener fired after every successful\n * {@link reparentRow} call. Use to react to drag-and-drop row\n * moves originating from\n * [`DragManager`](/api/overlay/variables/DragManager).\n *\n * @param listener - Receives the {@link RowReparentDetail} payload.\n *\n * @returns This table, for method chaining.\n */\n addRowReparentListener(listener: (detail: RowReparentDetail) => void): this {\n if (this._rowReparentWrappers.has(listener)) {\n return this;\n }\n\n const wrapper = (e: CustomEvent<RowReparentDetail>): void => listener(e.detail);\n\n this._rowReparentWrappers.set(listener, wrapper);\n Event.addListener(this, \"rowreparent\", wrapper);\n\n return this;\n }\n\n /**\n * Removes a previously registered `rowreparent` listener.\n *\n * @param listener - The exact callback reference passed to\n * {@link addRowReparentListener}.\n *\n * @returns This table, for method chaining.\n */\n removeRowReparentListener(listener: (detail: RowReparentDetail) => void): this {\n const wrapper = this._rowReparentWrappers.get(listener);\n\n if (!wrapper) {\n return this;\n }\n\n Event.removeListener(this, \"rowreparent\", wrapper);\n this._rowReparentWrappers.delete(listener);\n\n return this;\n }\n}\n\nconst TreeTableCallable = callable(TreeTable);\ntype TreeTableCallable = TreeTable;\nexport {\n TreeTable as _TreeTable,\n TreeTableCallable as TreeTable\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { AbstractStore } from \"~/data/AbstractStore.js\";\nimport { Border } from \"~/layout/Border.js\";\nimport { HBox } from \"~/layout/HBox.js\";\nimport { Container } from \"~/core/Container.js\";\nimport { Placement } from \"~/primitive/Placement.js\";\nimport { Button } from \"~/component/button/Button.js\";\nimport { PaginationBar } from \"~/component/display/PaginationBar.js\";\nimport { ProgressSpinner } from \"~/component/display/ProgressSpinner.js\";\nimport { ModelRecord } from \"~/data/ModelRecord.js\";\nimport { TreeTable } from \"~/component/table/TreeTable.js\";\nimport { TreeTableSpec } from \"~/component/table/TreeTableSpec.js\";\nimport { ExportOptions } from \"~/component/table/TableExporter.js\";\nimport { Tooltip } from \"~/overlay/Tooltip.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { Glyph } from \"~/component/display/Glyph.js\";\nimport { plus } from \"~/glyphs/solid/plus.js\";\nimport { minus } from \"~/glyphs/solid/minus.js\";\nimport { arrows_rotate } from \"~/glyphs/solid/arrows_rotate.js\";\nimport { ban } from \"~/glyphs/solid/ban.js\";\n\nGlyph.register(plus, minus, arrows_rotate, ban);\n\n/**\n * A composite panel that combines a {@link TreeTable} with an\n * add/remove/sync toolbar — the tree counterpart to {@link TablePanel}.\n *\n * The toolbar is docked to the north region; the tree table fills the\n * center region. The \"add\" button adds a row under the currently-\n * selected directory — or under a selected leaf's parent, or at root\n * when nothing is selected. Use {@link TreeTable.addRow} on the inner\n * table directly to override the selection-aware target.\n *\n * @category Components\n */\nclass TreeTablePanel extends Container {\n\n private _treeTable: TreeTable;\n private _toolbar: Component;\n private _syncBtn: Button;\n private _rejectBtn: Button;\n private _spinner: ProgressSpinner | null = null;\n private _paginationBar: PaginationBar | undefined = undefined;\n\n /**\n * Constructs a TreeTablePanel bound to the given store, configured\n * by the tree spec.\n *\n * @param store - The data store backing the tree.\n * @param spec - Tree presentation + hierarchy configuration.\n */\n constructor(store: AbstractStore, spec: TreeTableSpec) {\n super();\n\n this.setLayoutManager(new Border());\n\n this._toolbar = new Component();\n this._toolbar.setLayoutManager(new HBox());\n\n const addBtn = new Button({ glyph: \"plus\" });\n addBtn.setPreferredSize({ width: 28, height: 28 });\n addBtn.on(\"action\", () => { this.addRowUnderSelection(); });\n Tooltip.attach(addBtn, \"Add row\");\n this._toolbar.addComponent(addBtn);\n\n const removeBtn = new Button({ glyph: \"minus\" });\n removeBtn.setPreferredSize({ width: 28, height: 28 });\n removeBtn.on(\"action\", () => { this._treeTable.removeSelectedRow(); });\n Tooltip.attach(removeBtn, \"Remove selected row\");\n this._toolbar.addComponent(removeBtn);\n\n this._syncBtn = new Button({ glyph: \"arrows-rotate\" });\n this._syncBtn.setPreferredSize({ width: 28, height: 28 });\n this._syncBtn.on(\"action\", () => { void this._treeTable.sync(); });\n Tooltip.attach(this._syncBtn, \"Sync pending changes\");\n this._toolbar.addComponent(this._syncBtn);\n\n this._rejectBtn = new Button({ glyph: \"ban\" });\n this._rejectBtn.setPreferredSize({ width: 28, height: 28 });\n this._rejectBtn.on(\"action\", () => this._treeTable.reject());\n Tooltip.attach(this._rejectBtn, \"Reject pending changes\");\n this._toolbar.addComponent(this._rejectBtn);\n\n this._treeTable = new TreeTable(store, spec);\n\n super.addComponent(this._toolbar, { placement: Placement.NORTH });\n super.addComponent(this._treeTable, { placement: Placement.CENTER });\n\n store.on('loadingchange', (payload: { loading: boolean }) => {\n if (!this._spinner) {\n this._spinner = new ProgressSpinner(24);\n }\n\n if (payload.loading) {\n this._spinner.showOverlay(this._treeTable);\n } else {\n this._spinner.hideOverlay();\n }\n });\n\n const refreshSyncButtons = (): void => this.refreshSyncButtons();\n store.on('add', refreshSyncButtons);\n store.on('remove', refreshSyncButtons);\n store.on('datachange', refreshSyncButtons);\n store.on('sync', refreshSyncButtons);\n store.on('load', refreshSyncButtons);\n\n this.refreshSyncButtons();\n }\n\n /**\n * Adds a new record under the user's current selection. When the\n * selection is a directory, the new record slots underneath it;\n * when the selection is a leaf, the record slots under the leaf's\n * parent (matching VS Code / Finder \"new sibling\" behaviour). With\n * no selection, the record lands at root — the documented fallback\n * that mirrors the no-state add of {@link TreeTable.addRow}.\n *\n * @returns The newly created record.\n */\n private addRowUnderSelection(): ModelRecord {\n const selected = this._treeTable.getSelectedRecord();\n\n if (selected === null) {\n return this._treeTable.addRow();\n }\n\n if (this._treeTable.isDirectoryRecord(selected)) {\n return this._treeTable.addRow({}, selected);\n }\n\n const parentId = selected.get(this._treeTable.getTreeSpec().parentField);\n const parent = parentId != null ? this._treeTable.getRecordById(parentId) : undefined;\n\n return this._treeTable.addRow({}, parent);\n }\n\n /**\n * Updates the Sync and Reject button-enabled state to reflect whether the\n * store has any unsynced changes. Both buttons disable when there is\n * nothing to sync or reject.\n */\n private refreshSyncButtons(): void {\n const hasChanges = this._treeTable.getStore().hasPendingChanges();\n\n this._syncBtn.setEnabled(hasChanges);\n this._rejectBtn.setEnabled(hasChanges);\n }\n\n /**\n * Returns the TreeTable component managed by this panel.\n *\n * @returns The managed {@link TreeTable}.\n */\n getTreeTable(): TreeTable {\n return this._treeTable;\n }\n\n /**\n * Returns the toolbar component containing the add/remove/sync buttons.\n *\n * @returns The toolbar {@link Component}.\n */\n getToolbar(): Component {\n return this._toolbar;\n }\n\n /**\n * Enables or disables the \"Export as CSV\" / \"Export as JSON\" entries in\n * the underlying tree table's column context menu.\n *\n * @param enabled - When true the export items are appended to the menu.\n */\n setExportMenuEnabled(enabled: boolean): this {\n this._treeTable.setExportMenuEnabled(enabled);\n\n return this;\n }\n\n /**\n * Triggers a CSV download of the current store view.\n *\n * @param options - Optional export options (e.g. include hidden columns, custom filename).\n */\n exportCSV(options?: ExportOptions): void {\n this._treeTable.exportCSV(options);\n }\n\n /**\n * Triggers a JSON download of the current store view.\n *\n * @param options - Optional export options (e.g. include hidden columns, custom filename).\n */\n exportJSON(options?: ExportOptions): void {\n this._treeTable.exportJSON(options);\n }\n\n /**\n * Docks a {@link PaginationBar} to the south region of the panel.\n *\n * @param bar - The pagination bar to attach. Replaces any previously attached bar.\n */\n setPaginationBar(bar: PaginationBar): this {\n if (this._paginationBar) {\n this.removeComponent(this._paginationBar);\n this._paginationBar.dispose();\n }\n\n this._paginationBar = bar;\n super.addComponent(bar, { placement: Placement.SOUTH });\n\n return this;\n }\n\n /**\n * Returns the currently attached pagination bar, or undefined if none is set.\n *\n * @returns The pagination bar previously installed via {@link setPaginationBar}.\n */\n getPaginationBar(): PaginationBar | undefined {\n return this._paginationBar;\n }\n}\n\nconst TreeTablePanelCallable = callable(TreeTablePanel);\ntype TreeTablePanelCallable = TreeTablePanel;\nexport {\n TreeTablePanel as _TreeTablePanel,\n TreeTablePanelCallable as TreeTablePanel\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { CellRenderer } from \"~/component/table/cell/renderer/CellRenderer.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { Link } from \"~/component/input/Link.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction options for {@link LinkCellRenderer}.\n *\n * @category Components\n */\nexport interface LinkCellRendererOptions {\n /**\n * CSS colour for the link text. Defaults to the theme-tinted colour of\n * [`Link`](/api/component/input/classes/Link) itself, so leaving this unset\n * keeps every link cell in step with every standalone link.\n */\n color ?: string;\n}\n\n/**\n * A display-only cell renderer that presents its string value as a link.\n * Built for the\n * [`ColumnConfig.renderer`](/api/component/table/interfaces/ColumnConfig#renderer)\n * seam so a column can present a clickable value without a custom renderer\n * subclass.\n *\n * Composes a [`Link`](/api/component/input/classes/Link) in its presentational\n * (`interactive: false`) mode, so a link cell looks exactly like a standalone\n * link but claims no `role`, takes no tab focus, and does no keyboard\n * handling — all of which belong to the enclosing {@link Table}.\n *\n * The renderer is purely presentational: it does not handle the click itself.\n * Pair the column with the {@link Table} `\"cellclick\"` event and act on the\n * matching `field` to open / navigate. Because a custom-rendered cell carries no\n * editor, the cell never enters edit mode, so the click is always a plain click.\n *\n * @example\n * ```typescript\n * new Table(store, {\n * columns: [{ field: 'refTable', renderer: () => new LinkCellRenderer() }],\n * });\n * table.on('cellclick', e => {\n * if (e.field === 'refTable') open(String(e.value));\n * });\n * ```\n *\n * @category Components\n */\nclass LinkCellRenderer extends CellRenderer<String | null> {\n\n private readonly _text: Text;\n private _value: String | null = null;\n\n constructor(options?: LinkCellRendererOptions) {\n super();\n\n this._text = new Link(\"\", {\n // The Table owns keyboard nav (RovingTabIndex) and routes clicks\n // through its own \"cellclick\", so the link must neither take tab\n // focus nor wire its own keyboard handling.\n interactive: false,\n // An unset `color` is gated out by applyOptions, so it falls\n // through to Link's own class default rather than being pinned here.\n foregroundColor: options?.color,\n });\n // Renderer Texts opt out of auto-measure — the host cell force-sizes them.\n this._text.setAutoMeasure(false);\n this.addComponent(this._text);\n }\n\n /**\n * Returns the underlying {@link Text} used to display the link label.\n *\n * @returns The link's {@link Text} component.\n */\n getText(): Text {\n return this._text;\n }\n\n /**\n * Returns the cached value, or `null` when the cell is empty.\n *\n * @returns The displayed value, or `null`.\n */\n getValue(): String | null {\n return this._value;\n }\n\n /**\n * Caches the value and renders it as link text. `null` and `undefined`\n * both normalise to `null` and render as the empty string.\n *\n * @param value - The value to display, or `null`/`undefined` to clear.\n */\n setValue(value: String | null): void {\n this._value = value ?? null;\n this._text.setText(this._value === null ? \"\" : String(this._value));\n }\n}\n\nconst LinkCellRendererCallable = callable(LinkCellRenderer);\ntype LinkCellRendererCallable = LinkCellRenderer;\nexport {\n LinkCellRenderer as _LinkCellRenderer,\n LinkCellRendererCallable as LinkCellRenderer\n};\n"],"mappings":"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"}